编写Logger通用类
主要功能:
print → DebugLogger → debug.log
log_info() → InfoLogger → info.log
log_time() → InfoLogger → info.log
log_err() → ErrorLogger → error.log
logger.py
import logging.config
import threading
import os
import sys
import time
from abc import ABC, abstractmethod
from datetime import datetime
from typing import Optional
# Setup logs directory
os.makedirs("logs", exist_ok=True)
# Configure logging
logging.config.fileConfig('logging.conf')
class DualLogger(ABC):
def __init__(self, logger: logging.Logger, level: int, use_stderr: bool = False):
self.terminal = sys.__stderr__ if use_stderr else sys.__stdout__
self.logger = logger
self.level = level
def write(self, message: str) -> None:
if not message.strip():
return
self._console(message.rstrip())
self.log(message.rstrip())
def flush(self) -> None:
self.terminal.flush()
@abstractmethod
def log(self, message: str) -> None:
pass
def _console(self, message: str) -> None:
thread_name = threading.current_thread().name
timestamp = datetime.now().strftime('%H:%M:%S.%f')[:-3]
self.terminal.write(f"{timestamp}::{thread_name}::{message}\n")
class DebugLogger(DualLogger):
def log(self, message: str) -> None:
self.logger.debug(message)
class InfoLogger(DualLogger):
def log(self, message: str) -> None:
self.logger.info(message)
class TimeLogger(DualLogger):
def log(self, message: str) -> None:
self.logger.info(message)
class ErrorLogger(DualLogger):
def __init__(self):
super().__init__(logging.getLogger("error"), logging.ERROR, use_stderr=True)
def log(self, message: str) -> None:
self.logger.error(message)
# Configure system loggers
sys.stdout = DebugLogger(logging.getLogger("debug"), logging.DEBUG)
sys.stderr = ErrorLogger()
info_logger = InfoLogger(logging.getLogger("info"), logging.INFO)
class ElapseTimer:
def __init__(self):
self._thread_local = threading.local()
def get_elapsed(self) -> float:
current_time = time.time()
if not hasattr(self._thread_local, 'previous_time'):
self._thread_local.previous_time = current_time
elapsed = current_time - self._thread_local.previous_time
self._thread_local.previous_time = current_time
return elapsed
timer = ElapseTimer()
def time_key(s: Optional[str], logger: DualLogger) -> None:
if not s:
return
try:
elapsed = timer.get_elapsed()
logger.write(f"{elapsed:1.3f}--{s}")
except Exception as e:
sys.__stderr__.write(f"Time key error: {str(e)}\n")
# Public API
def log_err(s: str) -> None:
sys.stderr.write(s)
def log_info(s: str) -> None:
info_logger.write(s)
def log_time(s: str) -> None:
time_key(s, info_logger)
if __name__ == '__main__':
# 普通日志
print("中文 message") # 输出到debug.log
log_info("中文Operation started") # 输出到info.log
# 耗时跟踪
log_time("中文Operation started") # 输出到info.log
time.sleep(0.5)
log_time("中文Operation completed")
log_time("中文Operation started3")
time.sleep(0.5)
log_time("中文Operation completed4")
# 错误处理
try:
1 / 0
except Exception:
log_err("中文Division error occurred") # 输出到error.log
logging.conf
[loggers]
keys=root,debug,info,error
[handlers]
keys=debug_handler,info_handler,error_handler
[formatters]
keys=detailed
[logger_root]
level=DEBUG
handlers=
[logger_debug]
level=DEBUG
handlers=debug_handler
qualname=debug
[logger_info]
level=INFO
handlers=info_handler
qualname=info
[logger_error]
level=ERROR
handlers=error_handler
qualname=error
[formatter_detailed]
format=%(asctime)s.%(msecs)03d %(threadName)s %(levelname)-8s %(message)s
datefmt=%Y-%m-%d %H:%M:%S
[handler_debug_handler]
level=DEBUG
formatter=detailed
class=handlers.RotatingFileHandler
args=('logs/debug.log', 'a', 1048576, 5)
[handler_info_handler]
level=INFO
formatter=detailed
class=handlers.RotatingFileHandler
args=('logs/info.log', 'a', 1048576, 5, 'utf-8')
[handler_error_handler]
level=ERROR
formatter=detailed
class=handlers.RotatingFileHandler
args=('logs/error.log', 'a', 1048576, 5, 'utf-8')