Django 日志配置解析

在 Django 中设置和使用日志是一个有效的方式来监控和调试应用程序。日志可以帮助你理解应用的运行状态,记录错误信息,以及跟踪重要的系统事件。Django 使用 Python 的标准 logging 模块来配置和管理日志。

目录

配置日志

日志配置通常在 Django 的设置文件中(如 settings.py)进行。Django 允许你详细地自定义日志记录器、处理器、过滤器和格式化器。

简单的日志配置:

python 复制代码
LOGGING = {
    'version': 1,
    #日志配置的版本,当前只支持 1
    'disable_existing_loggers': False, 
    # 设置为 False 表示不禁用在配置加载前已经存在的日志记录器。如果设置为 True,除了那些在配置中明确声明的,所有现存的日志记录器将被禁用。
    # 处理器
    'handlers': {
        'console': {
            'level': 'ERROR',
            'class': 'logging.StreamHandler',
        },
        # 日志输出到控制台
    },
    # 记录器
    'loggers': {
        'django': {
            'handlers': ['console'],
            'level': 'ERROR',
            'propagate': True,
        },
    },
}

对于生产环境,需要将日志写入文件 ,并进行归档管理。这通常通过配置文件处理器来实现,如使用 logging.handlers.RotatingFileHandler 或 logging.handlers.TimedRotatingFileHandler 来按文件大小或时间自动分割日志文件。

正式版的settings.py的log 配置:

python 复制代码
LOG_BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
# __file__ 是一个特殊变量,它包含当前文件(通常是一个 Python 脚本)的路径。
# os.path.abspath(__file__):获取 __file__ 的绝对路径。这是为了确保路径是完整的,不依赖于当前工作目录
# os.path.dirname():获取路径中的目录部分。第一次调用得到的是文件所在的目录(例如,如果 __file__ 是 /home/user/project/app/settings.py,则返回 /home/user/project/app)

LOG_DIR = os.path.join(LOG_BASE_DIR, 'logs')
# 这行代码的结果是在项目根目录下创建一个名为 logs 的文件夹,用于存储所有日志文件。
os.makedirs(LOG_DIR, exist_ok=True)
#这个函数用于创建目录。如果目录的中间部分不存在,os.makedirs() 会创建它们。
# exist_ok=True 参数表示如果目录已经存在,不会抛出异常,而是正常处理。这对于重启应用不希望因为目录已存在而出错的场景非常有用

log_format = '%(asctime)s | Request ID: %(request_id)s | %(module)s | %(funcName)s | %(message)s '
# 样式输出:
#2024-06-28 09:52:35 | Request ID: 4241ec7f-ad58-4582-bf3b-282XXXXXX3d | log_middleware | __call__ | Request URL: /XXXXX

LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,、
    # 过滤器:自定义过滤器,用于在日志消息中添加一个请求 ID,使得在并发访问日志时可以轻松追踪到特定请求的日志。假定 RequestIDFilter 是一个已定义的过滤器类,它可能从请求元数据中提取或生成唯一的请求 ID
    'filters': {
        'request_id': {
            '()': RequestIDFilter,
        },
    },
    # 格式化器: 定义了日志的输出格式
    'formatters': {
        'default': {
            'format': log_format, # 调用上面的格式
            'datefmt': '%Y-%m-%d %H:%M:%S',
        },
    },
    # 处理器
    'handlers': {
        'info_file': {
            'level': 'INFO',
            'class': 'logging.handlers.TimedRotatingFileHandler',# 支持按时间自动分割日志文件。
            'filename': os.path.join(LOG_DIR, 'info.log'), # 指定日志文件的存储位置
            'when': 'midnight', # 指定日志文件分割的时间点,这里设置为 'midnight',表示每天午夜分割日志文件。
            'backupCount': 7, #日志文件的保留数量,这里设置为 7,表示保存最近 7 天的日志文件。
            'formatter': 'default', #  使用前面定义的 default 格式化器。
            'filters': ['request_id'],
        },
        'error_file': {
            'level': 'ERROR', # 处理来自 Django 请求模块的 ERROR 级别日志
            'class': 'logging.handlers.TimedRotatingFileHandler',
            'filename': os.path.join(LOG_DIR, 'error.log'),
            'when': 'midnight',
            'backupCount': 7,
            'formatter': 'default',
            'filters': ['request_id'],
        },
        'console': {
            'level': 'DEBUG',
            'class': 'logging.StreamHandler',
            'formatter': 'default',
            'filters': ['request_id'],
        },
    },
    # 记录器
    'loggers': {
    # 专门用于捕获 Django 框架产生的所有日志,从 INFO 级别开始记录。
        'django': {
            'handlers': ['info_file', 'error_file', 'console'],
            'level': 'INFO',
            'propagate': True,
        },
        # 捕获所有未被其他记录器处理的日志,同样从 INFO 级别开始。
        '': {
            'handlers': ['info_file', 'error_file', 'console'],
            'level': 'INFO',
            'propagate': True,
        },
        'django.request': {
            'handlers': ['error_file', 'console'],
            'level': 'ERROR',
            'propagate': False, #  设置为 False,阻止日志向更高级别的记录器传播,这在此记录器中很有用,以避免错误信息被重复记录
        },
    },
}

写日志

可以使用 logging 模块来记录日志。首先需要导入 logging 库,然后创建一个日志记录器实例:

python 复制代码
import logging

logger = logging.getLogger(__name__)

def my_view(request):
    try:
        # 你的业务逻辑
        pass
    except Exception as e:
        logger.error('Something went wrong: %s', e)

Django 和 Python 的日志系统支持多种日志级别,你可以根据需要记录不同级别的信息:

DEBUG: 详细信息,通常只在诊断问题时有用。

INFO: 确认事情按预期运行。

WARNING: 表明有一些意外,或在不久的将来可能出现问题(例如"磁盘空间低")。软件还能按预期工作。

ERROR: 由于更严重的问题,软件已不能执行某些功能。

CRITICAL: 严重错误,表明程序本身可能无法继续运行。

日志中间件

日志中间件可以在每次请求处理过程中提供更精细的日志记录。例如,它可以详细记录每个请求的 URL、请求体、响应体等,这些是通过标准 Django 日志设置不易实现的。

python 复制代码
class LogMiddleware:
    def __init__(self, get_response):
        self.get_response = get_response # 这是下一个中间件或最终的视图函数。在 Django 中间件是一个请求处理的链条,每个中间件都需要调用下一个中间件来继续处理请求
        self.logger = logging.getLogger(__name__) # 使用 Python 的 logging 库来创建一个日志记录器,这个记录器使用当前模块的名字(__name__)作为标识。这样,日志的输出可以根据模块来筛选和控制

    def __call__(self, request):
        self.logger.info('Request URL: %s', request.path_info)
        self.logger.info('Request body: %s', request.body[:1024])

        response = self.get_response(request) # 调用链中下一个中间件或视图函数,并获取响应对象
        if response['Content-Type'] == 'application/json':
            content = json.loads(response.content)
            self.logger.info('Response body: %s', json.dumps(content, indent=4))
        return response

Django 中的 MIDDLEWARE 设置是一个中间件的列表,定义了每个请求在处理过程中经过的中间件。这个列表中的每个中间件都需要实现 call 方法,并能接受一个请求对象并返回一个响应对象。中间件在列表中的顺序很重要,因为它决定了请求和响应的处理顺序。

python 复制代码
# 使用 Python 的 contextvars 模块定义了一个上下文变量,该变量将在请求的处理过程中保持请求 ID。contextvars 提供了一种在异步应用中保持变量上下文的方法,这对于 Django 异步视图或在异步环境下工作时尤其重要。
request_id_var = contextvars.ContextVar('request_id', default='unknown')

