Django灵活使用logging

Django使用python的内置模块logging来管理自己的日志。

一、loggint四大组件

  1. Formatters
  2. Filters
  3. Handlers
  4. Loggers

FORMATTERS

Formatter(格式化):定义了怎么显示内容,因为最终的日志都会是以文本的形式展现,formatter就是描述怎么来做这件事。formatter通常都是使用python格式化字符串的方法来对日志进行格式化。

格式 描述
%(name)s 记录器的名称
%(levelno)s 数字形式的日志记录级别
%(levelname)s 日志记录级别的文本名称
%(filename)s 执行日志记录调用的源文件的文件名称
%(pathname)s 执行日志记录调用的源文件的路径名称
%(funcName)s 执行日志记录调用的函数名称
%(module)s 执行日志记录调用的模块名称
%(lineno)s 执行日志记录调用的行号
%(created)s 执行日志记录的时间
%(asctime)s 日期和时间
%(msecs)s 毫秒部分
%(thread)d 线程ID
%(threadName)s 线程名称
%(process)d 进程ID
%(message)s 记录的消息

Filters

Filter(过滤器):提供了传递给handler之前的附加功能。在通常情况下,一条日志信息只要达到logger的级别之后就会传递给handler处理,但是我们可以通过使用filter来对日志进行额外的过滤。
例如我们可以使用某个filter来控制只允许某个特定的源的ERROR级别的日志。
Filter还运行咱们在处理之前修改日志,例如降低或者提高日志的级别。
Filter可以在logger和handler中同时使用,而且多个filter会同时工作。

Handlers

Handlers(处理器):他来处理具体每条信息,例如是将日志打印到屏幕还是记录到文件或者发送至某个网络连接。
和记录器(logger)一样,Handlers也有自己的记录级别,如果日志级别低于handler的级别,handler同样会忽略掉该条日志。
django内置处理器

格式 描述
logging.StreamHandler 可以向类似与sys.stdout或者sys.stderr的任何文件对象(file object)输出信息
logging.FileHandler 将日志消息写入文件filename
logging.handlers.RotatingFileHandler(filename) 如果文件的大小超 maxBytes制定的值,那么它将被备份为filenamel
logging.handlers.TimedRotatingFileHandler 实现按指定时间数分割日志文件
logging.handlers.DatagramHandler(host,port) 送日志消息给位于制定host和port上的UDP服务器。使用UDP协议,将日志信息发送到网络
logging.handlers.HTTPHandler(host, url) 使用HTTP的GET或POST方法将日志消息上传到一台HTTP 服务器
logging.handlers.SocketHandler 使用TCP协议,将日志信息发送到网络
logging.handlers.SysLogHandler 日志输出到syslog
logging.handlers.NTEventLogHandler 远程输出日志到Windows NT/2000/XP的事件日志
logging.handlers.SMTPHandler 远程输出日志到邮件地址
logging.handlers.MemoryHandler 日志输出到内存中的制定buffer

Loggers

Loggers(日志记录器),系统中的每一条日志都是由该组件进行记录的,每一个记录器都应该有其自己的名称并标记其最低记录的等级。
日志文件中的每一条信息都是一条信息记录,都应该有其对应的级别,然后记录一些打印该条日志时的一些元数据,来告诉查阅者出现这条日志时程序的大致情况,例如调用堆栈或者状态码等等。
当一条日志给到logger时,logger会对该条信息的级别与自身的级别进行比较,如果该日志级别不低于本身级别,logger就会进行下一步操作。相反,如果日志的信息级别比logger的低,那么其会忽略这条日志,不进行任何操作。
当logger经过级别的比较之后决定要对某条日志进行处理时,就将该条日志交给了Handlers。

django内置记录器

关键字参数 描述
django 在Django层次结构中的所有消息记录器。
django.request 与请求处理相关的日志消息。5xx响应被提升为错误消息;4xx响应被提升为警告消息。
django.server 与由RunServer命令调用的服务器所接收的请求的处理相关的日志消息。HTTP 5XX响应被记录为错误消息,4XX响应被记录为警告消息,其他一切都被记录为INFO。
django.template 与模板呈现相关的日志消息
django.db.backends 有关代码与数据库交互的消息

二、Django的默认logging配置

默认情况下,Django的logging配置如下:

  • DEBUGTrue时: django logger在INFO或更高级别以django层(django.server除外)发送消息到控制台。
  • DEBUGFalse时: django logger在ERROR或CRITICAL级别以django层(django.server除外)发送消息到AdminEmailHandler。
  • 独立于DEBUG的值: django.server logger发送INFO或更高级别的消息到控制台。

三、配置logging

