|
import logging |
|
import json |
|
from datetime import datetime |
|
import os |
|
from pathlib import Path |
|
import sys |
|
import traceback |
|
from typing import Dict, Any |
|
|
|
class CustomFormatter(logging.Formatter): |
|
"""Custom formatter that includes extra fields in the message""" |
|
def format(self, record: logging.LogRecord) -> str: |
|
|
|
message = super().format(record) |
|
|
|
|
|
if hasattr(record, 'extra'): |
|
extras = ' | '.join(f"{k}={v}" for k, v in record.extra.items()) |
|
message = f"{message} | {extras}" |
|
|
|
return message |
|
|
|
def setup_logging() -> None: |
|
"""Configure logging with custom formatter""" |
|
|
|
logs_dir = Path("logs") |
|
logs_dir.mkdir(exist_ok=True) |
|
|
|
|
|
console_formatter = CustomFormatter( |
|
'%(asctime)s | %(levelname)s | %(name)s | %(message)s', |
|
datefmt='%Y-%m-%d %H:%M:%S' |
|
) |
|
|
|
file_formatter = CustomFormatter( |
|
'%(asctime)s | %(levelname)s | %(name)s | %(message)s', |
|
datefmt='%Y-%m-%d %H:%M:%S' |
|
) |
|
|
|
|
|
console_handler = logging.StreamHandler(sys.stdout) |
|
console_handler.setFormatter(console_formatter) |
|
|
|
|
|
current_time = datetime.now().strftime('%Y%m%d_%H%M%S') |
|
file_handler = logging.FileHandler( |
|
logs_dir / f'app_{current_time}.log', |
|
encoding='utf-8' |
|
) |
|
file_handler.setFormatter(file_formatter) |
|
|
|
|
|
root_logger = logging.getLogger() |
|
root_logger.setLevel(logging.DEBUG) |
|
|
|
|
|
root_logger.handlers = [] |
|
|
|
|
|
root_logger.addHandler(console_handler) |
|
root_logger.addHandler(file_handler) |
|
|
|
def get_logger(name: str) -> logging.Logger: |
|
"""Get a logger with the given name""" |
|
return logging.getLogger(name) |
|
|
|
class LoggerMixin: |
|
"""Mixin to add logging capabilities to a class""" |
|
@classmethod |
|
def get_logger(cls) -> logging.Logger: |
|
return get_logger(cls.__name__) |
|
|
|
@property |
|
def logger(self) -> logging.Logger: |
|
return get_logger(self.__class__.__name__) |