|
| 1 | +#!/usr/bin/env python |
| 2 | +# -*- coding: utf-8 -*- |
| 3 | + |
| 4 | +import os |
| 5 | +import logging |
| 6 | +import datetime |
| 7 | +import json |
| 8 | +import traceback |
| 9 | +from logging.handlers import TimedRotatingFileHandler |
| 10 | + |
| 11 | +class SimpleLogger: |
| 12 | + """使用标准logging模块实现的日志记录器,保持与原来简单日志记录器相同的使用方式""" |
| 13 | + |
| 14 | + # 日志级别映射 |
| 15 | + _level_map = { |
| 16 | + 'DEBUG': logging.DEBUG, |
| 17 | + 'INFO': logging.INFO, |
| 18 | + 'WARNING': logging.WARNING, |
| 19 | + 'ERROR': logging.ERROR, |
| 20 | + 'CRITICAL': logging.CRITICAL |
| 21 | + } |
| 22 | + |
| 23 | + def __init__(self, name, log_dir=None): |
| 24 | + """初始化日志记录器 |
| 25 | + |
| 26 | + Args: |
| 27 | + name: 日志记录器名称 |
| 28 | + log_dir: 日志目录,默认为当前工作目录下的 simple_logs 目录 |
| 29 | + """ |
| 30 | + self.name = name |
| 31 | + |
| 32 | + # 确定日志目录 |
| 33 | + if log_dir is None: |
| 34 | + self.log_dir = os.path.join(os.getcwd(), 'simple_logs') |
| 35 | + else: |
| 36 | + self.log_dir = log_dir |
| 37 | + |
| 38 | + # 确保日志目录存在 |
| 39 | + try: |
| 40 | + os.makedirs(self.log_dir, exist_ok=True) |
| 41 | + except Exception as e: |
| 42 | + print(f"创建日志目录失败: {str(e)}") |
| 43 | + |
| 44 | + # 创建日志文件目录结构:按年月划分 |
| 45 | + today = datetime.datetime.now() |
| 46 | + year_month_dir = os.path.join(self.log_dir, f"{today.year:04d}-{today.month:02d}") |
| 47 | + try: |
| 48 | + os.makedirs(year_month_dir, exist_ok=True) |
| 49 | + except Exception as e: |
| 50 | + print(f"创建年月日志目录失败: {str(e)}") |
| 51 | + |
| 52 | + # 日志文件路径基础名 |
| 53 | + self.log_file_base = os.path.join(year_month_dir, f"{name}") |
| 54 | + self.log_file = f"{self.log_file_base}.log" |
| 55 | + |
| 56 | + # 创建logging对象 |
| 57 | + self.logger = logging.getLogger(name) |
| 58 | + self.logger.setLevel(logging.DEBUG) # 设置为最低级别,允许所有日志通过 |
| 59 | + |
| 60 | + # 清除现有的处理器,避免重复输出 |
| 61 | + if self.logger.handlers: |
| 62 | + self.logger.handlers.clear() |
| 63 | + |
| 64 | + # 创建文件处理器,使用TimedRotatingFileHandler实现按日期划分日志文件 |
| 65 | + file_handler = TimedRotatingFileHandler( |
| 66 | + self.log_file, |
| 67 | + when='midnight', # 每天午夜切换一次 |
| 68 | + interval=1, # 间隔为1天 |
| 69 | + backupCount=31, # 保留最近31天的日志 |
| 70 | + encoding='utf-8', |
| 71 | + atTime=datetime.time(0, 0, 0) # 在午夜0点执行 |
| 72 | + ) |
| 73 | + # 设置日志文件后缀格式为日期 |
| 74 | + file_handler.suffix = "%Y-%m-%d.log" |
| 75 | + file_handler.setLevel(logging.DEBUG) |
| 76 | + |
| 77 | + # 创建格式化器,确保日志内容不会被logging自动格式化 |
| 78 | + formatter = logging.Formatter('%(message)s') |
| 79 | + file_handler.setFormatter(formatter) |
| 80 | + |
| 81 | + # 将处理器添加到logger |
| 82 | + self.logger.addHandler(file_handler) |
| 83 | + |
| 84 | + # 打印日志文件路径 |
| 85 | + print(f"简单日志记录器初始化: {self.log_file}") |
| 86 | + |
| 87 | + def _write_log(self, level, message, extra=None): |
| 88 | + """写入日志 |
| 89 | + |
| 90 | + Args: |
| 91 | + level: 日志级别 |
| 92 | + message: 日志消息 |
| 93 | + extra: 额外信息 |
| 94 | + """ |
| 95 | + try: |
| 96 | + # 获取当前时间 |
| 97 | + now = datetime.datetime.now() |
| 98 | + now_str = now.strftime('%Y-%m-%d %H:%M:%S') |
| 99 | + |
| 100 | + # 检查是否需要更新日志目录(如果跨月份) |
| 101 | + today = datetime.datetime.now() |
| 102 | + year_month_dir = os.path.join(self.log_dir, f"{today.year:04d}-{today.month:02d}") |
| 103 | + expected_log_file_base = os.path.join(year_month_dir, f"{self.name}") |
| 104 | + |
| 105 | + # 如果当前日志文件基础路径与预期不符,则需要更新处理器 |
| 106 | + if self.log_file_base != expected_log_file_base: |
| 107 | + # 更新日志文件路径 |
| 108 | + self.log_file_base = expected_log_file_base |
| 109 | + self.log_file = f"{self.log_file_base}.log" |
| 110 | + |
| 111 | + # 确保目录存在 |
| 112 | + try: |
| 113 | + os.makedirs(year_month_dir, exist_ok=True) |
| 114 | + except Exception as e: |
| 115 | + print(f"创建年月日志目录失败: {str(e)}") |
| 116 | + |
| 117 | + # 构建日志内容 |
| 118 | + log_data = { |
| 119 | + 'time': now_str, |
| 120 | + 'level': level, |
| 121 | + 'module': self.name, |
| 122 | + 'message': message |
| 123 | + } |
| 124 | + |
| 125 | + # 添加额外信息 |
| 126 | + if extra: |
| 127 | + log_data.update(extra) |
| 128 | + |
| 129 | + # 转换为JSON字符串 |
| 130 | + log_content = json.dumps(log_data, ensure_ascii=False) |
| 131 | + |
| 132 | + # 使用logging模块记录日志 |
| 133 | + log_level = self._level_map.get(level, logging.INFO) |
| 134 | + self.logger.log(log_level, log_content) |
| 135 | + |
| 136 | + return True |
| 137 | + except Exception as e: |
| 138 | + print(f"写入日志失败: {str(e)}") |
| 139 | + print(traceback.format_exc()) |
| 140 | + return False |
| 141 | + |
| 142 | + def info(self, message, extra=None): |
| 143 | + """记录INFO级别日志""" |
| 144 | + return self._write_log('INFO', message, extra) |
| 145 | + |
| 146 | + def warning(self, message, extra=None): |
| 147 | + """记录WARNING级别日志""" |
| 148 | + return self._write_log('WARNING', message, extra) |
| 149 | + |
| 150 | + def error(self, message, extra=None): |
| 151 | + """记录ERROR级别日志""" |
| 152 | + return self._write_log('ERROR', message, extra) |
| 153 | + |
| 154 | + def debug(self, message, extra=None): |
| 155 | + """记录DEBUG级别日志""" |
| 156 | + return self._write_log('DEBUG', message, extra) |
| 157 | + |
| 158 | + def critical(self, message, extra=None): |
| 159 | + """记录CRITICAL级别日志""" |
| 160 | + return self._write_log('CRITICAL', message, extra) |
| 161 | + |
| 162 | +# 日志实例缓存,避免重复创建 |
| 163 | +_logger_cache = {} |
| 164 | + |
| 165 | +# 获取日志记录器 |
| 166 | +def get_simple_logger(name): |
| 167 | + """获取简单日志记录器 |
| 168 | + |
| 169 | + Args: |
| 170 | + name: 日志记录器名称 |
| 171 | + |
| 172 | + Returns: |
| 173 | + SimpleLogger: 简单日志记录器实例 |
| 174 | + """ |
| 175 | + # 使用缓存避免重复创建同名日志器 |
| 176 | + if name not in _logger_cache: |
| 177 | + _logger_cache[name] = SimpleLogger(name) |
| 178 | + return _logger_cache[name] |
0 commit comments