Django 日志配置解析

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

目录

  • 配置日志
  • 写日志
  • 日志中间件
  • 日志配置与日志中间件

配置日志

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


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 配置:

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: /XXXXXLOGGING = {'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 库,然后创建一个日志记录器实例:

import logginglogger = logging.getLogger(__name__)def my_view(request):try:# 你的业务逻辑passexcept Exception as e:logger.error('Something went wrong: %s', e)

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

DEBUG: 详细信息,通常只在诊断问题时有用。
INFO: 确认事情按预期运行。
WARNING: 表明有一些意外,或在不久的将来可能出现问题(例如“磁盘空间低”)。软件还能按预期工作。
ERROR: 由于更严重的问题,软件已不能执行某些功能。
CRITICAL: 严重错误,表明程序本身可能无法继续运行。

日志中间件

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

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 的 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_idresponse = self.get_response(request)response['X-Request-ID'] = request_idreturn 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 ,查看方式:

curl -i https://yourserver.com/yourpath

返回的值:

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 配置定义的日志记录可能更广泛,不仅限于请求和响应信息,还包括应用的其他部分的日志,如数据库操作、系统错误等。

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/37198.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

复盘|接口自动化测试框架建设的经验与教训

为什么选择这个话题&#xff1f; 一是发现很多“点工”在转型迷茫期都会问一些自动化测试相关的问题&#xff0c;可以说自动化测试是“点工”升级的必经之路&#xff1b;二是Google一下接口自动化测试&#xff0c;你会发现很多自动化测试框架相关的文章&#xff0c;但是大部分…

VBA 批量变换文件名

1. 页面布局 在“main”Sheet中按照下面的格式编辑。 2. 实现代码 Private wsMain As Worksheet Private intIdx As LongPrivate Sub getExcelBookList(strPath As String)Dim fso As ObjectDim objFile As ObjectDim objFolder As ObjectSet fso CreateObject("Script…

CAN和CANFD数据写入.asc文件的dll

因为工作需要&#xff0c;需要做一些硬件不是CANoe的上位机&#xff08;比如说周立功CAN,NI-CAN&#xff09;&#xff0c;上位机需要有记录数据的功能&#xff0c;所以用Qt制作了一个记录数据的dll&#xff0c;方便重复使用&#xff08;因为有的客户指定了编程软件&#xff0c;…

Android高级面试_12_项目经验梳理

Android 高级面试-1&#xff1a;Handler 相关 问题&#xff1a;Handler 实现机制&#xff08;很多细节需要关注&#xff1a;如线程如何建立和退出消息循环等等&#xff09; 问题&#xff1a;关于 Handler&#xff0c;在任何地方 new Handler 都是什么线程下? 问题&#xff1a…

Redis主从复制、哨兵以及Cluster集群

1.Redis高可用 在web服务器中&#xff0c;高可用是指服务器可以正常访问的时间&#xff0c;衡量的标准是在多长时间内可以提供正常服务&#xff08;99.9%、99.99%、99.999%等等&#xff09;。 但是在Redis语境中&#xff0c;高可用的含义似乎要宽泛一些&#xff0c;除了保证提供…

加密教程:pdf怎么加密?7个pdf加密技巧任你选(图文详解)

pdf作为一种便携式文档&#xff0c;是展示内容的首选格式&#xff0c;目前也已广泛应用于交换和分享重要等温&#xff0c;例如内部报告、人力资源文件&#xff0c;以及商业提案等包含敏感信息的文档。然而&#xff0c;在如今的数字化时代&#xff0c;随着越来越多的企业将其文档…

划分子网、子网掩码、地址范围、网络地址和广播地址。

一公司原来使用 192.168.1.0/24 这个标准网络&#xff0c;现在想为公司的每个部门(共六个)单独配置一个子网&#xff0c;其中最大的部门要分配 IPv4 地址的数量不超过 25 个。求每个子网的子网掩码、地址范围、网络地址和广播地址。 因为2&#xff0c;4&#xff0c;8所以划分为…

el-date-picker设置时间范围

下面这种写法会报错&#xff1a;找不到expirationDate&#xff0c;这是因为涉及到this的指向问题 在普通函数中&#xff0c;this 的上下文并不指向 Vue 组件实例&#xff0c;而是取决于函数的调用方式或者是否使用了严格模式 <el-date-pickerclass"date-icon-common&q…

capitalize()方法——字符串首字母转换为大写

自学python如何成为大佬(目录):https://blog.csdn.net/weixin_67859959/article/details/139049996?spm1001.2014.3001.5501 语法参考 capitalize()方法用于将字符串的首字母转换为大写&#xff0c;其他字母为小写&#xff0c;例如图1所示的效果。 图1 字符串首字母大写效果…

代码随想录算法训练营第五十二天-复习|LeetCode704 二分查找、LeetCode35 搜索插入位置、LeetCode27 移除元素

题1&#xff1a; 指路&#xff1a;704. 二分查找 - 力扣&#xff08;LeetCode&#xff09; 代码&#xff1a; class Solution { public:int search(vector<int>& nums, int target) {int left 0; int right nums.size() - 1;while (left < right) {int middl…

动手实现一个可发送短信随机密码的高安全性用户密码系统

动手实现一个可发送短信随机密码的高安全性用户密码系统 1、背景2、设计3、代码实现3.1、首先先看一下ThreadSpecificSecureRandom组件代码实现,如图所示3.2、调用nextInt的UserPasswordSystemManager组件的代码实现3.3、UserPasswordSystemManager组件所提供的方法,请看下图…

智慧数据中心可视化:高效管理与直观监控的未来

随着数据中心的规模和复杂性不断增加&#xff0c;传统管理方式难以满足需求。智慧数据中心通过图扑可视化实现实时数据监控和智能分析&#xff0c;将复杂的基础设施直观呈现&#xff0c;极大提升了运维效率、故障排查速度和资源优化能力&#xff0c;为企业提供现代化、智能化的…

Android使用WorkManager实现循环定时通知

引入包 def work_version “2.9.0” // (Java only) implementation “androidx.work:work-runtime:$work_version” // Kotlin coroutines implementation "androidx.work:work-runtime-ktx:$work_version"// optional - RxJava2 support implementation "a…

农业新质生产力数据(2012-2022年)原始+dofile+测算数据集

数据简介&#xff1a;农业新质生产力是指在现代农业发展中&#xff0c;通过融合尖端科技、信息技术与创新管理模式&#xff0c;实现农业生产效率飞跃、产品质量显著提升及生产可持续性增强的一种革新性生产能力&#xff0c;农业新质生产力代表了从依赖传统资源转向依靠科技创新…

一加Ace3 刷机救砖简化说明

注意&#xff1a;工具使用英文目录&#xff0c;支持救砖和降级。PJE110国行版&#xff0c;CPH2609国际版。目前国行版不能完美转换国际版&#xff0c;每次升级都需要刷oplusstanvbk&#xff0c;不建议使用。跨国转换或ROOT一定先解锁Bootloader&#xff0c;可以使用“一加全能工…

【java 执行 postgresql sql 结果不一致】

java 执行 postgresql sql 结果不一致 问题描述&#xff1a;pk_deptdoc&#xff0c;deptname 这两个字段只显示第一个字符。解决方案&#xff1a;是因为用到了UNION ALL&#xff0c;为了两边查询结果一致&#xff0c;我们一般会定一个空值&#xff0c;却没有定义数据类型&#…

Linux免交互

免交互 交互&#xff1a;我们发出指令控制程序的运行&#xff0c;程序在接受到指令之后按照指令的效果做出对应的反应 免交互&#xff1a;间接的通过第三方的方式把指令传送给指定的程序&#xff0c;不用直接的下达指令 Here Document 免交互&#xff1a; 这是命令行格式&…

国外Essay写作需要哪些明确规划?

Essay是一项有挑战性的任务&#xff0c;因此需要一个明确的写作规划&#xff0c;以确保您的Essay有逻辑性、准确性和连贯性。以下是一些不同寻常的写作规划的建议&#xff0c;以帮助您编写一篇成功的Essay。 创意闪光&#xff1a; 一个好的Essay写作规划应该让您的创意闪光。…

CSS隐藏元素:探索不同的隐藏技巧

在网页设计中&#xff0c;我们经常需要隐藏某些元素&#xff0c;以实现特定的布局效果或响应式设计。CSS提供了多种方法来隐藏元素&#xff0c;每种方法都有其特定的用途和效果。今天&#xff0c;我们就来探索一下CSS中隐藏元素的几种常见方式。 隐藏元素的两种方式 方式一&a…

为什么用excel求出的和是错误的?

Excel中求和结果错误的原因可能有几种常见的情况&#xff1a;1. **数据格式问题**&#xff1a;有时候数字可能被错误地视为文本格式。这种情况下&#xff0c;Excel 在求和时会忽略这些单元格。你可以通过将这些单元格的格式改为数值格式来解决。2. **隐藏的行或列**&#xff1a…