Django-Logging日志
介绍并研究Logging模块的原理和Django实际生产环境中使用需要注意的点。
Logging介绍
当后端服务运行在生产环境中时,服务对外来说是一个黑盒,作为开发人员就需要做到对服务的监控和跟踪。因此logging
模块就显得十分重要,logging
模块允许我们的去构造一些非常有用的日志信息用于跟踪和监控。
Python的logging
模块可以很好的适用于各种复杂的情况:
- 多线程支持:
Multi-threading support
- 支持不同的日志级别:
Categorization via different levels of logging
- 灵活性并可配置:
Flexibility and configurability
Logging架构
整体日志处理的流程如下,有如下几个关键的模块依次介绍:
- Logger
- Log Record
- Handlers
- Formatters
- Filters
Logger
该模块是开发者最常用到的模块. 当使用logger.info("Stock was sold at %s", price)
,即有如下图的情况。
Log records
该模块是logging
用来记录所有必要信息的模块,即一条日志的信息,日志中的关键参数,调用栈信息等等,都包含在record中。可以说record对象是存储日志信息并被handler序列化至控制台或者文件中。
Handlers
该模块将log record
记录并输出。Handler模块的主要作用是对日志进行分发和传播。
标准logging模块中有如下几个内建的handler
FileHandler
:记录日志到文件中StreamHandler
: 记录日志到流中stdout
,stderr
SMTPHandler
: 通过Email发送日志SockerHandler
: 发送日志到Socket流中SyslogHandler, NTEventHandler, HTTPHandler, MemoryHandler
:
Formatters
该模块是将metadata-rich
的log record
序列化到字符串。
例如:'%(asctime)s %(levelname)s %(name)s: %(message)s'
将会被构造成
“2017-07-19 15:31:13,942 INFO parent.child: Hello EuroPython.”
Filters
该模块是更细粒度控制日志的工具。开发者可以自己定义具体的filter
方法,并对log record
进行操作,返回True/False
. Filters返回False
, 则当前Logger拒绝此次日志记录。
下图是一个日志处理的全部流程:当如果设置了propagate==True
则就会将日志信息继续传递给父节点的handler进行处理。即当使用了LOG=logging.getLogger(__name__)
, 当传入__name__
的为参数时,将会建立以项目Project
为基础的日志等级关系,当前日志没有命中时使用父节点的handler。
实际运用-Django
在实际的Django项目中,记录请求日志和对应的全局唯一的
request_id
.
- 自定义
Filter
:用来在log record
中记录当前请求的request_id - 自定义
Middlewarel
:用来做Django的Middleware
,并将其添加到Django
的Middleware
中 - 自定义Log格式
class RequestIDFilter(logging.Filter):
def filter(self, record):
record.request_id = getattr(worker_local, 'request_id')
return True
class RequestIdMiddleware(MiddlewareMixin):
def process_request(self, request):
worker_local.request_id = request.META.get('REQUEST_ID', create_unique_id())
def process_response(self, request, response):
LOG.info("{} {} status: {}".format(request.method, request.path, response.status_code))
return response
自定义LOGGING
时,注意在具体的某个handler
中定义具体的class
,formatter
,class
.
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'filters': {
'request_id': {
"()": "project.middleware.logging.RequestIDFilter",
}
},
'formatters': {
'verbose': {
'format': '[%(levelname)s][%(asctime)s][%(module)s] [%(request_id)s] %(message)s'
},
'simple': {
'format': '[%(levelname)s] %(message)s'
}
},
'handlers': {
'null': {
'level': 'DEBUG',
'class': 'logging.NullHandler'
},
'console': {
'level': 'DEBUG',
'class': 'logging.StreamHandler',
'formatter': 'verbose',
'filters': ['request_id']
},
},
'loggers': {
'': {
'level': 'INFO',
'handlers': ['console'],
},
'project': {
'level': 'INFO',
'handlers': ['console'],
'propagate': False
},
},
}
这样就可以实现,打印日志的同时,添加了关键的request_id
字段,方便跟踪和定位问题。
总结
Python的Logging
模块提供了丰富的功能,能应对各种复杂场景。在生产环境中,项目具备良好的,可读性强的日志,可以方便开发人员跟踪定位问题。总之,日志是非常重要的一环,本文只是对日志模块结合自己的实践经历做简单的总结,以后还需要学习更多并实践更多。
Reference
- guide-logging-python