django默认使用字典方式来配置logging。在配置文件中,我们使用变量LOGGING设置具体的loggers, handlers, filters和formatters
logging设置是在Django的setup()方法中进行的,所以当我们在项目的setting.py文件中设置好之后,我们所设置的logger在django程序运行过程中是随时可用的。

1、disable_existing_loggers

将默认配置中的所有logger都将禁用,默认为True,所以一定要慎用。建议将disable_existing_loggers设置为False,并重新定义部分或全部默认loggers。

2、propagate

向不向更高级别的logger传递,可以基于每个记录器控制该传播。
如果您不希望特定记录器传播到其父项,则可以关闭此行为。

3、示例

示例1:将所有记录从django logger写入本地文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'handlers': {
'file': {
'level': 'DEBUG',
'class': 'logging.FileHandler',
'filename': '/path/to/django/debug.log',
},
},
'loggers': {
'django': {
'handlers': ['file'],
'level': 'DEBUG',
'propagate': True,
},
},
}

确保修改filename 路径为运行Django应用的用户有权限写入的一个位置。

示例2:Django 的日志打印到控制台

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import os

LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'handlers': {
'console': {
'class': 'logging.StreamHandler',
},
},
'loggers': {
'django': {
'handlers': ['console'],
'level': os.getenv('DJANGO_LOG_LEVEL', 'INFO'),
},
},
}

默认此配置只将级别INFO或更高版本的消息发送到控制台,但你可以设置环境变量 DJANGO_LOG_LEVEL=DEBUG,以查看Django的所有调试日志记录。

示例3: 我自己项目的一个logging配置,所以日志写入一个日志文件,upgrade写入一个单独的日志文件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'formatters': {
'default': {
'format': '%(asctime)s %(name)s %(lineno)s %(levelname)s %(message)s',
'datefmt': '%Y-%m-%d %H:%M:%S'
}
},
'handlers': {
'console': {
'level': 'DEBUG',
'class': 'logging.StreamHandler',
'formatter': 'default'
},
"file": {
'level': 'DEBUG',
'class': 'logging.handlers.TimedRotatingFileHandler',
'filename': os.path.join(BASE_DIR,'logs') + os.sep + 'xxx.log',
'when': 'D',
'interval': 1,
'formatter': 'default'
},
'upgradeFile': {
'level': 'DEBUG',
'class': 'logging.FileHandler',
'filename': os.path.join(BASE_DIR, 'logs') + os.sep + 'upgrade.log',
'formatter': 'default'
}
},
'loggers': {
'upgrade': {
'handlers': ['upgradeFile'],
'level': 'DEBUG',
'propagate': False # 禁止向上级传播
}
},
#配置根logger
'root': {
"level": "DEBUG",
"handlers": ["console","file"]
}
}

这里定义了一个root的配置项(根logger),且并不包含在四大组件当中。在使用logging.getLogger([name])获取logger实例的时候,如果没有名字,返回logger层级中的根logger(root logger),就是这个。

四、使用logging

一旦我们配置好对应的loggers, handlers, filters,和formatters之后,我们就可以在我们的代码中使用logging了,使用logging非常的简单,例如下面的代码:

1
2
3
4
5
6
7
8
9
10
11
# 导入logging库
import logging

# 获取logger的一个实例
logger = logging.getLogger(__name__)

def my_view(request, arg1, arg):
...
if bad_mojo:
# 记录一条错误信息
logger.error('Something went wrong!')

1、命名loggers

logging.getLogger() 调用获取(如有必要则创建)一个logger 的实例。
Logger实例通过一个名称标识,Logger使用名称标识的目的是用于配置。这里的名称对应settings配置中对应的记录器(loggers)里的名称。
如果没有命名,则会使用根logger,这是源码里的一段:

1
2
3
4
5
6
7
8
9
10
def getLogger(name=None):
"""
Return a logger with the specified name, creating it if necessary.

If no name is specified, return the root logger.
"""
if name:
return Logger.manager.getLogger(name)
else:
return root

Logger的名称习惯上通常使用__name__,即包含该logger的Python模块的名字。 这允许你基于模块filter和handle日志调用。

2、调用logging

Logger 实例为每个默认的日志级别提供一个入口方法:

  • logger.debug()
  • logger.info()
  • logger.warning()
  • logger.error()
  • logger.critical()

还有另外两个调用:

  • logger.log(self, level, msg, *args, **kwargs):打印消息时手工指定日志级别。
  • logger.exception():创建一个ERROR 级别日志消息,它封装当前异常栈的帧。

我自己的调用案例:

1
2
3
4
5
6
7
8
logger = logging.getLogger(__name__)
logger_upgrade = logging.getLogger('upgrade')

....
logger.info("检测客户端版本更新: %s" % data)
....
logger_upgrade.error("检测到非法版本,aid: %s"% aid)
....

检测到非法版本的log单独使用一个记录器,存入单独的文件,方便后面查看。