Update observability docs and task utilities

- Add Observability.md documentation
- Standardize task logging with correlation_id support
- Add log_sanitizer utility for PII masking
- Update Tasks.md tracking
- Update geo_cache tasks and other task modules with correlation_id

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
2026-05-03 11:52:09 +02:00
parent 7b93499551
commit 0133489920
17 changed files with 582 additions and 124 deletions

View File

@@ -9,10 +9,16 @@ be updated at runtime through the blocklist router.
The scheduler job ID is ``"blocklist_import"`` — using a stable id means
re-registering the job (e.g. after a schedule update) safely replaces the
existing entry without creating duplicates.
Correlation IDs are propagated through the task using
:mod:`app.utils.correlation` so that task logs can be correlated with the
triggering request (for manually triggered imports) or assigned a unique
ID per run (for scheduled imports).
"""
from __future__ import annotations
import uuid
from typing import TYPE_CHECKING, Any
import structlog
@@ -20,6 +26,7 @@ import structlog
from app.services import ban_service, blocklist_service
from app.tasks.db import task_db
from app.tasks.timeout_utils import run_with_timeout
from app.utils.correlation import get_correlation_id, reset_correlation_id, set_correlation_id
from app.utils.runtime_state import get_effective_settings
if TYPE_CHECKING:
@@ -37,18 +44,36 @@ JOB_ID: str = "blocklist_import"
TASK_TIMEOUT_SECONDS: int = 300
async def _run_import_with_resources(settings: Settings, http_session: ClientSession) -> None:
async def _run_import_with_resources(
settings: Settings,
http_session: ClientSession,
correlation_id: str | None = None,
) -> None:
"""APScheduler callback that imports all enabled blocklist sources.
Args:
settings: The resolved application settings used for database access.
http_session: The shared aiohttp session used for blocklist downloads.
correlation_id: Optional correlation ID from the triggering request.
If ``None``, a new UUID4 is generated to identify this run.
"""
if correlation_id is None:
correlation_id = str(uuid.uuid4())
token = set_correlation_id(correlation_id)
try:
await _do_import_with_settings(settings, http_session)
finally:
reset_correlation_id(token)
async def _do_import_with_settings(settings: Settings, http_session: ClientSession) -> None:
"""Inner import logic that runs with correlation context set."""
async def _do_import() -> None:
socket_path: str = settings.fail2ban_socket
log.info("blocklist_import_starting")
log.info("blocklist_import_starting", correlation_id=get_correlation_id())
try:
async with task_db(settings) as db:
result = await blocklist_service.import_all(
@@ -59,12 +84,13 @@ async def _run_import_with_resources(settings: Settings, http_session: ClientSes
)
log.info(
"blocklist_import_finished",
correlation_id=get_correlation_id(),
total_imported=result.total_imported,
total_skipped=result.total_skipped,
errors=result.errors_count,
)
except Exception:
log.exception("blocklist_import_unexpected_error")
log.exception("blocklist_import_unexpected_error", correlation_id=get_correlation_id())
await run_with_timeout("blocklist_import", _do_import(), TASK_TIMEOUT_SECONDS)