FreeDATA/freedata_server/log_handler.py

91 lines
2.7 KiB
Python

import logging.config
import structlog
import sys
def setup_logging(filename: str = "", level: str = "DEBUG"):
"""
Configure stdlib logging + structlog.
- File handler: RotatingFileHandler (10 MB, 10 backups)
- Console: colored
"""
timestamper = structlog.processors.TimeStamper(fmt="iso")
pre_chain = [
structlog.stdlib.add_log_level,
timestamper,
]
config_dict = {
"version": 1,
"disable_existing_loggers": False,
"formatters": {
"plain": {
"()": structlog.stdlib.ProcessorFormatter,
"processor": structlog.dev.ConsoleRenderer(colors=False),
"foreign_pre_chain": pre_chain,
},
"colored": {
"()": structlog.stdlib.ProcessorFormatter,
"processor": structlog.dev.ConsoleRenderer(colors=True),
"foreign_pre_chain": pre_chain,
},
},
"handlers": {
"default": {
"level": level,
"class": "logging.StreamHandler",
"formatter": "colored",
"stream": "ext://sys.stdout",
},
},
"loggers": {
"": {
"handlers": ["default"],
"level": level,
"propagate": True,
},
},
}
if filename:
config_dict["handlers"]["file"] = {
"level": level,
"class": "logging.handlers.RotatingFileHandler",
"filename": filename,
"maxBytes": 10 * 1024 * 1024, # 10 MB
"backupCount": 10,
"formatter": "plain",
}
config_dict["loggers"][""]["handlers"].append("file")
logging.config.dictConfig(config_dict)
structlog.configure(
processors=[
structlog.stdlib.add_log_level,
structlog.stdlib.PositionalArgumentsFormatter(),
timestamper,
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,
)
logger = structlog.get_logger()
def handle_exception(exc_type, exc_value, exc_traceback):
if issubclass(exc_type, KeyboardInterrupt):
sys.__excepthook__(exc_type, exc_value, exc_traceback)
return
logger.error("Uncaught exception", exc_info=(exc_type, exc_value, exc_traceback))
sys.excepthook = handle_exception
if filename:
logger.info("Logger initialized", log_file=filename)
return logger