50 lines
1.9 KiB
Python
50 lines
1.9 KiB
Python
import logging
|
||
import sys
|
||
from contextvars import ContextVar
|
||
from logging.handlers import RotatingFileHandler
|
||
|
||
# Храним trace_id текущего потока/корутины.
|
||
TRACE_ID: ContextVar[str] = ContextVar("trace_id", default="-")
|
||
|
||
|
||
class TraceIdFilter(logging.Filter):
|
||
# Добавляет trace_id в каждую запись лога для сквозной трассировки.
|
||
def filter(self, record: logging.LogRecord) -> bool:
|
||
record.trace_id = TRACE_ID.get()
|
||
return True
|
||
|
||
|
||
def set_trace_id(trace_id: str) -> None:
|
||
TRACE_ID.set(trace_id)
|
||
|
||
|
||
def setup_logging(level: str = "INFO", log_file: str | None = None) -> None:
|
||
# stderr — Docker и Celery prefork корректно его подхватывают.
|
||
handlers: list[logging.Handler] = [logging.StreamHandler(sys.stderr)]
|
||
if log_file:
|
||
# Ротация файлового лога: 50MB × 5 файлов, чтобы диск VPS не переполнился
|
||
# при многомесячной работе.
|
||
handlers.append(
|
||
RotatingFileHandler(
|
||
log_file,
|
||
maxBytes=50 * 1024 * 1024,
|
||
backupCount=5,
|
||
encoding="utf-8",
|
||
)
|
||
)
|
||
trace_filter = TraceIdFilter()
|
||
for handler in handlers:
|
||
handler.addFilter(trace_filter)
|
||
handler.setLevel(getattr(logging, level.upper(), logging.INFO))
|
||
root = logging.getLogger()
|
||
root.setLevel(getattr(logging, level.upper(), logging.INFO))
|
||
# Убираем старые хендлеры, чтобы не дублировать после fork.
|
||
root.handlers.clear()
|
||
for handler in handlers:
|
||
root.addHandler(handler)
|
||
fmt = logging.Formatter(
|
||
"%(asctime)s | %(levelname)s | %(name)s | trace=%(trace_id)s | %(message)s"
|
||
)
|
||
for handler in root.handlers:
|
||
handler.setFormatter(fmt)
|