我在Python多线程方面的实践相当差。那么,现在,我正在研究如何从多个线程获取日志信息。我看到了很多不同的方法,但我想从简单的方法开始。因此,任务是创建多个线程并从每个线程中记录数据。为了识别日志源,我想在日志输出中添加一些自定义标记。我知道log lib有一个到达LogRecord属性(thread,threadName等),它运行良好。所以,我有一些例子(logging-from-multiple-threads)并做了一些修改。这是一个完整的代码:
import logging
import threading
import time
logger = logging.getLogger()
syslog = logging.StreamHandler()
formatter = logging.Formatter('%(project)s : %(thread)x '
'%(levelname)-8s '
'%(message)s')
syslog.setFormatter(formatter)
logger.setLevel(logging.DEBUG)
logger.addHandler(syslog)
class ContextFilter(logging.Filter):
def __init__(self, project):
super(ContextFilter, self).__init__()
self.project = project
def filter(self, record):
record.project = self.project
return True
def worker(args):
while not args['stop']:
logging.debug('Hi from {}'.format(args['project']))
time.sleep(0.5)
def main():
projects = ['project_1', 'project_2']
info = {'stop': False}
threads = []
for project in projects:
info['project'] = project
logger.addFilter(ContextFilter(project))
thread = threading.Thread(target=worker, args=(info,))
thread.start()
threads.append(thread)
while True:
try:
logging.debug('Hello from main')
time.sleep(1.75)
except KeyboardInterrupt:
info['stop'] = True
break
for t in threads:
t.join()
if __name__ == '__main__':
main()
以下是输出结果:
project_2 : 7fa627e77700 DEBUG Hi from project_2
project_2 : 7fa6293d0700 DEBUG Hello from main
project_2 : 7fa627676700 DEBUG Hi from project_2
project_2 : 7fa627e77700 DEBUG Hi from project_2
project_2 : 7fa627676700 DEBUG Hi from project_2
project_2 : 7fa627e77700 DEBUG Hi from project_2
project_2 : 7fa627676700 DEBUG Hi from project_2
project_2 : 7fa627e77700 DEBUG Hi from project_2
project_2 : 7fa627676700 DEBUG Hi from project_2
project_2 : 7fa6293d0700 DEBUG Hello from main
project_2 : 7fa627e77700 DEBUG Hi from project_2
实际上,这不是我所期待的。你能告诉我一些我做错的事吗?
答案 0 :(得分:2)
你到底想要什么?如果你想知道为什么"嗨来自"没有显示项目名称,请尝试:
logging.debug('Hi from {}'.format(args['project']))
编辑:回答你的评论,实际上你确实从两个线程中获取了日志。
但是您的info
对象是共享的。当您传递args=(info,))
时,实际上您传递了对info
对象的引用,而不是副本。
因此,第一次for
循环执行时,您获得info['project'] = "project_1"
,但是第二次info['project']
被#34; project_2"覆盖。
您的工作线程只读取来自同一info
字典...
答案 1 :(得分:2)
您的部分问题来自您传递的对象变量。当您传递args=(info,)
时,您正在传递reference to an object(您稍后修改并传递给下一个对象),而不是该对象的副本。将同一个对象传递给多个线程可能会变得很危险,因此可以race conditions
首先,我们可以删除ContextFilter。我们将它们添加到全局记录器中,而不是跟踪每个线程的任何内容。
import logging
import threading
import time
logger = logging.getLogger()
syslog = logging.StreamHandler()
formatter = logging.Formatter('%(project)s : %(thread)x '
'%(levelname)-8s '
'%(message)s')
syslog.setFormatter(formatter)
logger.setLevel(logging.DEBUG)
logger.addHandler(syslog)
我发现通常构建threading.Thread
类对除了最简单的任务之外的所有类都更有用。
此类维护自己的running
状态,并使用正确的extra
数据构建自己的日志记录适配器。
class Worker(threading.Thread):
def __init__(self, info):
self.running=False
self.info=info
self.logger=logging.LoggerAdapter(logger, self.info)
super(Worker, self).__init__()
def start(self):
self.running=True
super(Worker, self).start()
def stop(self):
self.running=False
def run(self):
while self.running:
self.logger.debug('Hi from {}'.format(self.info['project']))
time.sleep(0.5)
现在我们需要改变一些事情。我们需要使用自己的Worker
类。
我们不需要对记录器做任何事情,该类将管理自己的LoggerAdapter。
我们希望确保每次都创建一个新的info对象,这很简单,我们可以直接在函数调用({'project': project}
)中直接传递它而无需赋值变量。
我们需要确保在从主线程登录时传递project
变量。使用另一个LoggerAdapter可能更好。
一旦我们打破循环,我们就可以让每个线程停止,然后等待每个线程(join()
也许可以移动到stop
类的Worker
方法中< / p>
def main():
projects = ['project_1', 'project_2']
threads = []
for project in projects:
thread = Worker({'project': project})
thread.start()
threads.append(thread)
while True:
try:
logging.debug('Hello from main', extra={'project':'main'})
time.sleep(1.75)
except KeyboardInterrupt:
break
for t in threads:
t.stop()
for t in threads:
t.join()
if __name__ == '__main__':
main()
此代码会产生
等结果project_1 : 7f4b44180700 DEBUG Hi from project_1
project_2 : 7f4b4397f700 DEBUG Hi from project_2
main : 7f4b45c8d700 DEBUG Hello from main
project_1 : 7f4b44180700 DEBUG Hi from project_1
project_2 : 7f4b4397f700 DEBUG Hi from project_2
project_1 : 7f4b44180700 DEBUG Hi from project_1
project_2 : 7f4b4397f700 DEBUG Hi from project_2
project_1 : 7f4b44180700 DEBUG Hi from project_1
project_2 : 7f4b4397f700 DEBUG Hi from project_2
main : 7f4b45c8d700 DEBUG Hello from main
project_1 : 7f4b44180700 DEBUG Hi from project_1
有很多方法可以整理代码并使其更具可读性,但这至少应该为您提供一些学习起点和开始实验的起点。当您了解有关线程的更多信息时,您还应该阅读有关thread synchronization机制的内容。我最近开始using Queue
s进行线程之间的通信,引导代码更容易调试。