Django logging задает контекст глобально за запрос?
Предположим, что я хочу записать эту строку форматирования:
%(levelname)s %(asctime)s %(module)s %(funcName)s %(message)s %(user_id)
Это можно сделать с помощью этого типа ведения журнала:
logging.error('Error fetching information', extra = { 'user_id': 22 } )
Это добавит текущий идентификатор пользователя для регистрации сообщений для текущего запроса.
Но дополнительный дидж должен быть добавлен к каждому вызову журнала.
Есть ли хороший способ добавить этот контекст в общую функцию в django (например, Middleware или функцию индекса представления), так что добавлен дополнительный словарь с идентификатором пользователя и все дальнейшие вызовы журналов в текущем запросе также зарегистрируйте текущего пользователя.
Ответы
Ответ 1
Существует промежуточное ПО ThreadLocal на https://github.com/jedie/django-tools/blob/master/django_tools/middlewares/ThreadLocal.py, которое поможет вам решить вашу проблему, чтобы сделать текущий запрос доступным во всем мире.
Итак, что вам нужно сделать, это добавить промежуточное ПО в настройку 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
С помощью этой настройки вы должны обнаружить, что в вашем журнале содержится соответствующая информация о запросе.
Ответ 3
Я думаю, что вы ищете настраиваемый формат журнала. В сочетании с ответом mawimawi, чтобы включить информацию о потоке, ваш метод форматирования может автоматически принимать информацию и добавлять ее к каждому зарегистрированному сообщению.
Есть несколько вещей, о которых нужно подумать: во-первых, переменные threadlocal могут быть опасными и, что еще хуже, информацией об утечке, если вы развертываете таким образом, что использует threadpool. Во-вторых, вам нужно быть осторожным, как вы пишете свой форматировщик, если он вызывается в контексте, который не имеет информации о потоке.