我的用例如下:我有一个处理请求的服务器,对于每个请求,我希望日志记录包含一个
user_id
。我希望团队中的其他开发人员能够尽可能无缝地使用它,这样他们就可以简单地导入 logging
并使用它,而无需传递 user_id 。
这是一个 MWE,但是,正如您所看到的,它并不总是有效:
import logging
import threading
import time
class ContextFilter(logging.Filter):
def __init__(self, user_id: str):
super().__init__()
self.local = threading.local()
self.local.user_id = user_id
def filter(self, record):
record.user_id = getattr(self.local, 'user_id', 'NoValue')
return True # Returning True ensures the log message is processed
# Set up logging
logger = logging.getLogger("my_logger")
logger.setLevel(logging.DEBUG)
# Add a handler
formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(user_id)s - %(message)s')
handler = logging.StreamHandler()
handler.setFormatter(formatter)
logger.addHandler(handler)
# logger.info("This is a test message.")
def worker(user_id: str):
logger.addFilter(ContextFilter(user_id=user_id))
logger.info("message 1")
time.sleep(0.5)
logger.info("message 2")
t1 = threading.Thread(target=worker, args=("user1",))
t2 = threading.Thread(target=worker, args=("user2",))
t1.start()
t2.start()
t1.join()
t2.join()
这是输出:
2024-11-20 17:46:39,780 - INFO - user1 - message 1
2024-11-20 17:46:39,780 - INFO - user2 - message 1
2024-11-20 17:46:40,284 - INFO - NoValue - message 2
2024-11-20 17:46:40,285 - INFO - user2 - message 2
我在这里缺少什么?
两个线程都将过滤器安装到同一个记录器,并且两个过滤器都被调用,最后安装的过滤器将显示其值。
只需一个过滤器,并让每个线程修改该 id 的线程本地版本。 (虽然所有线程都读取和写入相同的
self.local
,但每个线程在读取或写入时会看到不同的属性,这就是线程本地的含义)
import logging
import threading
import time
class ContextFilter(logging.Filter):
def __init__(self):
super().__init__()
self.local = threading.local()
def set_id(self, user_id):
self.local.user_id = user_id
def filter(self, record):
record.user_id = getattr(self.local, 'user_id', 'NoValue')
return True # Returning True ensures the log message is processed
# Set up logging
logger = logging.getLogger("my_logger")
logger.setLevel(logging.DEBUG)
# Add a handler
formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(user_id)s - %(message)s')
handler = logging.StreamHandler()
handler.setFormatter(formatter)
logger.addHandler(handler)
global_filter = ContextFilter()
logger.addFilter(global_filter)
def worker(user_id: str):
global_filter.set_id(user_id)
logger.info("message 1")
time.sleep(0.5)
logger.info("message 2")
t1 = threading.Thread(target=worker, args=("user1",))
t2 = threading.Thread(target=worker, args=("user2",))
t1.start()
t2.start()
t1.join()
t2.join()