fix: scope log filenames by service under root logs dir
This commit is contained in:
@@ -4,7 +4,7 @@ from pathlib import Path
|
|||||||
from typing import ClassVar, Literal
|
from typing import ClassVar, Literal
|
||||||
from urllib.parse import quote
|
from urllib.parse import quote
|
||||||
|
|
||||||
from pydantic import BaseModel, Field, computed_field, field_validator
|
from pydantic import BaseModel, Field, computed_field, field_validator, model_validator
|
||||||
from pydantic_settings import BaseSettings, SettingsConfigDict
|
from pydantic_settings import BaseSettings, SettingsConfigDict
|
||||||
|
|
||||||
|
|
||||||
@@ -21,8 +21,8 @@ class RuntimeSettings(BaseModel):
|
|||||||
log_rotation_max_bytes: int = 10_000_000
|
log_rotation_max_bytes: int = 10_000_000
|
||||||
log_dir: str = "logs"
|
log_dir: str = "logs"
|
||||||
log_error_dir: str = "logs/errors"
|
log_error_dir: str = "logs/errors"
|
||||||
log_file_name: str = "app.log"
|
log_file_name: str = ""
|
||||||
log_error_file_name: str = "error.log"
|
log_error_file_name: str = ""
|
||||||
log_sensitive_fields: list[str] = Field(
|
log_sensitive_fields: list[str] = Field(
|
||||||
default_factory=lambda: [
|
default_factory=lambda: [
|
||||||
"password",
|
"password",
|
||||||
@@ -47,15 +47,20 @@ class RuntimeSettings(BaseModel):
|
|||||||
def lock_log_error_dir(cls, _: object) -> str:
|
def lock_log_error_dir(cls, _: object) -> str:
|
||||||
return "logs/errors"
|
return "logs/errors"
|
||||||
|
|
||||||
@field_validator("log_file_name", mode="before")
|
@model_validator(mode="after")
|
||||||
@classmethod
|
def ensure_service_scoped_log_file_names(self) -> "RuntimeSettings":
|
||||||
def lock_log_file_name(cls, _: object) -> str:
|
service = "".join(
|
||||||
return "app.log"
|
char if char.isalnum() or char in {"-", "_"} else "-"
|
||||||
|
for char in self.service_name
|
||||||
|
).strip("-_")
|
||||||
|
service_name = service or "app"
|
||||||
|
|
||||||
@field_validator("log_error_file_name", mode="before")
|
if not self.log_file_name.strip():
|
||||||
@classmethod
|
self.log_file_name = f"{service_name}.log"
|
||||||
def lock_log_error_file_name(cls, _: object) -> str:
|
if not self.log_error_file_name.strip():
|
||||||
return "error.log"
|
self.log_error_file_name = f"{service_name}.error.log"
|
||||||
|
|
||||||
|
return self
|
||||||
|
|
||||||
|
|
||||||
class CelerySettings(BaseModel):
|
class CelerySettings(BaseModel):
|
||||||
|
|||||||
@@ -86,7 +86,7 @@ def test_e2e_error_logging(tmp_path: Path) -> None:
|
|||||||
server.should_exit = True
|
server.should_exit = True
|
||||||
thread.join(timeout=5)
|
thread.join(timeout=5)
|
||||||
|
|
||||||
error_entries = _read_json_lines(Path(tmp_path) / "errors" / "error.log")
|
error_entries = _read_json_lines(Path(tmp_path) / "errors" / "app.error.log")
|
||||||
entry = next(
|
entry = next(
|
||||||
item for item in error_entries if item.get("message") == "Unhandled exception"
|
item for item in error_entries if item.get("message") == "Unhandled exception"
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -83,7 +83,7 @@ def test_exception_handler_logs_stack_and_sends_500(tmp_path: Path) -> None:
|
|||||||
assert response.status_code == 500
|
assert response.status_code == 500
|
||||||
assert response.json()["detail"] == "Internal Server Error"
|
assert response.json()["detail"] == "Internal Server Error"
|
||||||
|
|
||||||
error_entries = _read_json_lines(Path(tmp_path) / "errors" / "error.log")
|
error_entries = _read_json_lines(Path(tmp_path) / "errors" / "app.error.log")
|
||||||
assert error_entries
|
assert error_entries
|
||||||
entry = error_entries[-1]
|
entry = error_entries[-1]
|
||||||
assert entry["level"] == "error"
|
assert entry["level"] == "error"
|
||||||
@@ -116,7 +116,7 @@ def test_invalid_request_id_is_replaced_and_used_in_error_context(
|
|||||||
response_request_id = response.headers["X-Request-ID"]
|
response_request_id = response.headers["X-Request-ID"]
|
||||||
assert response_request_id != "bad"
|
assert response_request_id != "bad"
|
||||||
|
|
||||||
error_entries = _read_json_lines(Path(tmp_path) / "errors" / "error.log")
|
error_entries = _read_json_lines(Path(tmp_path) / "errors" / "app.error.log")
|
||||||
assert error_entries
|
assert error_entries
|
||||||
entry = error_entries[-1]
|
entry = error_entries[-1]
|
||||||
assert entry["request_id"] == response_request_id
|
assert entry["request_id"] == response_request_id
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ def test_runtime_settings_defaults() -> None:
|
|||||||
assert settings.runtime.log_dir == "logs"
|
assert settings.runtime.log_dir == "logs"
|
||||||
assert settings.runtime.log_error_dir == "logs/errors"
|
assert settings.runtime.log_error_dir == "logs/errors"
|
||||||
assert settings.runtime.log_file_name == "app.log"
|
assert settings.runtime.log_file_name == "app.log"
|
||||||
assert settings.runtime.log_error_file_name == "error.log"
|
assert settings.runtime.log_error_file_name == "app.error.log"
|
||||||
assert "password" in settings.runtime.log_sensitive_fields
|
assert "password" in settings.runtime.log_sensitive_fields
|
||||||
|
|
||||||
|
|
||||||
@@ -33,7 +33,22 @@ def test_runtime_settings_env_override(monkeypatch: MonkeyPatch) -> None:
|
|||||||
|
|
||||||
assert settings.runtime.log_dir == "logs"
|
assert settings.runtime.log_dir == "logs"
|
||||||
assert settings.runtime.log_error_dir == "logs/errors"
|
assert settings.runtime.log_error_dir == "logs/errors"
|
||||||
assert settings.runtime.log_file_name == "app.log"
|
assert settings.runtime.log_file_name == "custom.log"
|
||||||
assert settings.runtime.log_error_file_name == "error.log"
|
assert settings.runtime.log_error_file_name == "custom-error.log"
|
||||||
assert settings.runtime.log_rotation == "size"
|
assert settings.runtime.log_rotation == "size"
|
||||||
assert settings.runtime.log_rotation_max_bytes == 2048
|
assert settings.runtime.log_rotation_max_bytes == 2048
|
||||||
|
|
||||||
|
|
||||||
|
def test_runtime_settings_default_file_names_follow_service_name(
|
||||||
|
monkeypatch: MonkeyPatch,
|
||||||
|
) -> None:
|
||||||
|
monkeypatch.delenv("SOCIAL_RUNTIME__LOG_FILE_NAME", raising=False)
|
||||||
|
monkeypatch.delenv("SOCIAL_RUNTIME__LOG_ERROR_FILE_NAME", raising=False)
|
||||||
|
monkeypatch.setenv("SOCIAL_RUNTIME__SERVICE_NAME", "worker-default")
|
||||||
|
|
||||||
|
settings = Settings()
|
||||||
|
|
||||||
|
assert settings.runtime.log_dir == "logs"
|
||||||
|
assert settings.runtime.log_error_dir == "logs/errors"
|
||||||
|
assert settings.runtime.log_file_name == "worker-default.log"
|
||||||
|
assert settings.runtime.log_error_file_name == "worker-default.error.log"
|
||||||
|
|||||||
Reference in New Issue
Block a user