loguru模块bind和filter的联合使用案例:将loguru改造为csv数据记录器

使用场景

在运行程序的过程中,通常需要使用设置日志信息来方便追踪程序运行状态或者是调试,也常常需要进行多次运算并将计算结果进行保存。一般来说,日志信息可以通过logging模块保存,而计算数据的记录常常是基于文本文件的读写功能实现的。如果要统一的实现两个功能,可以通过改造loguru模块进行实现。

这个实现主要是基于filter机制和bind方法

filter函数

使用 loguru 库进行 Python 日志记录时,可以通过定义自定义 filter 函数来收集特定的日志信息。该函数接受一个记录作为参数,并返回一个布尔值:如果返回 True,则记录该日志;如果返回 False,则不记录。

这个 filter 函数可以基于:

  • 日志消息的内容:record["message"]
  • 级别:record["level"]
  • 或者其他任何可用的日志属性来决定是否应该记录该条日志

注意,filter是要在添加``handler时进行配置,将 filter函数作为handlerfilter`参数。

from loguru import logger

def important_log_filter(record):
    # 只有当日志消息中包含 "重要" 关键字时,才记录该日志
    return "重要" in record["message"]

# 配置 logger,添加一个 handler,仅记录通过 filter 函数筛选的日志
logger.add("important_logs.log", filter=important_log_filter)

# 使用示例
logger.info("这是一条普通日志,不会被记录")
logger.info("这是一条重要日志,将会被记录")


bind函数

bind() 方法是一个强大的功能,用于向日志记录附加额外的上下文信息。这种方法非常有用,因为它允许为日志消息添加元数据,而无需在每次记录日志时重复这些信息。

bind() 的特点是不接受一组预定义的参数,而是接受任意数量的关键字参数,这些参数随后会成为日志消息的一部分,增加了日志的描述性和可追溯性,如用户ID、请求ID或任何其他对理解或分析日志有帮助的数据。而这些字段都会添加到 record["extra"]中,然后可以在:

  • filter函数

    from loguru import logger
    
    def user_filter(record):
        # 假设我们只对 user_id 为 "123" 的日志消息感兴趣
        return record["extra"].get("user_id") == "123"
    
    # 配置 logger,添加一个 handler,仅记录通过 user_filter 函数筛选的日志
    logger.add("filtered_logs.log", filter=user_filter)
    
    # 绑定 user_id 并尝试记录几条消息
    logger.bind(user_id="123").info("这条消息会被记录,因为用户ID匹配")
    logger.bind(user_id="456").info("这条消息不会被记录,因为用户ID不匹配")
    
    
  • format字符串or函数中

    # 自定义日志格式,包括绑定的上下文信息
    format = "{time} | {level} | {extra[user_id]} | {message}"
    
    # 添加一个 handler,使用上述格式
    logger.add("my_log.log", format=format)
    
    # 使用绑定的 logger 记录日志
    logger.info("完成了一个任务")
    
    

    用户ID是通过 bind() 方法绑定到日志记录器上的。

通过record["extra"][key"]进行使用。

bind() 方法返回一个新的 logger 实例,这个实例在其所有日志记录中自动包含了绑定的上下文信息。这意味着可以在程序的特定部分创建一个带有特定上下文的 logger,而不会影响全局 logger 实例或其他带有不同上下文的 logger 实例。

就是说,bind方法不会影响原来的logger实例的上下文信息

from loguru import logger

# 原始 logger 实例
logger.info("这是一条没有上下文信息的日志消息")

# 使用 bind() 添加上下文信息,创建一个新的 logger 实例
logger_with_context = logger.bind(user_id="123")

# 使用新的 logger 实例记录消息
logger_with_context.info("这条消息包含上下文信息")

# 再次使用原始 logger 实例记录消息
logger.info("这又是一条没有上下文信息的日志消息")

注意

  • 使用 logurubind() 方法时,不需要重新指定日志文件路径。
  • 需要在应用的不同部分添加不同的上下文信息时,可以在各个部分使用 bind() 方法,这样可以确保日志消息包含了正确的上下文信息,而不需要为每个上下文重新配置日志路径。
  • bind().add()loguru 中扮演了不同的角色:.add() 用于配置日志的输出目标和格式,而 bind() 用于为日志消息添加上下文信息。

赋予日志记录一个label的属性

这里的意图是,通过一个label="data"的标签,实现重要数据和程序运行信息分离的功能

loguru 库中,直接给日志消息指定一个标签(如 label 属性)并不是内置的功能,但是可以通过自定义消息格式来间接实现这个目的。要达到这个目的,需要联合filterbind

  1. 只使用 Filter 函数动态添加标签

    通过自定义函数,确实可以达到动态添加label属性的方法,但是这种方法比较低效,因为要通过判断reord的一些属性来实现,另外就是不方便手动控制:

    from loguru import logger
    
    def tag_logger(record):
        # 假设我们根据消息的内容动态添加标签
        if "错误" in record["message"]:
            record["extra"]["label"] = "错误"
        else:
            record["extra"]["label"] = "通用"
    
    # 配置 logger,添加一个 handler,并使用自定义的 filter 函数
    logger.add("tagged_logs.log", filter=tag_logger, format="{time} | {level} | {extra[label]} | {message}")
    
    # 发送日志消息
    logger.info("这是一条普通消息")
    logger.error("这是一条包含错误的消息")
    
    
    
  2. 通过bind添加上下文信息,并进行显示格式化。

    这个方法,添加label属性,就比较方便,只要引用bind后的logger实例就行。

    from loguru import logger
    
    # 定义一个自定义的日志格式,其中包含了消息的标签
    log_format = "<green>{time:YYYY-MM-DD HH:mm:ss}</green> | <level>{level: <8}</level> | <cyan>{extra[label]}</cyan> | <white>{message}</white>"
    
    # 配置 logger,使用自定义的格式
    # 注意:需要确保所有的日志消息都添加了相应的 `label`,否则会抛出 KeyError
    logger.add("labeled_logs.log", format=log_format, level="INFO")
    
    # 发送一条带有标签的日志消息
    logger.bind(label="特别").info("这是一条带有标签的日志")
    
    
    

    在这个例子中,通过 .bind() 方法为日志消息附加了额外的上下文信息( label 标签),然后在自定义日志格式中引用了这个标签,并设定格式。

实际案例代码

  1. bind和filter联合使用。

    这样组合,能完成的功能更丰富。也很实现运行记录和计算数据的分别记录器。

    from loguru import logger
    import sys
    
    def only_trace(record):
        '''
        只保存程序的运行记录
        :param record:
        :return:
        '''
        return "data" not in record["extra"]
    
    def only_data(record):
        return "data" in record["extra"]
    
    # 移除默认的控制台输出
    logger.remove()
    
    # 自定义日志格式
    log_format = "<green>{time:YYYY-MM-DD HH:mm:ss}</green> | <level>{level}</level> | <cyan>{name}</cyan>: <cyan>{line}</cyan> - <level>{message}</level>"
    # 添加控制台handler,使用自定义格式
    logger.add(sys.stdout, format=log_format)
    logger.add("./log/app_trace.log", format=log_format,filter=only_trace, level="DEBUG")
    data_format = "{time:YYYY-MM-DD HH:mm:ss}\t{message}"
    
    # 写模式,每次程序启动并添加 handler 时,app_data.csv 将会被新内容覆盖。
    logger.add("./log/app_data.csv", format=data_format,filter=only_data, level="DEBUG",mode="w")
    data_logger=logger.bind(data=True)
    
    def get_logger():
        return logger
    def get_datalogger():
        return data_logger
    

注意,这里没有添加``label标签,而是直接给data=True这样的上下文,方便filter函数的构造

  • 17
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
std::bind和std::function是C++11中引入的函数相关的工具。 std::bind是一个函数模板,用于将函数或成员函数与其参数绑定,生成一个新的可调用对象。它可以用来延迟函数的调用或者改变函数的参数顺序。std::bind的基本语法如下: ```cpp auto newFunc = std::bind(func, arg1, arg2, ...); ``` 其中,func是要绑定的函数或成员函数,arg1、arg2等是要绑定的参数。newFunc是一个可调用对象,可以像普通函数一样进行调用。 std::function是一个通用的函数封装器,可以存储、复制和调用任意可调用对象(函数、函数指针、成员函数指针等)。它可以用来实现回调函数、事件处理等功能。std::function的基本语法如下: ```cpp std::function<返回类型(参数类型)> func; ``` 其中,返回类型和参数类型可以根据实际情况进行定义。func可以被赋值为任意可调用对象,并且可以像普通函数一样进行调用。 下面是一个示例代码,演示了std::bind和std::function的使用: ```cpp #include <iostream> #include <functional> void func(int a, int b) { std::cout << "a + b = " << a + b << std::endl; } class Foo { public: void memberFunc(int a, int b) { std::cout << "a * b = " << a * b << std::endl; } }; int main() { auto newFunc = std::bind(func, 10, std::placeholders::_1); newFunc(20); // 输出:a + b = 30 Foo foo; auto memberFunc = std::bind(&Foo::memberFunc, &foo, std::placeholders::_1, 5); memberFunc(4); // 输出:a * b = 20 std::function<void(int, int)> callback = func; callback(3, 4); // 输出:a + b = 7 return 0; } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

月司

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

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

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

打赏作者

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

抵扣说明:

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

余额充值