d5a270f3af
- 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
75 lines
3.4 KiB
Python
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}') |