MicroFish/backend/app/utils/logger.py

127 lines
3.3 KiB
Python

"""
Logging configuration module
Provides unified log management, output to both console and file
"""
import os
import sys
import logging
from datetime import datetime
from logging.handlers import RotatingFileHandler
def _ensure_utf8_stdout():
"""
Ensure stdout/stderr use UTF-8
Work around Windows console garbled Chinese
"""
if sys.platform == 'win32':
# Reconfigure stdout to UTF-8 on Windows
if hasattr(sys.stdout, 'reconfigure'):
sys.stdout.reconfigure(encoding='utf-8', errors='replace')
if hasattr(sys.stderr, 'reconfigure'):
sys.stderr.reconfigure(encoding='utf-8', errors='replace')
# Log directory
LOG_DIR = os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))), 'logs')
def setup_logger(name: str = 'mirofish', level: int = logging.DEBUG) -> logging.Logger:
"""
Set up a logger
Args:
name: logger name
level: log level
Returns:
configured logger
"""
# Ensure log directory exists
os.makedirs(LOG_DIR, exist_ok=True)
# Create logger
logger = logging.getLogger(name)
logger.setLevel(level)
# Prevent logs from propagating to the root logger (avoid double output)
logger.propagate = False
# If a handler is already attached, do not add another
if logger.handlers:
return logger
# Log format
detailed_formatter = logging.Formatter(
'[%(asctime)s] %(levelname)s [%(name)s.%(funcName)s:%(lineno)d] %(message)s',
datefmt='%Y-%m-%d %H:%M:%S'
)
simple_formatter = logging.Formatter(
'[%(asctime)s] %(levelname)s: %(message)s',
datefmt='%H:%M:%S'
)
# 1. File handler — verbose log (date-named, with rotation)
log_filename = datetime.now().strftime('%Y-%m-%d') + '.log'
file_handler = RotatingFileHandler(
os.path.join(LOG_DIR, log_filename),
maxBytes=10 * 1024 * 1024, # 10MB
backupCount=5,
encoding='utf-8'
)
file_handler.setLevel(logging.DEBUG)
file_handler.setFormatter(detailed_formatter)
# 2. Console handler — concise log (INFO and above)
# Use UTF-8 on Windows to avoid garbled output
_ensure_utf8_stdout()
console_handler = logging.StreamHandler(sys.stdout)
console_handler.setLevel(logging.INFO)
console_handler.setFormatter(simple_formatter)
# Add handler
logger.addHandler(file_handler)
logger.addHandler(console_handler)
return logger
def get_logger(name: str = 'mirofish') -> logging.Logger:
"""
Get a logger (creating it if it does not exist)
Args:
name: logger name
Returns:
logger instance
"""
logger = logging.getLogger(name)
if not logger.handlers:
return setup_logger(name)
return logger
# Create the default logger
logger = setup_logger()
# Convenience methods
def debug(msg: str, *args, **kwargs) -> None:
logger.debug(msg, *args, **kwargs)
def info(msg: str, *args, **kwargs) -> None:
logger.info(msg, *args, **kwargs)
def warning(msg: str, *args, **kwargs) -> None:
logger.warning(msg, *args, **kwargs)
def error(msg: str, *args, **kwargs) -> None:
logger.error(msg, *args, **kwargs)
def critical(msg: str, *args, **kwargs) -> None:
logger.critical(msg, *args, **kwargs)