Python记录器在多线程环境中多次打印相同的输出

时间:2013-04-19 22:43:05

标签: python multithreading logging io

在我的代码中,记录器以某种方式打印出事件两次,但传统的print显示正确的事件数。

select 1,2
select 1,2
2013-04-19 18:37:30,618:4561354752 - SQLLogger - DEBUG - select 1,2
2013-04-19 18:37:30,618:4561354752 - SQLLogger - DEBUG - select 1,2
2013-04-19 18:37:30,618:4565561344 - SQLLogger - DEBUG - select 1,2
2013-04-19 18:37:30,618:4565561344 - SQLLogger - DEBUG - select 1,2

我不清楚为什么在多线程情况下多次发布相同的消息。

import logging
from threading import Thread

class SQLEngine(object):
    def __init__(self, DB_PATH):
        ch = logging.StreamHandler()
        ch.setLevel(logging.DEBUG)
        formatter = logging.Formatter('%(asctime)s:%(thread)d - %(name)s - %(levelname)s - %(message)s')
        ch.setFormatter(formatter)
        self.logger = logging.getLogger('SQLLogger')
        self.logger.setLevel(logging.DEBUG)
        self.logger.addHandler(ch)

    def execute(self,SQL,):
            self.logger.debug(SQL)
            print SQL

class DBWorker(Thread):
    def __init__(self, name):
        Thread.__init__(self)
        self.name = name

    def run(self):
        db = SQLEngine('')
        db.execute('select 1,2')

if __name__ == '__main__':
    DBWorker('thread 1').start()
    DBWorker('thread 2').start()

2 个答案:

答案 0 :(得分:2)

查看logger文档:

  

记录器永远不会直接实例化,但始终通过模块级函数logging.getLogger(name)实例化。对具有相同名称的getLogger()的多次调用将始终返回对同一Logger对象的引用。

现在你的构造函数正在调用以下代码:

ch = logging.StreamHandler()
self.logger = logging.getLogger('SQLLogger')
self.logger.addHandler(ch)

请注意,不要为每个SQLEngine对象创建新的记录器,但始终会获得对同一记录器实例的引用。这意味着您总是将处理程序添加到同一个记录器中,因此在创建第二个对象后,您的记录器有两个处理程序,每个处理程序都会打印到屏幕上。

我们只需要注册一个处理程序(例如在SQLEngine构造函数之外)或在getLogger中调用__init__,每个{{1}使用不同的名称实例。

答案 1 :(得分:0)

您已经启动了两个线程,每个线程都有自己的SQLEngine属性对象,在运行相应的线程时会执行该对象。如果您想在多个线程之间共享记录器,那么创建一个可以传递的单个记录器对象将是更好的选择。 GIL将负责其余的工作。

import logging
from threading import Thread

class SQLEngine(object):
    def __init__(self, DB_PATH):
        ch = logging.StreamHandler()
        ch.setLevel(logging.DEBUG)
        formatter = logging.Formatter('%(asctime)s:%(thread)d - %(name)s - %(levelname)s - %(message)s')
        ch.setFormatter(formatter)
        self.logger = logging.getLogger('SQLLogger')
        self.logger.setLevel(logging.DEBUG)
        self.logger.addHandler(ch) 

    def execute(self,SQL,): 
        self.logger.debug(SQL)
        print (SQL)

class DBWorker(Thread):
    def __init__(self, name, logengine):
        Thread.__init__(self)
        self.name = name
        self.logger = logengine

    def run(self):
        #db = SQLEngine('')
        self.logger.execute('select 1,2')

if __name__ == '__main__':
    logger = SQLEngine('')
    DBWorker('thread 1', logger).start()
    DBWorker('thread 2', logger).start()

这只是一个例子。正如@tomasz所提到的,您可以根据您的要求处理此记录器对象