Python3使用元类实现一个线程安全的logger

该博客介绍了如何使用Python的logging模块创建一个线程安全的单例日志类。日志类实现了将信息日志和错误日志分别写入不同的文件,并且使用TimedRotatingFileHandler按天进行日志轮转。同时,通过过滤器设置,使得信息日志只记录警告及以下级别,而错误日志记录错误及以上级别。
import threading
import logging  # 引入logging模块
import logging.handlers
# 这个路径抽离到配置文件中去
info_log_path = "/tmp/service/info.log"
err_log_path = "/tmp/service/error.log"


class SingletonType(type):
    """线程安全的单例的元类,只要继承了metaclass=SingletonType,就是一个线程安全的单例类,可以抽离到公共文件中被引用"""
    _instance_lock = threading.Lock()

    def __call__(cls, *args, **kwargs):
        if not hasattr(cls, "_instance"):
            with SingletonType._instance_lock:
                if not hasattr(cls, "_instance"):
                    cls._instance = super(SingletonType, cls).__call__(*args, **kwargs)
        return cls._instance


class SingleLogger(metaclass=SingletonType):
    def __init__(self):
        # 第一步,创建一个logger,这里默认是root
        self.logger = logging.getLogger()
        self.logger.setLevel(logging.INFO)  # Log等级总开关

        # 第二步,创建一个handler,用于写入日志文件
        # fh = logging.FileHandler(info_log_path,mode="w")
        info_fh = logging.handlers.TimedRotatingFileHandler(info_log_path, when="midnight")

        # fh.setLevel(logging.DEBUG)  # 输出到file的log等级的开关
        # 设置日志过滤器
        info_filter = logging.Filter()
        info_filter.filter = lambda record: record.levelno <= logging.WARNING  # 设置过滤等级
        info_fh.setLevel(logging.INFO)
        info_fh.addFilter(info_filter)

        # fh = logging.FileHandler(info_log_path,mode="w")
        err_fh = logging.handlers.TimedRotatingFileHandler(err_log_path, when="midnight")

        err_filter = logging.Filter()
        err_filter.filter = lambda record: record.levelno > logging.WARNING
        err_fh.setLevel(logging.ERROR)
        err_fh.addFilter(err_filter)

        # 第三步,定义handler的输出格式
        formatter = logging.Formatter("%(asctime)s - %(filename)s[line:%(lineno)d] - %(levelname)s: %(message)s")
        info_fh.setFormatter(formatter)
        err_fh.setFormatter(formatter)
        # 第四步,将logger添加到handler里面
        self.logger.addHandler(info_fh)
        self.logger.addHandler(err_fh)


