gpt4 book ai didi

python - 在 python 中从多个线程实现自定义输出日志记录

转载 作者:行者123 更新时间:2023-11-28 22:32:26 25 4
gpt4 key购买 nike

我在 Python 多线程方面的实践比较差。所以,现在,我正在研究如何从多个线程获取日志信息。我看到了很多不同的方法,但我想从简单的方法开始。因此,任务是创建多个线程并从每个线程中记录数据。为了识别日志的来源,我想在日志输出中放置一些自定义标签。我知道日志记录库有一个 reach 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

其实,这不是我所期待的。你能告诉我我做错了什么吗?

最佳答案

您的部分问题来自您传递的对象变量。当您传递 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。

我们要确保每次都创建一个新的信息对象,这非常简单,我们可以直接在函数调用中传递它 ({'project': project}) 而无需分配一个变量。

我们需要确保在从主线程登录时传递我们的 project 变量。使用另一个 LoggerAdapter 可能会更好。

一旦我们打破了循环,我们就可以要求每个线程停止,然后等待每个线程(join() 可能会移到 stop 方法中 worker 类)

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 Queues用于线程之间的通信,引导代码更易于调试。

关于python - 在 python 中从多个线程实现自定义输出日志记录,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40909762/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com