Merge pull request 'Логгер: вызывать setup_logger при импорте приложения (#18)' (#20) from feat/18-logger-diagnose into main

Reviewed-on: #20
This commit is contained in:
aep 2026-10-08 15:47:49 +03:00
commit 1e7027ba73
2 changed files with 52 additions and 0 deletions

View File

@ -10,6 +10,7 @@
HTTP-сервер жив.
Остановка: отмена подключения, остановка воркера (задания в работе отменяются), закрытие NATS и S3.
- Объекты lifespan доступны в `app.state`: `worker`, `nats`, `storage`, `normalizer`, `health`.
- При импорте модуля — `setup_logger` (без значений локальных переменных в трейсбеках), затем `app = create_app()`.
- `NATS_ENABLED=false` — подключения нет (`nats = disabled`), воркер запросов не получает (dev без NATS).
"""
@ -28,6 +29,7 @@ from src.core.clients import S3Storage
from src.core.config import Settings, get_settings
from src.core.exception_handlers import register_exception_handlers
from src.core.health import HealthProvider
from src.core.logger import setup_logger
from src.core.nats import NatsGateway
from src.core.worker import Worker
from src.gateway import create_hotfolder
@ -116,4 +118,7 @@ def create_app(settings: Settings | None = None) -> FastAPI:
return app
# Свой обработчик loguru вместо стандартного: diagnose=False — значения локальных переменных не попадают в
# трейсбеки (#18)
setup_logger(level="DEBUG" if get_settings().DEBUG else "INFO")
app = create_app()

View File

@ -2,6 +2,8 @@
воркер). NATS и S3 не нужны: в тестах lifespan `NATS_ENABLED=false` либо методы `NatsGateway` подменены."""
import asyncio
import subprocess
import sys
from datetime import datetime
from pathlib import Path
from typing import Any
@ -228,3 +230,48 @@ def test_create_app_fails_on_broken_rules(tmp_path: Path) -> None:
def test_create_app_uses_ocr_normalizer() -> None:
app = create_app(get_settings().model_copy(update={"SCAN_HOTFOLDER_PATH": "."}))
assert isinstance(app.state.normalizer, OcrNormalizer)
# --- Логгер: значения локальных переменных не попадают в лог (#18) ----------------------------------------------------
_LOG_EXCEPTION_SCRIPT = """
import importlib
import os
import sys
from loguru import logger
sys.path.insert(0, os.getcwd())
importlib.import_module("src.app")
def fail():
secret = "local-secret-value"
raise ValueError("boom" if secret else "")
try:
fail()
except ValueError:
logger.exception("ошибка")
logger.complete()
"""
def test_app_import_logs_exceptions_without_local_values(tmp_path: Path) -> None:
"""Импорт приложения настраивает loguru через setup_logger (diagnose=False): трейсбек есть, значений локальных
переменных в нём нет. Отдельный процесс — тесты перенастраивают обработчики loguru, здесь нужен чистый импорт.
Скрипт — файлом: без исходника (`python -c`) loguru не аннотирует строки и тест прошёл бы и с diagnose=True."""
script = tmp_path / "log_exception.py"
script.write_text(_LOG_EXCEPTION_SCRIPT)
result = subprocess.run(
[sys.executable, str(script)],
cwd=Path(__file__).resolve().parents[2],
capture_output=True,
text=True,
timeout=60,
check=True,
)
output = result.stdout + result.stderr
assert "ValueError: boom" in output
assert "local-secret-value" not in output