Django日志设置全局请求上下文?

23

假设我想以以下格式字符串记录日志:

%(levelname)s %(asctime)s %(module)s %(funcName)s %(message)s %(user_id)

可以使用以下类型的日志命令:

logging.error('Error fetching information', extra = { 'user_id': 22 } )

这将在当前请求的日志消息中添加当前用户ID。

但是,每个日志记录调用都需要添加额外的字典。

是否有一种好的方法可以在Django中的通用函数(例如中间件或视图的索引函数)中添加此上下文,以便设置带有用户ID的额外字典,并且当前请求中的所有后续日志记录调用也记录当前用户?


1
为什么mawimawi的答案不可接受?是因为线程本地使用还是其他原因? - Vinay Sajip
Vinay Sajip,一些声望点将独自消失。这就是在stackoverflow上的工作方式。 - mawimawi
@VinaySajip,是的,我正在尝试避免使用线程本地和额外的中间件。如果可以通过扩展日志记录类来实现,那就太好了。 - DhruvPathak
我更新了我的答案,建议一种避免使用线程本地变量或中间件的方法。 - Vinay Sajip
DhruvPathak:为什么你想避免使用中间件?我不明白原因。 - mawimawi
显示剩余3条评论
3个回答

8

https://github.com/jedie/django-tools/blob/master/django_tools/middlewares/ThreadLocal.py 上有一个名为 ThreadLocal 的中间件,可以帮助您解决在任何地方使当前请求可用的问题。

因此,您需要将该中间件添加到您的 MIDDLEWARE_CLASSES 设置中,并在某个位置创建以下函数:

 from django_tools.middlewares import ThreadLocal
 def log_something(levelname, module, funcname, message):
     user = ThreadLocal.get_current_user()
     # do your logging here. "user" is the user object and the user id is in user.pk

2

以下是一种不需要线程本地变量或中间件的可能方法:在您的views.py中,设置一个将线程映射到请求的字典,并设置锁以序列化对其的访问:

from threading import RLock
shared_data_lock = RLock()
request_map = {}

def set_request(request):
    with shared_data_lock:
        request_map[threading.current_thread()] = request

创建以下过滤器并将其附加到需要输出请求特定信息的处理程序:
import logging
class RequestFilter(logging.Filter):
    def filter(self, record):
        with shared_data_lock:
            request = request_map.get(threading.current_thread())
        if request:
            # Set data from the request into the record, e.g.
            record.user_id = request.user.id
        return True

然后,作为每个视图的第一条语句,在地图中设置请求:

def my_view(request, ...):
    set_request(request)
    # do your other view stuff here

通过这个设置,您应该能找到有关请求的相关信息在日志输出中。


1

我认为您正在寻找一个自定义日志格式化程序。结合mawimawi的答案来包含线程本地信息,您的格式方法可以自动获取该信息并将其添加到每个记录的消息中。

有几件事情需要考虑:首先,线程本地变量可能是危险的,更糟的是,如果您以使用线程池的方式部署,则会泄漏信息。其次,您需要谨慎编写格式化程序,以防在没有线程本地信息的上下文中调用它。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接