【问题标题】:How to log messages from different threads to different files?如何将来自不同线程的消息记录到不同的文件?
【发布时间】:2020-05-15 11:58:48
【问题描述】:

我有一个Driver.py 脚本,它根据给定的输入调用多个threads。线程基本上是运行选定对象的模块。所以Driver.py 可以调用thread_1.run()thread_2.run()thread_3.run(),并继续其进程。

Driver.py 将其输出记录到 main.log 文件夹中,我希望线程在其中将其输出记录到每个文件的唯一文件名中。 Driver.pyThreads 还使用在不同文件上定义的通用模块,它们还记录信息。

我首先在Driver 上调用setup_load("main.log"),然后在每个Thread 中调用setup_load(f"thread_{jobid}.log")。我意识到当 Thread 被调用时,现在 Driver.py 写入线程的日志文件。我可能会在 Thread 中使用不同的记录器,但是当 Thread 调用另一个模块时,因为这些通用模块正在使用 import logging,它们会写入根记录器定义的文件名。


=> 是否可以将来自不同线程的消息记录到不同的文件中?我在 SO (for example) 上找到了多个答案,但没有一个涵盖在不同文件上调用另一个模块时,他们如何找出他们可以使用的 logger

=> 所以我面临的问题是,由于每个线程都使用相同的底层记录器,当我在一个线程中更改 logging.basicConfig 的文件路径时,它会影响整个类线程和驱动程序,因为他们都在使用它。

=> 来自线程或驱动程序调用的不同模块的函数如何理解它应该选择哪个记录器?


How to change filehandle with Python logging on the fly with different classes and imports 的评论部分有讨论和推荐的解决方案。

@Martijn Pieters:

下一个选项:创建每个线程的处理程序,给每个处理程序一个过滤器 过滤日志记录thread 属性。将过滤器附加到任何 其他为 thread 设置的日志记录返回 False 的处理程序

【问题讨论】:

    标签: python logging


    【解决方案1】:

    是的,您可以将来自不同线程的日志条目定向到不同的文件。您需要:

    • 创建一个log filter,它可以通过LogRecord.thread or LogRecord.threadName attribute过滤记录
    • 创建一个过滤器,接受具有特定或所有线程 ID 的记录。
    • 为每个线程创建一个日志处理程序,为其提供一个日志过滤器,该过滤器只接受其特定线程的日志记录。
    • 将忽略线程日志记录的过滤器附加到任何其他处理程序。

    过滤时,您可以选择过滤线程 id(threading.get_ident() 返回的值)或线程名称(无论您作为 name 参数传递给 Thread() object 的任何内容)。如果您的线程名称有一个模式,那么您可以在这里使用它。

    创建自定义过滤器非常简单:

    import threading
    from logging import Filter
    
    class ThreadFilter(Filter):
        """Only accept log records from a specific thread or thread name"""
    
        def __init__(self, threadid=None, threadname=None):
            if threadid is None and threadname is None:
                raise ValueError("Must set at a threadid and/or threadname to filter on")
            self._threadid = threadid
            self._threadname = threadname
    
        def filter(self, record):
            if self._threadid is not None and record.thread != self._threadid:
                return False
            if self._threadname is not None and record.threadName != self._threadname:
                return False
            return True
    
    class IgnoreThreadsFilter(Filter):
        """Only accepts log records that originated from the main thread"""
    
        def __init__(self):
            self._main_thread_id = threading.main_thread().ident
    
        def filter(self, record):
            return record.thread == self._main_thread_id
    

    如果要匹配特定模式,请相应地调整代码。

    【讨论】:

    • 我很困惑,如果我有一个由主线程和/或特定线程调用的模块,例如:def hello_world(): logging.info("hello");我需要对该共享模块进行任何更改吗?
    • @alper 那么你可能没有线程特定的日志记录。然后你有特定于模块的日志记录。查看LogRecord attributes 并查看您希望为每个处理程序接受哪些属性组合。然后相应地调整您的过滤器。
    • 我可以过滤日志,但我不能在飞行中将它们重定向到不同的文件中。 //我想我有module-specific日志,就像我刚刚做的那样:做完之后导入日志basicConfig 并在主线程及其生成的线程使用的模块上执行 logging.info(...)。此时,我不确定如何让共享模块决定它应该使用哪个日志过滤器(ThreadFilterIgnoreThreadsFilter)。我试着生成一个简单的例子:gist.github.com/avatar-lavventura/…
    • @alper:从我的回答中纠正了IgnoreThreadsFiltermy version of your gist 中的代码按设计工作。您的代码不起作用的主要原因是您将过滤器添加到记录器对象,而不是 handlers
    • @alper:处理程序不知道线程正在关闭,所以是的,您必须自己关闭并删除它们。不关闭它们的问题有多大取决于您的操作系统对进程设置的限制。如果您遇到too many open files IOError,那么您确实有太多打开的文件,应该更好地处理关闭或配置您的操作系统以提高限制。
    猜你喜欢
    • 1970-01-01
    • 2010-10-21
    • 1970-01-01
    • 2011-01-30
    • 2023-04-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多