首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何在python应用程序的不同线程中对同一个记录器使用不同的日志处理程序?

如何在python应用程序的不同线程中对同一个记录器使用不同的日志处理程序?
EN

Stack Overflow用户
提问于 2021-07-04 01:30:00
回答 1查看 185关注 0票数 0

我正在使用第三方库(在本例中是pynetdicom)。从库中的模块生成日志,如下所示:

代码语言:javascript
复制
LOGGER = logging.getLogger('pynetdicom')
LOGGER.error("Some error message")

库还为pynetdicom记录器设置了一个空处理程序,因此默认情况下不会生成日志记录:

代码语言:javascript
复制
logging.getLogger('pynetdicom').addHandler(logging.NullHandler())

如果我希望库中的模块生成一些日志记录,我必须用其他方便的处理程序覆盖这个空处理程序。到目前为止,对我来说一切都很好。

在我的应用程序中,有三个不同的线程,它们都使用来自pynetdicom库的模块。我希望每个线程将pynetdicom事件记录到不同的文件中(例如,thread1将pynetdicom事件记录到线程1.log,thread2将pynetdicom事件记录到线程2.log,等等)。我怎样才能做到这一点?

我尝试在每个线程中将不同的处理程序附加到pynetdicom记录器上,但它当然没有工作,因为它们都在修改同一个记录器实例,从而覆盖处理程序。

编辑

我还尝试将不同的处理程序附加到pynetdicom记录器,并向每个记录器添加不同的筛选器,后者只接受从所需线程发出的日志。这也不起作用,因为pynetdicom创建了自己的内部线程,作为从我的线程调用的函数和方法的一部分。

我还使用inspect.stack()来捕获pynetdicom中每个对LOGGER.error('...')的调用的整个上下文,并试图在我的应用程序中找到生成调用的原始函数/模块。这也不起作用,因为有时堆栈会在创建pynetdicom内部线程时完成,并且找不到我的应用程序模块的跟踪。

编辑

为了进一步澄清这种情况,下面是一个例子:

代码语言:javascript
复制
import logging, threading

# Config pynetdicom to log to a file.
logger = logging.getLogger('pynetdicom')
logger.setLevel(logging.DEBUG)
logger.handlers = []
handler = RotatingFileHandler('dicom_events.log'))
handler.setLevel(logging.DEBUG)
formatter = logging.Formatter('%(asctime)s: %(levelname).1s: %(threadName)s: %(message)s ')    
handler.setFormatter(formatter)
logger.addHandler(handler)

# Functions for each thread in my application
def ServiceClassUser():
    # Some modules from pynetdicom are used from here. They write log messages in 'dicom_events.log'
    
def ServiceClassProvider():
    # Some modules pynetdicom are used from here. They write log messages in 'dicom_events.log'

# Initialize threads
scu_thread = threading.Thread(target = ServiceClassUser, name = 'StoreSCU')
scu_thread.start()

scp_thread = threading.Thread(target = ServiceClassProvider, name = 'StoreSCP')
scp_thread.start()

dicom_events.log看起来是这样的:

代码语言:javascript
复制
2021-07-12 19:20:05,830: D: Thread-2: Priority                      : Medium 
2021-07-12 19:20:05,830: D: Thread-2: ============================ END DIMSE MESSAGE ============================= 
2021-07-12 19:20:05,830: D: AcceptorThread@20210712191907: pydicom.read_dataset() TransferSyntax="Little Endian Explicit" 
2021-07-12 19:20:05,888: D: Thread-2: pydicom.read_dataset() TransferSyntax="Little Endian Implicit" 
2021-07-12 19:20:07,266: I: Thread-2: Received Store Request 
2021-07-12 19:20:07,266: D: Thread-2: ========================== INCOMING DIMSE MESSAGE ========================== 
2021-07-12 19:20:07,266: D: Thread-2: Message Type                  : C-STORE RQ 
2021-07-12 19:20:07,266: D: Thread-2: Presentation Context ID       : 21 
2021-07-12 19:20:07,266: D: Thread-2: Message ID                    : 1019 
2021-07-12 19:20:07,266: D: Thread-2: Affected SOP Class UID        : 1.2.840.113619.4.30 
2021-07-12 19:20:07,266: D: Thread-2: Affected SOP Instance UID     : 1.2.840.113619.2.131.1460334732.1626124764.739758 
2021-07-12 19:20:07,266: D: Thread-2: Data Set                      : Present 
2021-07-12 19:20:07,267: D: Thread-2: Priority                      : Medium 
2021-07-12 19:20:07,267: D: Thread-2: ============================ END DIMSE MESSAGE ============================= 
2021-07-12 19:20:07,267: D: AcceptorThread@20210712191907: pydicom.read_dataset() TransferSyntax="Little Endian Explicit" 
2021-07-12 19:20:07,291: I: AcceptorThread@20210712191907: Association Released 
2021-07-12 19:20:09,372: I: StoreSCU: Requesting Association
2021-07-12 19:20:09,375: D: Thread-6: Request Parameters: 
2021-07-12 19:20:09,375: D: Thread-6: ======================= OUTGOING A-ASSOCIATE-RQ PDU ======================== 
2021-07-12 19:20:09,375: D: Thread-6: Our Implementation Class UID:      1.2.826.0.1.3680043.9.3811.1.5.7  

线程2、线程6和AcceptorThread是由pynetdicom创建的.我无法控制它们,也无法知道最初创建它们的pynetdicom模块的调用。因此,在原始线程上使用过滤器没有帮助。不要使用格式化程序在日志文件中包含线程名或id。

EN

回答 1

Stack Overflow用户

发布于 2021-07-08 08:46:43

我建议您的每个线程将一个FileHandler添加到pynetdicom记录器,然后向每个处理程序添加一个过滤器,以只接受该线程发出的日志。

以下是一个例子:

代码语言:javascript
复制
import logging
import threading

logger = logging.getLogger("pynetdicom")
logger.setLevel(logging.DEBUG)


class FilterByThread(logging.Filter):
    def __init__(self, allowed_thread_id: int):
        super().__init__()
        self.allowed_thread_id = allowed_thread_id

    def filter(self, record: logging.LogRecord) -> bool:
        return record.thread == self.allowed_thread_id


def setup_thread_logging():
    current_thread_name = threading.current_thread().name
    logfile_name = current_thread_name + ".log"
    handler = logging.FileHandler(logfile_name)
    current_thread_id = threading.current_thread().ident
    log_filter = FilterByThread(current_thread_id)
    handler.addFilter(log_filter)
    logger.addHandler(handler)


def do_main_stuff():
    setup_thread_logging()
    logger.info("hello from Main Thread")


def do_stuff2():
    setup_thread_logging()
    logger.info("hello from thread 2")


def do_stuff3():
    setup_thread_logging()
    logger.info("hello from thread 3")


main_thread = threading.current_thread()
thread2 = threading.Thread(target=do_stuff2, name="thread2")
thread3 = threading.Thread(target=do_stuff3, name="thread3")

thread2.start()
thread3.start()
do_main_stuff()

thread2.join()
thread3.join()

它产生:

  • MainThread.log:来自主线程的你好
  • thread2.log:你好,来自线程2
  • thread3.log:你好,来自线程3

也许有更聪明或更有效的解决方案,但这个方法很容易理解。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/68241038

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档