You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何实现按用户和日期生成独立日志,解决多用户并发日志错位问题

问题根源

Python标准库logging模块默认采用单例模式,你调用logging.basicConfig修改的是全局共享的root logger配置,多线程并发场景下,后执行的log_setting调用会直接覆盖之前的配置,导致所有后续日志都写入最新配置的文件路径。单用户顺序访问时没有配置冲突所以功能正常,多用户并行访问时必然出现配置互窜。

解决方案

推荐两种可直接落地的实现方式:

方案1:ContextVar + 自定义Handler(业务代码无侵入,优先推荐)

利用Python 3.7+内置的contextvars实现请求上下文隔离,不需要修改原有业务代码的日志调用逻辑。
修改custom_logger.py代码如下:

import os
import datetime
import logging
from contextvars import ContextVar

# 定义上下文变量存储当前请求的用户、页面信息,天然支持线程/协程隔离
current_user = ContextVar('current_user', default=None)
current_page = ContextVar('current_page', default=None)

class UserFileHandler(logging.Handler):
    def emit(self, record):
        user = current_user.get()
        page = current_page.get()
        # 无用户信息的请求可以按需求走默认日志或直接丢弃
        if not user or not page:
            return
        # 动态构造日志路径
        log_path = f'./logs/{user}'
        os.makedirs(log_path, exist_ok=True)
        full_log_path = os.path.join(log_path, f"{datetime.date.today()}.log")
        # 构造日志格式
        formatter = logging.Formatter(
            f'%(asctime)s : %(levelname)s : {page} : %(pathname)s : %(lineno)s: %(funcName)s : %(message)s'
        )
        # 写入日志
        with open(full_log_path, 'a', encoding='utf-8') as f:
            f.write(formatter.format(record) + '\n')

class LogClass:
    def __init__(self):
        root_logger = logging.getLogger()
        root_logger.setLevel(logging.DEBUG)
        # 避免重复添加Handler
        if not any(isinstance(h, UserFileHandler) for h in root_logger.handlers):
            root_logger.addHandler(UserFileHandler())
    
    def log_setting(self, user_name, page_name):
        # 仅更新当前请求的上下文变量,不修改全局日志配置
        current_user.set(str(user_name))
        current_page.set(str(page_name))

原有中间件、业务代码的日志调用逻辑完全不需要修改,并发场景下不会出现日志串写问题。

方案2:独立Logger实例(逻辑简单,适合小项目)

给每个用户创建独立的Logger实例,配置完全隔离,不会互相干扰。
修改custom_logger.py代码如下:

import os
import datetime
import logging

class LogClass:
    def log_setting(self, user_name, page_name):
        # 用用户名作为Logger唯一标识,每个用户对应独立实例
        user_logger = logging.getLogger(f"user_{user_name}")
        # 避免重复添加Handler
        if user_logger.handlers:
            return user_logger
        # 构造日志路径
        log_path = f'./logs/{user_name}'
        os.makedirs(log_path, exist_ok=True)
        full_log_path = os.path.join(log_path, f"{datetime.date.today()}.log")
        # 配置独立Handler
        handler = logging.FileHandler(full_log_path, encoding='utf-8')
        formatter = logging.Formatter(
            f'%(asctime)s : %(levelname)s : {page_name} : %(pathname)s : %(lineno)s: %(funcName)s : %(message)s'
        )
        handler.setFormatter(formatter)
        user_logger.addHandler(handler)
        user_logger.setLevel(logging.DEBUG)
        # 禁止日志向上传递到root Logger,避免重复打印
        user_logger.propagate = False
        return user_logger

修改中间件代码,将用户专属Logger绑定到请求对象:

from .custom_logger import LogClass
log_obj=LogClass()
def simple_middleware(get_response):
    def middleware(request):
        user_token=request.headers['userid']     
        user_name=request.headers['username']
        page_name=request.headers['pagename']
        # 将用户专属Logger绑定到request
        request.user_logger = log_obj.log_setting(str(user_name),str(page_name))
        response = get_response(request)
        return response
    return middleware

业务代码中需要将原有全局logging.info(xxx)的调用,修改为request.user_logger.info(xxx)即可。

内容的提问来源于stack exchange,提问作者BetaTester

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.04 02:09:03