Python日志模块
日志是一个好东西、特别是在系统出现问题甚至是BUG的时候、非常的及时有效。Django使用了Pyhton内置的logging模块来实现他的日志系统;但并不是所有的业务场景都需要使用logging模块,Python官方也推荐了一些使用的方法:
业务场景 | 最佳工具 | 备注 |
---|---|---|
普通情况下,在控制台显示输出 | print() |
|
报告正常程序操作过程中发生的事件 | logging.info() (或者更详细的logging.debug() ) |
|
发出有关特定事件的警告 | warnings.warn() 或者logging.warning () |
|
报告错误 | 弹出异常 | |
在不引发异常的情况下报告错误 | logging.error() , logging.exception() 或者logging.critical() |
同时Python内置的logging模块也定义了几种日志的级别,按照事件严重程度由低到高进行排列如下:
日志级别 | 级别数值 | 业务场景 | 备注 |
---|---|---|---|
DEBUG | 10 | 详细信息、常用于代码调试 | |
INFO | 20 | 程序正常运行过程中产生的一些系统信息 | |
WARNING | 30 | 用户警告,虽然程序还在正常运行,但是有可能发生错误 | |
ERROR | 40 | 由于更严重的问题,程序已不能执行一些功能了。 | |
CRITICAL | 50 | 严重错误,程序已不能继续运行。 |
注:默认级别是WARNING,表示只有WARING和比WARNING更严重的事件才会被记录到日志内,低级别的信息会被忽略。因此,默认情况下,DEBUG和INFO会被忽略,WARING、ERROR和CRITICAL会被记录。关于Python Logging日志模块更详细的内容、感兴趣的小伙伴可以自动去查看官网文档:https://docs.python.org/zh-cn/3.6/library/logging.html
Django日志组件
Django提供了四大日志组件,他们分别是 Logger、Handlers、Filters 和 Formatters。
Logger(记录器):Logger是日志系统的入口、每一条写入 Logger 的消息都是一条日志记录,每一条日志记录都包含级别,代表对应消息的严重程度。当 Logger 处理一条消息时,会将自己的日志级别和这条消息配置的级别做对比。如果消息的级别匹配或者高于 Logger 的日志级别,它就会被进一步处理,否则这条消息就会被忽略掉。Logger是日志记录的处理类/对象,当 Logger 确定了一条消息需要处理之后,会把它传给 Handler;一个Logger可以有多个Handlers。
Handlers(处理器):它的主要功能是决定如何处理 Logger 中的每一条消息,比如把消息输出到屏幕、文件或者 Email 中。和 Logger 一样,Handler 也有级别的概念。如果一条日志记录的级别不匹配或者低于 Handler 的日志级别,则会被 Handler 忽略。上面我们说了一个Logger可以有多个Handlers,而每一个Handler 可以有不同的日志级别。这样就可以根据消息的重要性不同,来提供不同类型的输出。例如,你可以添加一个 Handler 把 ERROR 和 CRITICAL 消息发到你的 Email,再添加另一个 Handler 把所有的消息(包括 ERROR 和 CRITICAL 消息)保存到文件里。
Filters(过滤器):过滤器用于Logger/Handlers之上,在日志记录从 Logger 传到 Handler 的过程中,使用 Filter 来做额外的控制。例如,只允许某个特定来源的 ERROR 消息输出。Filter 还被用来在日志输出之前对日志记录做修改。例如,当满足一定条件时,把日志级别从 ERROR 降到 WARNING 。Filter 在 Logger 和 Handler 中都可以添加,多个 Filter 可以链接起来使用,来做多重过滤操作。
Formmaters(格式化器):定义日志文件记录的格式。
配置Django日志
在Django控制台中输出日志
讲了这么多、下面我们一起来看看如何在Django控制台中配置一个简单的日志记录,代码如下:
# 在settings.py里面配置LOGGING日志
import os
LOGGING = {
# 日志版本
'version': 1,
# 是否禁用已有的其他logger
'disable_existing_loggers': False,
# 配置handlers处理器
'handlers': {
'console': {
'class': 'logging.StreamHandler',
},
},
'root': {
'handlers': ['console'],
'level': 'WARNING',
},
# 配置loggers记录器
'loggers': {
'django': {
'handlers': ['console'],
'level': os.getenv('DJANGO_LOG_LEVEL', 'INFO'),
'propagate': False,
},
},
}
注:这里我们采用dictConfig的格式来定义日志记录,dictConfig是用一个字典格式的形式来定义日志内容的。这种方式在本地开发环境进行调试时比较有帮助,很直观,需要的时候马上就能看到输出,不必切换到文件或者查收邮件。不过在生产环境下最好不要输出到控制台;默认情况下,只有在DEBUG = True的时候才会将日志输出到控制台,而且只处理INFO以上级别的日志。默认情况下,这种日志并不会输出很多信息,如果你想看到操作数据库的细节,可以在logger的level中设置’level’: os.getenv(‘DJANGO_LOG_LEVEL’, ‘DEBUG’),这样就可以了。
Django控制台日志器定义好之后我们就去APP下面的views.py业务逻辑里面进行应用:
# 导入logging库
import logging
# 获取一个logger对象
logger = logging.getLogger(__name__)
def home(request):
......
try:
open('test.txt', 'r')
except Exception as e:
logger.error(e)
......
return render(request, 'home.html')
这里我们通过open去打开一个名为test.txt的只读文件、实际上项目中是没有这个文件的;那么如果我们去访问home.html页面、控制台一定会输出错误日志的、如下:
我们可以看到错误日志已经被成功的捕获到并且输出在PyCharm的控制台中、当然把日志打印导控制台、一般是在开发期间的信息展示;而且在生产环境中我们需要的是INFO及以上级别的日志信息、并输出到log日志文件中信息保存以便在异常的时候进行查看。实际上、logger对象有很多内置方法、比如:logger.debug();logger.info();logger.warning();logger.error();logger.critical();logger.log():手动输出一条指定日志级别的日志消息。(上面五种方法的基础版);logger.exception():创建一个包含当前异常堆栈帧的 ERROR 级别日志消息等。
本地文件形式输出日志
当然、我们还可以把日志文件输出到本地文件中,这里我们我们在settings.py配置文件中重新定义日志的输出方式为本地文件形式、代码如下:
# 在settings.py里面配置LOGGING日志
LOGGING = {
# 日志版本
'version': 1,
# 是否禁用已有的其他logger
'disable_existing_loggers': False,
# 配置handlers处理器
'handlers': {
'file': {
'level': 'DEBUG',
'class': 'logging.FileHandler',
'filename': 'log/debug.log',
},
},
'loggers': {
'django': {
'handlers': ['file'],
'level': 'DEBUG',
'propagate': True,
},
},
}
重启项目之后我们去访问home.html页面、在项目的log目录下面我们可以看到输出的debug.log日志文件;这个日志文件包含了项目输出的所有日志信息。那么我们在实际的生产环境中到底该如何配置一个生产级别的Django日志系统呢?
完整Django日志配置
上面的Django日志输出都比较简单、有很多信息都没有打印出来;比如打印的时间、打印的类、函数等信息都没有。这样我们再排查问题的时候就很难进行定位;我们一起来看一个完整的日志配置、代码如下:
#线上环境时要关闭debug
DEBUG = False
#线上环境时要允许所有ip访问,或有自己的规则
ALLOWED_HOSTS = ['*']
LOG_DIR = "logs"
LOGGING = {
# 日志版本
'version': 1,
# 是否禁用已有的其他logger
'disable_existing_loggers': False,
# 这里我们定义一个simple格式的formatters
'formatters': {
'simple': { # exact format is not important, this is the minimum information
# asctime代表打印当前的时间、name代表当前的类、lineno代表类的行数、levelname代表日志等级、message就是日志信息
'format': '%(asctime)s %(name)-12s %(lineno)d %(levelname)-8s %(message)s',
},
},
'handlers': {
# 控制台输出
'console': {
'class': 'logging.StreamHandler',
'formatter': 'simple',
},
# 邮件输出:错误级别的日志发送到邮件
'mail_admins': { # Add Handler for mail_admins for `warning` and above
'level': 'ERROR',
'class': 'django.utils.log.AdminEmailHandler',
},
# 文本文件输出到指定文件
'file': {
'level': 'INFO',
'class': 'logging.FileHandler',
'formatter': 'simple',
'filename': os.path.join(LOG_DIR, 'admin.log'),
},
},
# 系统全局级别默认的日志记录器、他是logger里面的一种特殊的记录器
'root': {
# 系统全局默认日志往控制台和文本文件同事输出
'handlers': ['console', 'file'],
'level': 'INFO',
},
}
然后我们启动项目、去访问前端页面并触发日志记录;会发现在logs目录里面已经产生了日志文件、如下:
从上面的日志信息我们可以看到详细的日志信息包含日志时间、访问的类以及类的行、日志级别、详细的日志信息等内容。
注:详细的内容感兴趣的小伙伴可以自行去看官方文档:https://docs.djangoproject.com/zh-hans/3.0/topics/logging/