Files
g2s-aggregator/app/runtime/logging.py
T
Раис Юсупалиев 02f5ef93b0
Deploy / deploy (push) Successful in 50s
улучшены логи, otel collector добавлен в git
2026-06-20 05:24:17 +03:00

106 lines
2.9 KiB
Python

"""Centralized runtime logging bootstrap."""
import json
import logging
import sys
from collections.abc import Iterable
from typing import Any, TextIO
import structlog
_UVICORN_LOGGER_NAMES = ("uvicorn", "uvicorn.error", "uvicorn.access")
def configure_logging(
*,
log_level: int = logging.INFO,
stream: TextIO | None = None,
) -> None:
resolved_stream = sys.stdout if stream is None else stream
formatter = structlog.stdlib.ProcessorFormatter(
foreign_pre_chain=list(_shared_processors()),
processors=[
structlog.stdlib.ProcessorFormatter.remove_processors_meta,
structlog.processors.JSONRenderer(sort_keys=True, ensure_ascii=False),
],
)
handler = logging.StreamHandler(resolved_stream)
handler.setLevel(log_level)
handler.setFormatter(formatter)
structlog.reset_defaults()
structlog.configure(
processors=[
structlog.stdlib.PositionalArgumentsFormatter(),
_render_context_in_event,
*_shared_processors(),
structlog.processors.StackInfoRenderer(),
structlog.processors.format_exc_info,
structlog.stdlib.ProcessorFormatter.wrap_for_formatter,
],
logger_factory=structlog.stdlib.LoggerFactory(),
wrapper_class=structlog.stdlib.BoundLogger,
cache_logger_on_first_use=True,
)
_configure_logger(logging.getLogger(), handler=handler, log_level=log_level)
for logger_name in _UVICORN_LOGGER_NAMES:
_configure_logger(
logging.getLogger(logger_name),
handler=handler,
log_level=log_level,
propagate=False,
)
def _shared_processors() -> tuple[structlog.types.Processor, ...]:
return (
structlog.stdlib.add_logger_name,
structlog.stdlib.add_log_level,
structlog.processors.TimeStamper(fmt="iso", utc=True),
)
def _render_context_in_event(
_logger: Any,
_method_name: str,
event_dict: structlog.types.EventDict,
) -> structlog.types.EventDict:
event = str(event_dict.get("event", ""))
context = " ".join(
f"{key}={_serialize_log_value(value)}"
for key, value in sorted(event_dict.items())
if key not in {"event", "exc_info", "stack_info"}
)
event_dict["event"] = f"{event} {context}" if context else event
return event_dict
def _serialize_log_value(value: object) -> str:
return json.dumps(
value,
ensure_ascii=False,
sort_keys=True,
separators=(",", ":"),
default=str,
)
def _configure_logger(
logger: logging.Logger,
*,
handler: logging.Handler,
log_level: int,
propagate: bool = True,
) -> None:
_clear_handlers(logger.handlers)
logger.setLevel(log_level)
logger.handlers = [handler]
logger.propagate = propagate
def _clear_handlers(handlers: Iterable[logging.Handler]) -> None:
for existing_handler in list(handlers):
existing_handler.close()