130 lines
4.0 KiB
Python
130 lines
4.0 KiB
Python
#!/usr/bin/env python3
|
|
# -*- coding: utf-8 -*-
|
|
"""
|
|
跨平台日志工具模块
|
|
功能:
|
|
1. 自动适配不同操作系统的日志路径
|
|
2. 支持中文等非ASCII字符
|
|
3. 日志文件自动按日期分割
|
|
4. 控制台与文件双输出
|
|
"""
|
|
|
|
import os
|
|
import sys
|
|
import logging
|
|
from logging.handlers import TimedRotatingFileHandler
|
|
from datetime import datetime
|
|
import platform
|
|
|
|
|
|
class CrossPlatformLogger:
|
|
def __init__(self, name="intelligence_system"):
|
|
"""
|
|
初始化跨平台日志系统
|
|
|
|
参数:
|
|
name: 日志名称(用于创建日志文件夹)
|
|
"""
|
|
self.system = platform.system().lower()
|
|
self.logger = logging.getLogger(name)
|
|
self.logger.setLevel(logging.INFO)
|
|
|
|
# 确保日志目录存在
|
|
self.log_dir = self._get_log_dir(name)
|
|
os.makedirs(self.log_dir, exist_ok=True)
|
|
|
|
# 配置日志格式
|
|
formatter = logging.Formatter(
|
|
'%(asctime)s - %(name)s - %(levelname)s - %(message)s',
|
|
datefmt='%Y-%m-%d %H:%M:%S'
|
|
)
|
|
|
|
# 控制台处理器
|
|
self._setup_console_handler(formatter)
|
|
|
|
# 文件处理器(按天分割)
|
|
self._setup_file_handler(formatter)
|
|
|
|
# 处理未捕获的异常
|
|
sys.excepthook = self.handle_uncaught_exception
|
|
|
|
def _get_log_dir(self, name: str) -> str:
|
|
"""获取适合当前平台的日志目录路径"""
|
|
if self.system == 'windows':
|
|
base_dir = os.path.join(os.environ['APPDATA'], name)
|
|
elif self.system == 'darwin': # macOS
|
|
base_dir = os.path.expanduser(f"~/Library/Logs/{name}")
|
|
else: # Linux及其他Unix-like系统
|
|
base_dir = f"/var/log/{name}" if os.access("/var/log", os.W_OK) \
|
|
else os.path.expanduser(f"~/.local/share/{name}")
|
|
|
|
return base_dir
|
|
|
|
def _setup_console_handler(self, formatter: logging.Formatter):
|
|
"""配置控制台输出(兼容不同终端的编码)"""
|
|
console = logging.StreamHandler()
|
|
|
|
# Windows终端特殊处理
|
|
if self.system == 'windows' and not sys.stdout.isatty():
|
|
import colorama
|
|
try:
|
|
colorama.init()
|
|
except ImportError:
|
|
pass
|
|
|
|
# 解决Windows控制台编码问题
|
|
if sys.stdout.encoding != 'utf-8':
|
|
import io
|
|
sys.stdout = io.TextIOWrapper(
|
|
sys.stdout.buffer,
|
|
encoding='utf-8',
|
|
errors='replace'
|
|
)
|
|
|
|
console.setFormatter(formatter)
|
|
self.logger.addHandler(console)
|
|
|
|
def _setup_file_handler(self, formatter: logging.Formatter):
|
|
"""配置日志文件输出(UTF-8编码)"""
|
|
log_file = os.path.join(
|
|
self.log_dir,
|
|
f"{datetime.now().strftime('%Y%m%d')}.log"
|
|
)
|
|
|
|
# 使用TimedRotatingFileHandler实现日志分割
|
|
file_handler = TimedRotatingFileHandler(
|
|
filename=log_file,
|
|
when='midnight', # 每天午夜分割
|
|
encoding='utf-8',
|
|
backupCount=30 # 保留30天日志
|
|
)
|
|
file_handler.setFormatter(formatter)
|
|
self.logger.addHandler(file_handler)
|
|
|
|
def handle_uncaught_exception(self, exc_type, exc_value, exc_traceback):
|
|
"""全局异常捕获"""
|
|
self.logger.error(
|
|
"未捕获的异常:",
|
|
exc_info=(exc_type, exc_value, exc_traceback)
|
|
)
|
|
|
|
@staticmethod
|
|
def get_logger(name: str = None) -> logging.Logger:
|
|
"""获取配置好的日志实例"""
|
|
return CrossPlatformLogger(name).logger
|
|
|
|
|
|
def setup_logging(name: str = "intelligence_system"):
|
|
"""快速配置日志(兼容旧代码)"""
|
|
return CrossPlatformLogger(name).logger
|
|
|
|
|
|
# 测试代码
|
|
if __name__ == "__main__":
|
|
logger = CrossPlatformLogger().logger
|
|
logger.info("这是一条info日志(包含中文测试)")
|
|
try:
|
|
1 / 0
|
|
except Exception as e:
|
|
logger.error("除零错误示例", exc_info=True)
|