Files
vector/backend/logging_setup.py
Sadmin d5a270f3af B22: прикладное логирование vector.* + администрирование в доках (по образцу РВС)
- backend/logging_setup.py: setup_logging() (stdout INFO + файл /tmp/vector/vector.log,
  ротация 5×2 МБ, VectorLOGLEVEL/VectorLOGFILE/VectorLOGDIR), get_logger('vector.*')
- события: vector.analyze (матрасчёт с длительностью), vector.auth (login_ok/failed/
  lockout), vector.audit (каждая запись), vector.contour (ok/failed/rejected),
  vector.operations (operation_complete), vector.geocode (успех/timeout/ошибка)
- существующие логгеры переведены в namespace vector.* (geo, geo.osm, rules, water, admin)
- README §13а: таблица наблюдаемых событий + правила (без ПДн/секретов)
- SETUP_GUIDE: §15 RPO/RTO + backup≠restore, §16 grep по vector.*, §17 диагностика
  по цепочкам (БД, 500, fallback-зоны, вход), §17а чек-лист передачи проекта
- DEPLOY.md: вместо устаревшего дубля — ссылка на актуальные доки + шпаргалка стенда
- тесты: 239 passed
2026-09-25 09:38:39 +03:00

75 lines
3.4 KiB
Python

"""Единая настройка логирования ВЕКТОРа (B22).
Все модульные логгеры проекта — `logging.getLogger('vector.<модуль>')` —
конфигурируются один раз при старте приложения (backend.main → setup_logging()).
Формат: время, уровень, логгер, сообщение. Вывод — stdout (контейнер),
дополнительно файл /tmp/vector/vector.log внутри контейнера (ротация 5×2 МБ).
Наблюдаемые события (ключевые слова для grep по логам):
vector.analyze — анализ: case_id, радиус, зоны, время расчёта
vector.auth — login_ok / login_failed / lockout / logout
vector.contour — отправка зон в КОНТУР (ok/failed)
vector.admin — синхронизация закрытия кейса
vector.audit — запись аудит-события (кто, что)
vector.geo — геослой: PostGIS-режим, fallback на Overpass, ошибки
"""
from __future__ import annotations
import logging
import logging.handlers
import os
from pathlib import Path
_LOG_FORMAT = '%(asctime)s %(levelname)s %(name)s: %(message)s'
_LOG_DATEFMT = '%Y-%m-%d %H:%M:%S'
# Порог вывода в stdout: INFO на сервере, DEBUG при VectorLOGLEVEL=debug
_LEVEL = os.getenv('VectorLOGLEVEL', 'info').upper()
# Файл-хэндлер: по умолчанию включён (внутри контейнера /tmp/vector).
_FILE_ENABLED = os.getenv('VectorLOGFILE', '1').strip().lower() not in {'0', 'false', 'no'}
_FILE_DIR = Path(os.getenv('VectorLOGDIR', '/tmp/vector'))
def setup_logging() -> None:
"""Конфигурация корневого пакета 'vector'. Идемпотентна."""
root = logging.getLogger('vector')
if getattr(root, '_vector_configured', False):
return
root.setLevel(logging.DEBUG)
fmt = logging.Formatter(_LOG_FORMAT, datefmt=_LOG_DATEFMT)
stream = logging.StreamHandler()
stream.setLevel(getattr(logging, _LEVEL if _LEVEL in {'DEBUG', 'INFO', 'WARNING', 'ERROR'} else 'INFO'))
stream.setFormatter(fmt)
root.addHandler(stream)
if _FILE_ENABLED:
try:
_FILE_DIR.mkdir(parents=True, exist_ok=True)
file_handler = logging.handlers.RotatingFileHandler(
_FILE_DIR / 'vector.log',
maxBytes=2 * 1024 * 1024,
backupCount=5,
encoding='utf-8',
)
file_handler.setLevel(logging.INFO)
file_handler.setFormatter(fmt)
root.addHandler(file_handler)
except OSError:
root.warning('Файл-лог недоступен (%s) — только stdout', _FILE_DIR)
# uvicorn-логи ошибок приложения (traceback-и) идут тем же форматом
uvicorn_error = logging.getLogger('uvicorn.error')
uvicorn_error.handlers.clear()
uvicorn_error.propagate = True
root._vector_configured = True # type: ignore[attr-defined]
def get_logger(name: str) -> logging.Logger:
"""Модульный логгер: get_logger('analyze') → 'vector.analyze'."""
short = name.removeprefix('vector.')
return logging.getLogger(f'vector.{short}')