gpt4 book ai didi

python - Django session : How to include session ID in every log record?

转载 作者:行者123 更新时间:2023-12-03 17:29:25 25 4
gpt4 key购买 nike

我想在 Django 的每个日志条目中包含 session ID(至少是其中的一部分)。这将帮助我将每个用户 session 的日志组合在一起,以防多个用户同时与我的 Django API 交互。

我知道我可以从 Request 对象(当它存在时)检索 session ID,但我必须为整个框架中的每个 logger.info() 这样做。相反,在 settings.py 中以某种方式提供 Request 会更好,这样我就可以在 LOGGING 配置中将 ID 添加到格式化程序的格式字符串中。

这将确保 session ID 包含在每个日志条目中,我不需要在每个 View 或序列化程序等中考虑它。

但是我还没有找到如何在 settings.py 中使请求或 session 可用的方法。

编辑:

进一步解释。我有以下信号来捕获用户登录并在它发生时记录此事件。我想在此消息中添加 session ID。但我希望 Django 默认为系统中的任何消息执行此操作 - 我不想在 log_user_login 函数和我的代码中的任何其他函数中获取 session ID。

所以在下面的代码中,我想启动记录器事件,但没有指定 session ID。

import logging
from django.dispatch import receiver
from django.contrib.auth.signals import user_logged_in

logger = logging.getLogger(__name__)

@receiver(user_logged_in)
def log_user_login(sender, user, request, **kwargs):
logger.info(f"User {user.username} logged in.")

我希望 Django 通过 settings.py 中的配置自动添加 session ID,以使用以下代码进行说明:
def add_session_id(record):
record.attrs = record.__dict__.keys()
return True


LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'filters': {
'add_session_id': {
'()': 'django.utils.log.CallbackFilter',
'callback': add_session_id,
}
},
'formatters': {
'file': {
'format': '%(attrs)s',
},
},
}

这将记录以下消息:
dict_keys(['name', 'msg', 'args', 'levelname', 'levelno', 'pathname', 'filename', 'module', 'exc_info', 'exc_text', 'stack_info', 'lineno', 'funcName', 'created', 'msecs', 'relativeCreated', 'thread', 'threadName', 'processName', 'process', 'attrs', 'message', 'asctime'])

所以在我看来,提到的记录既不包含请求也不包含 session 。

最佳答案

这是我的最终解决方案,非常适合我。
我编写了一个中间件 thread_local.py,它从每个请求中提取用户名和 session ID(如果可用)并将这些值存储为线程的局部变量。
它允许我从项目的其他部分访问用户名和 session ID,包括日志记录。
thread_local.py:

import logging
from threading import local

_thread_locals = local()

logger = logging.getLogger(__name__)


def get_local_thread_value(item):
"""
Returns value of an item of this current thread.
"""
return getattr(_thread_locals, item, None)


def list_thread_locals(attrs):
"""
Returns a line of key:val pairs of current thread locals.
"""
return str("Current thread locals: " + ', '.join(
[
f"{attr}: {str(get_local_thread_value(attr))}"
for attr in attrs
]))


class ThreadLocalMiddleware():
"""
This middleware, for every request separately, reads important values
from Request or Session objects and adds them to local thread attributes.
This makes the values available throughout the project - in views, etc.
"""
def __init__(self, get_response=None):
"""
One-time configuration and initialization.
Called only once at server startup.
"""
self.get_response = get_response
self.attrs_to_remove = ['c_user', 'c_session_id', ]

def _set_user(self, request):
"""Get username from request data and add it to thread."""
c_user = getattr(request, 'user', None)
logger.debug(f"_set_user to _thread_locals: {c_user}")
setattr(_thread_locals, 'c_user', c_user)

def _set_session_id(self, request):
"""Get session ID from session store and add it to thread."""
c_session = getattr(request, 'session', {})
c_session_key = getattr(c_session, 'session_key', None)
c_session_id = c_session_key[-10:] if c_session_key else None
logger.debug(f"_set_session_id to _thread_locals: {c_session_id}")
setattr(_thread_locals, 'c_session_id', c_session_id)

def _unset_local_attr(self):
"""Remove attributes provided in attr_list from local thread."""
logger.debug(f"_unset_local_attr")
for attr in self.attrs_to_remove:
if hasattr(_thread_locals, attr):
delattr(_thread_locals, attr)

def __call__(self, request):
"""
Called for every request.
Fetch and store User information into local thread,
which is then available for logging filter.
"""
# Code to be executed for each request before
# the view (and later middlewares) are called.

self._set_user(request)
self._set_session_id(request)
logger.debug(list_thread_locals(self.attrs_to_remove) + " (request)")

# Forward Request further the satck and wait for Response
response = self.get_response(request)

# Code to be executed for each request/response after
# the view is called.

# Unset all local thread attributes
# to ensure they are not kept in next request/response cycle.
self._unset_local_attr()
logger.debug(list_thread_locals(self.attrs_to_remove) + " (response)")

# Let other middlewares execute their response part.
return response

def process_exception(self, request, exception):
"""
In case of exception, delete the local thread attributes,
and return None so that other middlewares can act too.
"""
logger.debug('process_exception: exception in thread_local')
self._unset_local_attr()
那么当然不要忘记在settings.py中注册它。
日志:
对于常规日志记录,我编写了一个自定义日志记录过滤器:
from core.middleware.thread_local import get_local_thread_value

class CustomLoggingFilter(logging.Filter):
"""
If logging handler is configured with this filter,
a log record is updated with User information.
These attributes can be accessed by any logging formatter,
as for example: %(c_session_id).

The filter() method must return True, to ensure the record is logged.
"""
def filter(self, record):
record.c_user = get_local_thread_value('c_user')
record.c_session_id = get_local_thread_value('c_session_id')
return True
然后 setting.py 中的日志记录部分看起来像这样(为了更详细,删除了一些部分):
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'filters': {
'custom_logging_filter': {
'()': 'core.utils.log.CustomLoggingFilter',
},
},
'formatters': {
'file': {
'format': ('%(asctime)s %(c_session_id)-10s '
'%(levelname)-8s %(c_user)s '
'%(message)s (%(name)s)'),
},
},
'handlers': {
'base_file': {
'level': 'INFO',
'filters': ['custom_logging_filter'],
'class': 'logging.FileHandler',
'formatter': 'file',
'filename': LOG_FILE_BASE,
},
},
'loggers': {
'': {
'handlers': ['base_file', ],
'level': 'DEBUG',
'propagate': True,
},
}
}
可以对 Django 的信号使用相同的方法直接在数据库中记录这些审计事件,例如,当 user_logged_in 信号触发自定义接收器时,它调用一个函数来记录审计模型中的新对象。

关于python - Django session : How to include session ID in every log record?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57372621/

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