mirror of
https://github.com/Alishahryar1/free-claude-code.git
synced 2026-06-02 06:13:46 +02:00
Use canonical FCC server log path
This commit is contained in:
@@ -413,14 +413,6 @@ FIELDS: tuple[ConfigFieldSpec, ...] = (
|
||||
default="8082",
|
||||
restart_required=True,
|
||||
),
|
||||
ConfigFieldSpec(
|
||||
"LOG_FILE",
|
||||
"Log File",
|
||||
"runtime",
|
||||
settings_attr="log_file",
|
||||
default="logs/server.log",
|
||||
restart_required=True,
|
||||
),
|
||||
ConfigFieldSpec(
|
||||
"MESSAGING_PLATFORM",
|
||||
"Messaging Platform",
|
||||
|
||||
+2
-1
@@ -12,6 +12,7 @@ from loguru import logger
|
||||
from starlette.types import Receive, Scope, Send
|
||||
|
||||
from config.logging_config import configure_logging
|
||||
from config.paths import server_log_path
|
||||
from config.settings import get_settings
|
||||
from core.trace import extract_claude_session_id_from_headers, trace_event
|
||||
from providers.exceptions import ProviderError
|
||||
@@ -85,7 +86,7 @@ def create_app(*, lifespan_enabled: bool = True) -> FastAPI:
|
||||
"""Create and configure the FastAPI application."""
|
||||
settings = get_settings()
|
||||
configure_logging(
|
||||
settings.log_file, verbose_third_party=settings.log_raw_api_payloads
|
||||
server_log_path(), verbose_third_party=settings.log_raw_api_payloads
|
||||
)
|
||||
|
||||
app_kwargs: dict[str, Any] = {
|
||||
|
||||
@@ -105,7 +105,7 @@ class InterceptHandler(logging.Handler):
|
||||
|
||||
|
||||
def configure_logging(
|
||||
log_file: str, *, force: bool = False, verbose_third_party: bool = False
|
||||
log_file: str | Path, *, force: bool = False, verbose_third_party: bool = False
|
||||
) -> None:
|
||||
"""Configure loguru with JSON output to log_file and intercept stdlib logging.
|
||||
|
||||
|
||||
@@ -5,6 +5,8 @@ from pathlib import Path
|
||||
FCC_CONFIG_DIRNAME = ".fcc"
|
||||
FCC_ENV_FILENAME = ".env"
|
||||
CLAUDE_WORKSPACE_DIRNAME = "agent_workspace"
|
||||
FCC_LOGS_DIRNAME = "logs"
|
||||
SERVER_LOG_FILENAME = "server.log"
|
||||
|
||||
|
||||
def config_dir_path() -> Path:
|
||||
@@ -23,3 +25,9 @@ def default_claude_workspace_path() -> Path:
|
||||
"""Return the default Claude workspace path."""
|
||||
|
||||
return config_dir_path() / CLAUDE_WORKSPACE_DIRNAME
|
||||
|
||||
|
||||
def server_log_path() -> Path:
|
||||
"""Return the canonical server log path."""
|
||||
|
||||
return config_dir_path() / FCC_LOGS_DIRNAME / SERVER_LOG_FILENAME
|
||||
|
||||
@@ -310,7 +310,6 @@ class Settings(BaseSettings):
|
||||
# ==================== Server ====================
|
||||
host: str = "0.0.0.0"
|
||||
port: int = 8082
|
||||
log_file: str = "logs/server.log"
|
||||
# Optional server API key to protect endpoints (Anthropic-style)
|
||||
# Set via env `ANTHROPIC_AUTH_TOKEN`. When empty, no auth is required.
|
||||
anthropic_auth_token: str = Field(
|
||||
|
||||
@@ -57,6 +57,7 @@ def test_admin_config_masks_secrets_and_exposes_manifest(monkeypatch, tmp_path):
|
||||
keys = {field["key"] for field in body["fields"]}
|
||||
assert "ANTHROPIC_AUTH_TOKEN" in keys
|
||||
assert "OPENROUTER_API_KEY" in keys
|
||||
assert "LOG_FILE" not in keys
|
||||
auth_field = next(
|
||||
field for field in body["fields"] if field["key"] == "ANTHROPIC_AUTH_TOKEN"
|
||||
)
|
||||
|
||||
@@ -85,7 +85,6 @@ def test_create_app_provider_error_handler_returns_anthropic_format():
|
||||
claude_workspace="./agent_workspace",
|
||||
host="127.0.0.1",
|
||||
port=8082,
|
||||
log_file="server.log",
|
||||
)
|
||||
with (
|
||||
patch.object(api_app_mod, "get_settings", return_value=settings),
|
||||
@@ -122,7 +121,6 @@ def test_create_app_provider_error_default_logs_exclude_provider_message():
|
||||
claude_workspace="./agent_workspace",
|
||||
host="127.0.0.1",
|
||||
port=8082,
|
||||
log_file="server.log",
|
||||
log_api_error_tracebacks=False,
|
||||
)
|
||||
with (
|
||||
@@ -160,7 +158,6 @@ def test_create_app_general_exception_handler_returns_500():
|
||||
claude_workspace="./agent_workspace",
|
||||
host="127.0.0.1",
|
||||
port=8082,
|
||||
log_file="server.log",
|
||||
)
|
||||
with (
|
||||
patch.object(api_app_mod, "get_settings", return_value=settings),
|
||||
@@ -197,7 +194,6 @@ def test_create_app_general_exception_default_logs_exclude_exception_message():
|
||||
claude_workspace="./agent_workspace",
|
||||
host="127.0.0.1",
|
||||
port=8082,
|
||||
log_file="server.log",
|
||||
log_api_error_tracebacks=False,
|
||||
)
|
||||
with (
|
||||
@@ -236,7 +232,6 @@ def test_app_lifespan_sets_state_and_cleans_up(tmp_path, messaging_enabled):
|
||||
claude_workspace=str(tmp_path / "data"),
|
||||
host="127.0.0.1",
|
||||
port=8082,
|
||||
log_file=str(tmp_path / "server.log"),
|
||||
)
|
||||
|
||||
fake_platform = MagicMock()
|
||||
@@ -315,7 +310,6 @@ def test_app_lifespan_cleanup_continues_if_platform_stop_raises(tmp_path):
|
||||
claude_workspace=str(tmp_path / "data"),
|
||||
host="127.0.0.1",
|
||||
port=8082,
|
||||
log_file=str(tmp_path / "server.log"),
|
||||
)
|
||||
|
||||
fake_platform = MagicMock()
|
||||
@@ -366,7 +360,6 @@ async def test_runtime_startup_validation_failure_does_not_block_server(tmp_path
|
||||
claude_workspace=str(tmp_path / "data"),
|
||||
host="127.0.0.1",
|
||||
port=8082,
|
||||
log_file=str(tmp_path / "server.log"),
|
||||
)
|
||||
app = FastAPI()
|
||||
runtime = api_runtime_mod.AppRuntime(
|
||||
@@ -414,7 +407,6 @@ async def test_graceful_asgi_lifespan_model_validation_failure_starts(tmp_path):
|
||||
claude_workspace=str(tmp_path / "data"),
|
||||
host="127.0.0.1",
|
||||
port=8082,
|
||||
log_file=str(tmp_path / "server.log"),
|
||||
)
|
||||
app = api_app_mod.GracefulLifespanApp(FastAPI())
|
||||
sent: list[MutableMapping[str, Any]] = []
|
||||
@@ -460,7 +452,6 @@ def test_app_lifespan_messaging_import_error_no_crash(tmp_path, caplog):
|
||||
claude_workspace=str(tmp_path / "data"),
|
||||
host="127.0.0.1",
|
||||
port=8082,
|
||||
log_file=str(tmp_path / "server.log"),
|
||||
)
|
||||
|
||||
api_app_mod = importlib.import_module("api.app")
|
||||
@@ -496,7 +487,6 @@ def test_app_lifespan_platform_start_exception_cleanup_still_runs(tmp_path):
|
||||
claude_workspace=str(tmp_path / "data"),
|
||||
host="127.0.0.1",
|
||||
port=8082,
|
||||
log_file=str(tmp_path / "server.log"),
|
||||
)
|
||||
|
||||
fake_platform = MagicMock()
|
||||
@@ -547,7 +537,6 @@ def test_app_lifespan_flush_pending_save_exception_warning_only(tmp_path):
|
||||
claude_workspace=str(tmp_path / "data"),
|
||||
host="127.0.0.1",
|
||||
port=8082,
|
||||
log_file=str(tmp_path / "server.log"),
|
||||
)
|
||||
|
||||
fake_platform = MagicMock()
|
||||
@@ -582,3 +571,29 @@ def test_app_lifespan_flush_pending_save_exception_warning_only(tmp_path):
|
||||
|
||||
session_store.flush_pending_save.assert_called_once()
|
||||
registry_cleanup.assert_awaited_once()
|
||||
|
||||
|
||||
def test_create_app_writes_server_log_under_fcc_home(monkeypatch, tmp_path):
|
||||
"""App logging uses ~/.fcc/logs/server.log regardless of cwd."""
|
||||
from loguru import logger
|
||||
|
||||
import config.logging_config as logging_config_mod
|
||||
from api.app import create_app
|
||||
from config.paths import server_log_path
|
||||
|
||||
run_dir = tmp_path / "run"
|
||||
run_dir.mkdir()
|
||||
monkeypatch.chdir(run_dir)
|
||||
monkeypatch.setenv("HOME", str(tmp_path))
|
||||
monkeypatch.setenv("USERPROFILE", str(tmp_path))
|
||||
monkeypatch.setattr(logging_config_mod, "_configured", False)
|
||||
|
||||
create_app(lifespan_enabled=False)
|
||||
logger.info("canonical log path test")
|
||||
logger.complete()
|
||||
|
||||
canonical_log = server_log_path()
|
||||
assert canonical_log == tmp_path / ".fcc" / "logs" / "server.log"
|
||||
assert canonical_log.is_file()
|
||||
assert "canonical log path test" in canonical_log.read_text(encoding="utf-8")
|
||||
assert not (run_dir / "logs" / "server.log").exists()
|
||||
|
||||
@@ -22,7 +22,6 @@ async def test_messaging_start_failure_default_logs_exclude_traceback(caplog):
|
||||
claude_workspace="./agent_workspace",
|
||||
host="127.0.0.1",
|
||||
port=8082,
|
||||
log_file="server.log",
|
||||
log_api_error_tracebacks=False,
|
||||
)
|
||||
runtime = api_runtime_mod.AppRuntime(app=MagicMock(), settings=settings)
|
||||
|
||||
@@ -57,6 +57,26 @@ class TestSettings:
|
||||
|
||||
assert settings.claude_workspace == str(tmp_path / ".fcc" / "agent_workspace")
|
||||
|
||||
def test_server_log_path_uses_fcc_home(self, monkeypatch, tmp_path):
|
||||
"""The server log location is fixed under ~/.fcc."""
|
||||
from config.paths import server_log_path
|
||||
|
||||
monkeypatch.setenv("HOME", str(tmp_path))
|
||||
monkeypatch.setenv("USERPROFILE", str(tmp_path))
|
||||
|
||||
assert server_log_path() == tmp_path / ".fcc" / "logs" / "server.log"
|
||||
|
||||
def test_removed_log_file_env_is_ignored(self, monkeypatch):
|
||||
"""Legacy LOG_FILE values do not affect Settings or block startup."""
|
||||
from config.settings import Settings
|
||||
|
||||
monkeypatch.setenv("LOG_FILE", "custom/server.log")
|
||||
monkeypatch.setitem(Settings.model_config, "env_file", ())
|
||||
|
||||
settings = Settings()
|
||||
|
||||
assert not hasattr(settings, "log_file")
|
||||
|
||||
def test_blank_claude_workspace_uses_fcc_home(self, monkeypatch, tmp_path):
|
||||
"""An explicit blank env value keeps the default workspace path."""
|
||||
from config.settings import Settings
|
||||
|
||||
Reference in New Issue
Block a user