logger = SingleLogger().logger
<think> 首先,用户的问题是:"我想查找使用Python创建日志并在其他使用的方法 请问Python创建日志并在其他使用实现方法"。用户引用了几个站内引用,但根据系统指令,我需要专注于用户的问题。 回顾引用: - 引用[1]: 介绍Python实现的日志记录实例。 - 引用[2]: 提到Logger生成日志。 - 引用[3]: 关于自定义日志工具,将日志输出到文件和控制台。 - 引用[4]: 日志监控程序示例。 用户的核心需求是:如何创建一个日志Logger class)并在其他使用它。这意味着我需要提供一个定义的示例,并展示如何在另一个中实例化和使用这个日志。 系统级指令: - 使用$...$用于行内数学表达式(虽然这里可能不涉及数学,但如果有的话)。 - 独立公式用$$...$$(同样,可能不适用)。 - LaTeX语法正确(确保代码块格式正确)。 - 使用中文回答。 - 生成相关问题。 - 回答中引用的段落末尾自然地添加引用标识(如[^1])。引用来自提供的引用列表。 用户上一次的问题:用户的问题是关于创建日志并在其他使用,我应该基于引用提供实现方法。 结构化回答: 1. **介绍**:简要介绍日志的重要性,并基于引用说明。 2. **实现方法**: - 定义一个日志(如CustomLogger),继承自Pythonlogging模块。 - 展示如何配置日志(如输出到文件和控制台)。 - 展示如何在另一个使用这个日志3. **代码示例**:提供Python代码块。 4. **生成相关问题**:在末尾添加相关问题。 基于引用: - 引用[1],[2],[3]都提到了自定义日志。 - 引用[3]特别讨论了创建日志工具,将日志输出到文件和控制台,解决PyCharm乱码问题。 - 引用[4]是关于监控日志,但不是核心需求。 实现思路: - 创建一个CustomLogger使用logging模块。 - 配置日志格式、级别、输出位置(文件和流)。 - 在其他中,实例化CustomLogger使用单例模式共享同一实例。 代码结构: ```python import logging import os from logging.handlers import RotatingFileHandler class CustomLogger: def __init__(self, name=&#39;custom_logger&#39;, log_dir=&#39;logs&#39;, log_file=&#39;app.log&#39;): # 确保日志目录存在 os.makedirs(log_dir, exist_ok=True) log_file_path = os.path.join(log_dir, log_file) # 创建logger self.logger = logging.getLogger(name) self.logger.setLevel(logging.DEBUG) # 设置日志级别 # 创建文件处理器 file_handler = RotatingFileHandler(log_file_path, maxBytes=1024*1024, backupCount=5) file_handler.setLevel(logging.DEBUG) # 创建流处理器(输出到控制台) stream_handler = logging.StreamHandler() stream_handler.setLevel(logging.INFO) # 控制台只输出INFO及以上级别 # 创建格式化器 formatter = logging.Formatter(&#39;%(asctime)s - %(name)s - %(levelname)s - %(message)s&#39;) file_handler.setFormatter(formatter) stream_handler.setFormatter(formatter) # 添加处理器到logger self.logger.addHandler(file_handler) self.logger.addHandler(stream_handler) def get_logger(self): return self.logger # 在其他使用 class MyService: def __init__(self): self.logger = CustomLogger().get_logger() # 实例化日志 def do_something(self): self.logger.info("Starting do_something method") try: # 做一些操作 self.logger.debug("Debug message") except Exception as e: self.logger.error(f"Error occurred: {e}") ``` 在回答中引用相关引用: - 当提到自定义日志时,引用引用[3]或[1]。 - 末尾添加相关问题。 生成相关问题: 1. 如何配置日志级别以实现不同环境的需求? 2. 日志轮转的最佳实践是什么? 3. 如何在多线程环境中安全使用日志? 确保回答是中文。 </think> ### Python创建日志并在其他使用的方法 在Python中,自定义日志并在其他使用是提高代码可维护性的常见实践。基于引用[1]和[3],核心思想是通过Python内置的`logging`模块创建自定义日志,配置日志输出(如文件和控制台),然后在其他中实例化或注入该日志实现日志记录[^1][^3]。以下是具体实现步骤和示例: #### 1. **创建自定义日志** 定义一个`CustomLogger`,继承自`logging.Logger`: ```python import logging import os from logging.handlers import RotatingFileHandler class CustomLogger: def __init__(self, name="app_logger", log_dir="logs", log_file="app.log", level=logging.DEBUG): # 确保日志目录存在 os.makedirs(log_dir, exist_ok=True) log_path = os.path.join(log_dir, log_file) # 创建日志记录器 self.logger = logging.getLogger(name) self.logger.setLevel(level) # 设置日志级别 # 配置日志格式(包含时间、级别、消息) formatter = logging.Formatter(&#39;%(asctime)s - %(name)s - %(levelname)s - %(message)s&#39;) # 文件处理器:输出到文件(支持轮转以防文件过大) file_handler = RotatingFileHandler( log_path, maxBytes=1024*1024, backupCount=5, encoding=&#39;utf-8&#39; ) file_handler.setFormatter(formatter) self.logger.addHandler(file_handler) # 控制台处理器:输出到终端 console_handler = logging.StreamHandler() console_handler.setFormatter(formatter) self.logger.addHandler(console_handler) def info(self, msg): self.logger.info(msg) def warning(self, msg): self.logger.warning(msg) def error(self, msg): self.logger.error(msg) def debug(self, msg): self.logger.debug(msg) ``` **关键点说明**: - **日志轮转**:`RotatingFileHandler`确保日志文件不会过大,自动创建新文件备份(引用[3]优化)[^3]。 - **编码处理**:设置`encoding=&#39;utf-8&#39;`避免PyCharm等IDE中文乱码问题(基于引用[3])。 - **灵活配置**:通过参数调整日志级别(如`logging.INFO`生产环境用)。 #### 2. **在其他使用日志** 在其他中实例化`CustomLogger`,直接调用其方法: ```python class ServiceClass: def __init__(self): # 实例化日志(可复用同一实例) self.logger = CustomLogger(name="ServiceLogger") def execute_task(self): try: self.logger.info("任务开始执行") # 模拟业务逻辑 result = 10 / 2 self.logger.debug(f"计算结果: {result}") except Exception as e: self.logger.error(f"任务失败: {str(e)}") # 使用示例 if __name__ == "__main__": service = ServiceClass() service.execute_task() # 输出日志到文件和控制台 ``` **代码解释**: - **日志注入**:在`ServiceClass`的构造函数中创建`CustomLogger`实例。 - **级别控制**:`info()`用于常规记录,`error()`用于错误捕获。 - **输出效果**:日志同时写入`logs/app.log`文件和终端控制台。 #### 3. **优化实践(基于引用)** - **单例模式**:若需全局共享日志实例,可修改`CustomLogger`为单例(避免多次初始化): ```python class CustomLogger: _instance = None def __new__(cls, *args, **kwargs): if cls._instance is None: cls._instance = super().__new__(cls) return cls._instance # 其余代码不变 ``` - **动态关键字**:如引用[4]所示,可扩展日志支持关键字监控(如匹配"error"时触发警报)[^4]。 ### 总结 通过自定义`CustomLogger`,您可以将日志功能模块化,并在任意中通过实例化或注入方式使用。这提高了代码复用性,便于统一管理日志格式、级别和输出目标。实际项目中,建议结合引用[2]的日志生成模拟方法进行测试[^2]。
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

爱学习的狮王

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值