Как я могу добавить текущее имя пользователя в журнал доступа в django.server?
Я пытаюсь добавить текущее имя пользователя, если оно есть, в журнал доступа приложения Django:
INFO [django.server:161] "GET / HTTP/1.1" 200 116181
^ username should go here
Моя основная проблема заключается в том, как мне разделить пользователя/имя пользователя между промежуточным ПО и фильтром, поскольку в фильтре у меня нет доступа к объекту request
?
У меня есть рабочее решение, использующее thread-local для хранения, но это не похоже на а хорошую идею
Тем более, что я не могу очистить значение в process_request
, поскольку оно очищается слишком рано, до того, как будет напечатана строка журнала.
Решение с threading.local()
log.py
import logging
import threading
local = threading.local()
class LoggedInUsernameFilter(logging.Filter):
def filter(self, record):
user = getattr(local, 'user', None)
if user and user.username:
record.username = user.username
else:
record.username = '-'
return True
class LoggedInUserMiddleware(object):
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
self.process_request(request)
return self.get_response(request)
def process_request(self, request):
from django.contrib.auth import get_user
user = get_user(request)
setattr(local, 'user', user)
настройки джанго
MIDDLEWARE = (
...
'commons.log.LoggedInUserMiddleware',
...
)
...
LOGGING = {
'version': 1,
'disable_existing_loggers': True,
'formatters': {
...
'verbose_with_username': {
'format': '[%(asctime)s] %(levelname)s [%(name)s:%(lineno)s] %(username)s %(message)s'
}
},
'filters': {
'logged_in_username': {
'()': 'commons.log.LoggedInUsernameFilter',
}
},
'handlers': {
'console_with_username': {
'level': 'DEBUG',
'class': 'logging.StreamHandler',
'formatter': 'verbose_with_username',
'stream': sys.stdout,
},
},
'loggers': {
...
'django.server': {
'handlers': ['console_with_username'],
'level': 'INFO',
'propagate': False,
'formatter': 'verbose_with_username',
'filters': ['logged_in_username', ],
},
...
},
}
У меня есть рабочее решение, использующее thread-local для хранения данных, но это не кажется похожим на хорошую идею.
В некоторых ответах говорится, что это "чрезвычайно полезный" или "отличный метод", с оговоркой "асинхронный"
Даже авторитетное руководство по Python https://docs.python.org/3/howto/logging-cookbook.html#using-filters-to-impart-contextual-information предлагает потоково-локальное хранение:
Например, в веб-приложении обрабатываемый запрос (или, по крайней мере, интересные его части) может храниться в потоковой переменной (
threading.local
), а затем к нему можно обратиться изFilter
, чтобы добавить, скажем, информацию из запроса - скажем, удаленный IP-адрес и имя пользователя удаленного пользователя - вLogRecord
...
Также предлагается Использование LoggerAdapters и Использование contextvars, которые не подходят для django.server
ведения журнала, который находится вне вашего контроля.
Поэтому asgiref.local.Local
является хорошей заменой для оговорки "async".
# import threading
# local = threading.local()
from asgiref.local import Local
local = Local()