Files
LedgerLine/common/logging_config.py
T

65 lines
2.2 KiB
Python
Raw Normal View History

import logging
import sys
import structlog
# Loggers that install their own handlers (uvicorn's dictConfig runs before the
# app is imported). Their handlers are stripped so records propagate to the
# root handler below and come out as JSON like everything else.
# uvicorn.access is left alone on purpose: --no-access-log works by setting its
# propagate=False, and re-enabling propagation would turn access logs back on.
_FOREIGN_LOGGERS = ("uvicorn", "uvicorn.error")
def _add_service(service_name: str):
def processor(logger, method_name, event_dict):
event_dict["service"] = service_name
return event_dict
return processor
def configure_logging(service_name: str) -> structlog.stdlib.BoundLogger:
"""One JSON object per line on stdout, for structlog and stdlib loggers alike.
`shared` runs for every record, whichever API emitted it: structlog calls
pass through it via structlog.configure(), stdlib records (uvicorn, the
consumers' logging.getLogger(...)) via the formatter's foreign_pre_chain.
"""
shared = [
structlog.contextvars.merge_contextvars,
structlog.stdlib.add_log_level,
structlog.stdlib.add_logger_name,
structlog.processors.TimeStamper(fmt="iso", utc=True),
_add_service(service_name),
]
structlog.configure(
processors=[*shared, structlog.stdlib.ProcessorFormatter.wrap_for_formatter],
logger_factory=structlog.stdlib.LoggerFactory(),
wrapper_class=structlog.stdlib.BoundLogger,
cache_logger_on_first_use=True,
)
formatter = structlog.stdlib.ProcessorFormatter(
foreign_pre_chain=shared,
processors=[
structlog.stdlib.ProcessorFormatter.remove_processors_meta,
structlog.processors.format_exc_info,
structlog.processors.JSONRenderer(),
],
)
handler = logging.StreamHandler(sys.stdout)
handler.setFormatter(formatter)
root = logging.getLogger()
root.handlers = [handler]
root.setLevel(logging.INFO)
for name in _FOREIGN_LOGGERS:
foreign = logging.getLogger(name)
foreign.handlers = []
foreign.propagate = True
return structlog.get_logger(service_name)