Django配置日志系统

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页面、控制台一定会输出错误日志的、如下:

image-20210116213823530

我们可以看到错误日志已经被成功的捕获到并且输出在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,
        },
    },
}

image-20210116220831544

重启项目之后我们去访问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目录里面已经产生了日志文件、如下:

image-20210118110304512

从上面的日志信息我们可以看到详细的日志信息包含日志时间、访问的类以及类的行、日志级别、详细的日志信息等内容。
注:详细的内容感兴趣的小伙伴可以自行去看官方文档:https://docs.djangoproject.com/zh-hans/3.0/topics/logging/

推荐文章