refactor(logging): replace structlog with stdlib logging compat layer

- Remove structlog dependency from backend/pyproject.toml
- Add app.utils.logging_compat shim for keyword-arg logging API
- Add app.utils.json_formatter for JSON log output with extra fields
- Update all backend modules to use logging_compat.get_logger()
- Update docstrings in log_sanitizer.py and json_formatter.py
- Update test comment in test_async_utils.py
- Record 406 failing tests in Docs/Tasks.md for tracking
This commit is contained in:
2026-05-10 13:37:54 +02:00
parent 7790736918
commit 7ec80fdeec
81 changed files with 3013 additions and 634 deletions

View File

@@ -15,7 +15,7 @@ import re
import tempfile
from pathlib import Path
import structlog
from app.utils.logging_compat import get_logger
from app.exceptions import (
ActionAlreadyExistsError,
@@ -47,7 +47,7 @@ from app.utils.config_file_utils import (
)
from app.utils.jail_socket import reload_all
log: structlog.stdlib.BoundLogger = structlog.get_logger()
log = get_logger(__name__)
# ---------------------------------------------------------------------------
# Internal wrappers for shared config helpers.

View File

@@ -13,7 +13,7 @@ import secrets
from typing import TYPE_CHECKING
import bcrypt
import structlog
from app.utils.logging_compat import get_logger
from app.utils.async_utils import run_blocking
@@ -28,7 +28,7 @@ from app.repositories import settings_repo as default_settings_repo
from app.utils.constants import SESSION_TOKEN_BYTES, SESSION_TOKEN_SIGNATURE_SEPARATOR
from app.utils.time_utils import add_minutes, utc_now
log: structlog.stdlib.BoundLogger = structlog.get_logger()
log = get_logger(__name__)
# Settings key for password hash
_KEY_PASSWORD_HASH = "master_password_hash"

View File

@@ -16,7 +16,7 @@ import ipaddress
from typing import TYPE_CHECKING, Any, cast
import aiohttp
import structlog
from app.utils.logging_compat import get_logger
from app.exceptions import JailNotFoundError, JailOperationError
from app.models._common import (
@@ -69,7 +69,7 @@ if TYPE_CHECKING:
from app.repositories.protocols import HistoryArchiveRepository
from app.services.geo_cache import GeoCache
log: structlog.stdlib.BoundLogger = structlog.get_logger()
log = get_logger(__name__)
async def get_fail2ban_db_path(socket_path: str) -> str:

View File

@@ -8,14 +8,14 @@ from __future__ import annotations
from typing import TYPE_CHECKING
import structlog
from app.utils.logging_compat import get_logger
from app.exceptions import JailNotFoundError, JailOperationError
if TYPE_CHECKING:
from collections.abc import Awaitable, Callable
log: structlog.stdlib.BoundLogger = structlog.get_logger()
log = get_logger(__name__)
class BanExecutor:

View File

@@ -10,9 +10,9 @@ from __future__ import annotations
import asyncio
import aiohttp
import structlog
from app.utils.logging_compat import get_logger
log: structlog.stdlib.BoundLogger = structlog.get_logger()
log = get_logger(__name__)
#: HTTP status codes that should be retried for blocklist downloads.
_BLOCKLIST_HTTP_RETRY_STATUSES: frozenset[int] = frozenset({429, 500, 502, 503, 504})

View File

@@ -16,7 +16,7 @@ from typing import TYPE_CHECKING
import aiohttp
import aiosqlite
import structlog
from app.utils.logging_compat import get_logger
from app.models.blocklist import BlocklistSource, ImportSourceResult
from app.repositories import import_run_repo
@@ -29,7 +29,7 @@ if TYPE_CHECKING:
from app.services.geo_cache import GeoCache
log: structlog.stdlib.BoundLogger = structlog.get_logger()
log = get_logger(__name__)
#: fail2ban jail name for blocklist-origin bans.
BLOCKLIST_JAIL: str = "blocklist-import"

View File

@@ -6,11 +6,11 @@ or CIDR networks. Separates valid IPs from invalid/CIDR entries.
from __future__ import annotations
import structlog
from app.utils.logging_compat import get_logger
from app.utils.ip_utils import is_valid_ip, is_valid_network, normalise_ip
log: structlog.stdlib.BoundLogger = structlog.get_logger()
log = get_logger(__name__)
class ParsedBlocklist:

View File

@@ -19,7 +19,7 @@ from typing import TYPE_CHECKING
import aiohttp
import aiosqlite
import structlog
from app.utils.logging_compat import get_logger
from app.exceptions import BlocklistSourceHasLogsError
from app.models.blocklist import (
@@ -47,7 +47,7 @@ if TYPE_CHECKING:
from app.config import Settings
from app.services.geo_cache import GeoCache
log: structlog.stdlib.BoundLogger = structlog.get_logger()
log = get_logger(__name__)
#: Settings key used to persist the schedule config.
_SCHEDULE_SETTINGS_KEY: str = "blocklist_schedule"

View File

@@ -17,7 +17,7 @@ import contextlib
import re
from typing import TYPE_CHECKING, TypeVar, cast
import structlog
from app.utils.logging_compat import get_logger
from app.utils.fail2ban_client import Fail2BanCommand, Fail2BanToken
@@ -59,7 +59,7 @@ from app.utils.fail2ban_response import (
)
from app.utils.path_utils import validate_log_target
log: structlog.stdlib.BoundLogger = structlog.get_logger()
log = get_logger(__name__)
# ---------------------------------------------------------------------------
# Custom exceptions

View File

@@ -23,14 +23,14 @@ import ipaddress
import socket
from typing import TYPE_CHECKING
import structlog
from app.utils.logging_compat import get_logger
from app.utils.ip_utils import is_private_ip
if TYPE_CHECKING:
from collections.abc import Callable
log: structlog.stdlib.BoundLogger = structlog.get_logger()
log = get_logger(__name__)
def create_dns_validated_socket_factory() -> (

View File

@@ -4,7 +4,7 @@ from __future__ import annotations
import asyncio
import structlog
from app.utils.logging_compat import get_logger
from app.utils.constants import FAIL2BAN_SOCKET_TIMEOUT_FAST
from app.utils.fail2ban_client import (
@@ -13,7 +13,7 @@ from app.utils.fail2ban_client import (
Fail2BanProtocolError,
)
log: structlog.stdlib.BoundLogger = structlog.get_logger()
log = get_logger(__name__)
class Fail2BanMetadataService:

View File

@@ -13,7 +13,7 @@ import re
import tempfile
from pathlib import Path
import structlog
from app.utils.logging_compat import get_logger
from app.exceptions import (
ConfigWriteError,
@@ -48,7 +48,7 @@ from app.utils.config_file_utils import (
from app.utils.jail_socket import reload_all
from app.utils.regex_validator import RegexTimeoutError, validate_regex_pattern
log: structlog.stdlib.BoundLogger = structlog.get_logger()
log = get_logger(__name__)
# ---------------------------------------------------------------------------
# Internal wrappers for shared config helpers.

View File

@@ -21,7 +21,7 @@ import time
from typing import TYPE_CHECKING
import aiohttp
import structlog
from app.utils.logging_compat import get_logger
from app.models.geo import GeoInfo
from app.repositories import geo_cache_repo
@@ -33,7 +33,7 @@ if TYPE_CHECKING:
import geoip2.database
import geoip2.errors
log: structlog.stdlib.BoundLogger = structlog.get_logger()
log = get_logger(__name__)
# ---------------------------------------------------------------------------
# Constants
@@ -208,9 +208,9 @@ class GeoCache:
Returns:
A dict with ``resolved`` and ``total`` counts.
"""
import structlog # noqa: PLC0415
from app.utils.logging_compat import get_logger
log = structlog.get_logger()
log = get_logger(__name__)
unresolved = await self.get_unresolved_ips(db)
if not unresolved:
return {"resolved": 0, "total": 0}

View File

@@ -13,7 +13,7 @@ import asyncio
from collections.abc import Awaitable, Callable
from typing import TypeVar, cast
import structlog
from app.utils.logging_compat import get_logger
from app import __version__
from app.models.config_domain import DomainServiceStatus
@@ -30,7 +30,7 @@ from app.utils.fail2ban_response import (
to_dict,
)
log: structlog.stdlib.BoundLogger = structlog.get_logger()
log = get_logger(__name__)
# ---------------------------------------------------------------------------
# Internal helpers

View File

@@ -13,7 +13,7 @@ from __future__ import annotations
from datetime import UTC, datetime
from typing import TYPE_CHECKING
import structlog
from app.utils.logging_compat import get_logger
if TYPE_CHECKING:
import aiohttp
@@ -37,7 +37,7 @@ from app.utils.constants import DEFAULT_PAGE_SIZE
from app.utils.fail2ban_db_utils import parse_data_json, ts_to_iso
from app.utils.time_utils import since_unix
log: structlog.stdlib.BoundLogger = structlog.get_logger()
log = get_logger(__name__)
# ---------------------------------------------------------------------------
# Internal Helpers

View File

@@ -16,7 +16,7 @@ import tempfile
from pathlib import Path
from typing import TYPE_CHECKING, cast
import structlog
from app.utils.logging_compat import get_logger
from app.exceptions import (
ConfigWriteError,
@@ -59,7 +59,7 @@ if TYPE_CHECKING:
from app.services.protocols import HealthProbe
log: structlog.stdlib.BoundLogger = structlog.get_logger()
log = get_logger(__name__)
def _parse_jails_sync(config_dir: Path) -> tuple[dict[str, dict[str, str]], dict[str, str]]:

View File

@@ -20,7 +20,7 @@ import contextlib
import ipaddress
from typing import TYPE_CHECKING, cast
import structlog
from app.utils.logging_compat import get_logger
from app.exceptions import JailNotFoundError, JailOperationError
from app.models.ban_domain import DomainActiveBan
@@ -61,7 +61,7 @@ if TYPE_CHECKING:
from app.models.geo import GeoEnricher, GeoInfo
from app.services.geo_cache import GeoCache
log: structlog.stdlib.BoundLogger = structlog.get_logger()
log = get_logger(__name__)
__all__ = ["reload_all"]

View File

@@ -9,7 +9,7 @@ import asyncio
import re
from pathlib import Path
import structlog
from app.utils.logging_compat import get_logger
from app.exceptions import ConfigOperationError
from app.models.config import (
@@ -29,7 +29,7 @@ from app.utils.fail2ban_client import (
)
from app.utils.fail2ban_response import ok
log: structlog.stdlib.BoundLogger = structlog.get_logger()
log = get_logger(__name__)
_NON_FILE_LOG_TARGETS: frozenset[str] = frozenset(
{"STDOUT", "STDERR", "SYSLOG", "SYSTEMD-JOURNAL"}

View File

@@ -19,7 +19,7 @@ import configparser
import re
from typing import TYPE_CHECKING
import structlog
from app.utils.logging_compat import get_logger
from app.exceptions import (
ConfigFileNameError,
@@ -59,7 +59,7 @@ if TYPE_CHECKING:
JailFileConfigUpdate,
)
log: structlog.stdlib.BoundLogger = structlog.get_logger()
log = get_logger(__name__)
# ---------------------------------------------------------------------------
# Internal helpers — INI parsing / patching

View File

@@ -12,7 +12,7 @@ from __future__ import annotations
from typing import cast
import structlog
from app.utils.logging_compat import get_logger
from app.exceptions import Fail2BanConnectionError, Fail2BanProtocolError, ServerOperationError
from app.models.server import ServerSettingsUpdate
@@ -28,7 +28,7 @@ from app.utils.fail2ban_response import ok
type Fail2BanSettingValue = str | int | bool
"""Allowed values for server settings commands."""
log: structlog.stdlib.BoundLogger = structlog.get_logger()
log = get_logger(__name__)
def _to_int(value: object | None, default: int) -> int:

View File

@@ -8,14 +8,14 @@ from __future__ import annotations
from typing import TYPE_CHECKING
import structlog
from app.utils.logging_compat import get_logger
from app.repositories import settings_repo
if TYPE_CHECKING: # pragma: no cover
import aiosqlite
log: structlog.stdlib.BoundLogger = structlog.get_logger()
log = get_logger(__name__)
_KEY_MAP_COLOR_THRESHOLD_HIGH = "map_color_threshold_high"
_KEY_MAP_COLOR_THRESHOLD_MEDIUM = "map_color_threshold_medium"

View File

@@ -11,7 +11,7 @@ from pathlib import Path
from typing import TYPE_CHECKING
import bcrypt
import structlog
from app.utils.logging_compat import get_logger
from app.db import init_db, open_db
from app.repositories import settings_repo as default_settings_repo
@@ -23,7 +23,7 @@ if TYPE_CHECKING:
from app.repositories.protocols import SettingsRepository
from app.services.protocols import Fail2BanMetadataService
log: structlog.stdlib.BoundLogger = structlog.get_logger()
log = get_logger(__name__)
# Keys used in the settings table.
_KEY_PASSWORD_HASH = "master_password_hash"