# 这个中间件用于为每个进入的请求生成并附加一个唯一的请求 ID。
class RequestIDMiddleware:
    def __init__(self, get_response):
        self.get_response = get_response
        # 定义一个中间件类时,__init__ 方法接收一个 get_response 参数。这个参数是在中间件初始化时由 Django 传入的,并且它指向中间件链中下一个处理请求的函数(可能是另一个中间件的 __call__ 方法或者 Django 的视图处理函数)。

    def __call__(self, request):
    # 从同一个用户或同一个会话中的多个请求也能区分开。
        request_id = str(uuid.uuid4())
        request_id_var.set(request_id)
        request.request_id = request_id

        response = self.get_response(request)

        response['X-Request-ID'] = request_id

        return response

# 日志过滤器用于将请求 ID 从上下文变量注入到日志记录中。
class RequestIDFilter(logging.Filter):
# 继承 Python 的 logging.Filter 类。这意味着它可以重写 filter 方法,该方法用于决定一个特定的日志记录是否应该被记录或丢弃。
    def filter(self, record):
        record.request_id = request_id_var.get()
        return True

请求 ID 和用户 token 的区别

请求 ID:是为了日志记录和调试目的而生成的一个短暂的、唯一 的标识符,用于追踪单个请求的处理过程 。它通常只在服务器日志和响应头中使用,并且每个请求都不相同。

用户 Token:通常是用于认证和授权的长期有效的凭证,它表明了用户的身份,并且在多个请求间保持不变,除非用户的会话到期或被注销。

如果你想要基于用户或会话来生成一个持久的 ID,可能需要另一种实现方式,例如:

使用用户的 session ID 或 token 的某种散列值作为请求 ID 的一部分。

将请求 ID 存储在用户的会话中,并在会话持续期间重复使用该 ID。

这种方法可以帮助你跟踪一个用户在会话期间发起的所有请求,但它减少了能够跟踪单独请求的粒度。

其中这里的X-Request-ID ,查看方式:

bash 复制代码
curl -i https://yourserver.com/yourpath

返回的值:

bash 复制代码
HTTP/1.1 404 Not Found
Server: nginx/1.20.1
Date: Fri, 28 Jun 2024 11:06:58 GMT
Content-Type: text/html; charset=utf-8
Content-Length: 179
Connection: keep-alive
X-Frame-Options: DENY
X-Content-Type-Options: nosniff
Referrer-Policy: same-origin
Cross-Origin-Opener-Policy: same-origin
Vary: origin
X-Request-ID: 6654a63d-411d-4a9e-9380-636aXXXXXX


<!doctype html>
<html lang="en">
<head>

记录日志:

记录日志时包含 request_id:

在 Django 应用中,使用 RequestIDMiddleware 设置的 request_id 可以在任何日志消息中包括。这样做可以帮助你在查看日志文件时迅速关联到具体的请求。

客户端(如浏览器、移动应用或其他服务)在收到响应时,可以记录或显示 X-Request-ID。

request_id 成为了日志记录和错误追踪的关键部分。每个请求的处理从开始到结束都可以通过这个唯一标识符进行追踪。当系统出现问题时,通过提供 X-Request-ID ,技术支持人员可以迅速定位到问题发生时的后端日志,极大地提高了问题诊断的效率。这在分布式系统或对外提供 API 的服务中尤其有价值。

get_response 是一个可调用对象,通常指向中间件链中的下一个中间件或最终的视图 。这是 Django 中间件机制的核心组成部分,它使得中间件能够以层叠的方式处理请求和响应。

每个中间件的 call 方法可以决定在将请求传递给 get_response 前后做些什么,这提供了极大的灵活性。

日志配置与日志中间件

Django 日志系统 (LOGGING 配置)

Django 的 LOGGING 配置提供了一个灵活的日志系统,可以详细定义日志的处理方式,如何格式化,以及存储位置等。这些配置确保了应用可以记录关键的系统信息、错误、警告等,这对于监控和调试应用至关重要。

