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 设置中的日志级别、格式和目标(如文件、控制台或外部系统),可以确保这些信息被有效记录并且易于访问和分析。

相关推荐
数据库小组3 小时前
2026 年,MySQL 到 SelectDB 同步为何更关注实时、可观测与可校验?
数据库·mysql·数据库管理工具·数据同步·ninedata·selectdb·迁移工具
华科易迅3 小时前
MybatisPlus增删改查操作
android·java·数据库
Kethy__3 小时前
计算机中级-数据库系统工程师-计算机体系结构与存储系统
大数据·数据库·数据库系统工程师·计算机中级
SHoM SSER3 小时前
MySQL 数据库连接池爆满问题排查与解决
android·数据库·mysql
熬夜的咕噜猫4 小时前
MySQL备份与恢复
数据库·oracle
jnrjian4 小时前
recover database using backup controlfile until cancel 假recover,真一致
数据库·oracle
lifewange4 小时前
java连接Mysql数据库
java·数据库·mysql
大妮哟5 小时前
postgresql数据库日志量异常原因排查
数据库·postgresql·oracle
还是做不到嘛\.6 小时前
Dvwa靶场-SQL Injection (Blind)-基于sqlmap
数据库·sql·web安全
不写八个6 小时前
PHP教程004:php链接mysql数据库
数据库·mysql·php