日志处理器 (handlers) : 负责将日志消息发送到指定的目的地,比如文件或控制台。
过滤器 (filters) : 决定哪些日志记录应该被传递给日志处理器。
格式器 (formatters) : 定义日志输出的格式。
记录器 (loggers) : 为不同的应用或库配置日志级别和处理方式。
中间件 (MIDDLEWARE): 中间件是 Django 请求/响应处理的一个框架,允许你插入自定义代码到请求处理的生命周期中。这包括在请求处理之前、之后或在视图函数调用前后执行代码。

RequestIDMiddleware: 用于为每个请求生成一个唯一的请求 ID,并通过响应头返回这个 ID。这有助于追踪和关联请求处理过程中发生的所有事件和日志。

LogMiddleware: 可能用于记录请求和响应的细节,如请求路径、请求体的一部分、响应状态等。这是增强日志信息的一种方式,可以帮助开发者理解请求的上下文。

相互关系和潜在冲突

在 Django 中,日志系统和中间件不会直接冲突,因为它们处理的层面不同:

日志系统 主要关注于如何记录、格式化和存储日志消息。
中间件 则提供了一种方式在请求/响应处理过程中执行自定义逻辑。

使用 RequestIDMiddleware 为每个请求生成的 request_id 通过日志系统的 RequestIDFilter 添加到日志消息中,是一种典型的使两者协同工作的方式。这确保了所有日志消息都包含了与请求相关的唯一标识符,大大提高了问题追踪和调试的效率。

总结
不会造成冲突,而是互为补充 。中间件在请求处理过程中生成和管理请求 ID,而日志系统则负责将这些信息记录下来。通过合理配置,这两者可以非常好地一起工作,提供强大的调试和监控能力。这是构建可维护和可监控 web 应用的重要组成部分。

LogMiddleware 是操作在**请求处理层面,直接记录请求和响应的详细信息。**这些日志通常更具体,更偏向于应用级别的监控。
LOGGING 配置定义的日志记录可能更广泛,不仅限于请求和响应信息,还包括应用的其他部分的日志,如数据库操作、系统错误等。

python 复制代码
2024-06-28 01:57:11 | Request ID: 3e8f1840-0d19-4161-b09a-f03XXXXX555 | log_middleware | __call__ | Request URL: /xxxxx
2024-06-28 01:57:11 | Request ID: 3e8f1840-0d19-4161-b09a-f030XXXXXX5 | log_middleware | __call__ | Request body: b'{""}'

确保 LogMiddleware 正确配置在 Django 的 MIDDLEWARE 设置中,这样它就会被每个请求调用。同时,通过适当配置 LOGGING 设置中的日志级别、格式和目标(如文件、控制台或外部系统),可以确保这些信息被有效记录并且易于访问和分析。

相关推荐
小码的头发丝、31 分钟前
Django中ListView 和 DetailView类的区别
数据库·python·django
Karoku06641 分钟前
【企业级分布式系统】Zabbix监控系统与部署安装
运维·服务器·数据库·redis·mysql·zabbix
周全全1 小时前
MySQL报错解决:The user specified as a definer (‘root‘@‘%‘) does not exist
android·数据库·mysql
白云如幻1 小时前
MySQL的分组函数
数据库·mysql
知识的宝藏1 小时前
Django中间件应该怎么使用
中间件·django
千澜空2 小时前
celery在django项目中实现并发任务和定时任务
python·django·celery·定时任务·异步任务
荒川之神2 小时前
ORACLE 闪回技术简介
数据库·oracle
竹笋常青2 小时前
《流星落凡尘》
django·numpy
极客小张2 小时前
基于STM32的智能充电桩:集成RTOS、MQTT与SQLite的先进管理系统设计思路
stm32·单片机·嵌入式硬件·mqtt·sqlite·毕业设计·智能充电桩
时差9533 小时前
【面试题】Hive 查询:如何查找用户连续三天登录的记录
大数据·数据库·hive·sql·面试·database