Django默认使用python内置的logging模块来记录日志,其内置的日志处理模块实现了常用的日志处理功能,帮助开发者快速的使用日志功能,让开发者专注于编写业务逻辑代码,本文介绍如何在Django中优雅的处理日志。
Django中使用python内置的logging模块来记录日志,因此,我们可以直接在Django中使用logging的日志功能,包括:
- 日志输出到文件
- 日志输出到流
- 远程输出日志至tcp sockets
- 远程输出日志到UDP sockets
- 远程输出日志到邮件地址
- 日志输出到syslog
- 远程输出日志到Windows NT/2000/XP的事件日志
- 日志输出到内存中的制定buffer
- 通过”GET”或”POST”远程输出到HTTP服务器
为了更好的处理程序中出现的异常情况,需要记录日志并发送至统一的日志管理平台,目前标准的日志记录方案之一是file + filebeat + ELK
,本文不对该方案做任何介绍,仅介绍django项目中如何记录日志。
创建并初始化项目
创建Django项目:django-admin startproject django_sample
,目录结构如图
进入项目目录,安装django依赖项:pip install django
,如图
安装django app:python manage.py startapp log_sample_normal
,如图
创建视图:打开log_sample_normal/views.py
文件,写入如下代码1
2
3
4
5
6
7from django.http import JsonResponse
from django.views import View
class SampleLogEndPoint(View):
def get(self, request):
return JsonResponse({'status': 201, 'msg': 'success'})
为了访问上述视图,需要创建url到视图文件的路由,首先,创建log_sample_normal/urls.py
文件,写入如下代码1
2
3
4
5
6
7from django.urls import path
from log_sample_normal.views import SampleLogEndPoint
urlpatterns = [
path('sample_log', SampleLogEndPoint.as_view())
]
上述代码创建了app内部sample_log
到视图SampleLogEndPoint
之间的映射,接下来,我们将urlpatterns
注册到项目django_sample
中。
打开django_sample/urls.py
文件,添加如下代码:1
2
3
4
5
6
7
8
9
10from django.contrib import admin
from django.urls import path
# 新增该行代码
from django.urls import include
urlpatterns = [
path('admin/', admin.site.urls),
# 新增该行代码
path('log/', include('log_sample_normal.urls')),
]
目前为止,已经完成了项目的基础配置,接下来,启动django项目并尝试访问创建的视图:python manage.py runserver
访问视图,结果如下
日志的基本配置
django中默认的日志配置位于django/utils/log.py
文件中,Django项目在启动时,调用该文件的configure_logging
方法并传入django_sample.settings
包中的LOGGING
字段作为logging_settings
参数,格式为dict。
因此,只需要在django_sample.setting
包中配置LOGGING
字段即可。为了避免django_sample/settings.py
文件过大导致配置文件难以维护,我们将django_sample/settings.py
文件改为django_sample/settings
包,默认配置放入django.py
文件,日志相关的配置放入logging.py
文件,如下:
创建django_sample/settings/logging.py
文件并写入配置1
2
3
4
5
6
7
8
9
10
11
12
13LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'handlers': {
'console': {
'class': 'logging.StreamHandler',
},
},
'root': {
'handlers': ['console'],
'level': 'WARNING',
},
}
将django_sample/settings/django.py
和django_sample/settings/logging.py
下的配置导入setting包的__init__.py
文件
日志写入文件
logging中存在很多handler,可根据需要进行配置和调用,此处以写入固定大小的文件为例。
在logs/sample_log.log
文件中创建file
handler并创建sample_log
的logger1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'handlers': {
'console': {
'class': 'logging.StreamHandler',
},
'file': {
'class': 'logging.handlers.RotatingFileHandler',
'filename': 'logs/sample_log.log',
'backupCount': 5,
'maxBytes': 128,
},
},
'root': {
'handlers': ['console'],
'level': 'WARNING',
},
'loggers': {
'sample_log': {
'handlers': ['file'],
'level': 'INFO',
},
}
}
在视图SampleLogEndPoint
中获取sample_log
的logger实例并记录日志1
2
3
4
5
6
7
8
9
10
11
12
13from django.http import JsonResponse
from django.views import View
# 增加该行
import logging
# 增加该行
logger = logging.getLogger('sample_log')
class SampleLogEndPoint(View):
def get(self, request):
# 增加该行
logger.info('access to SampleLogEndPoint')
return JsonResponse({'status': 201, 'msg': 'success'})
访问http://127.0.0.1:8000/log/sample_log
触发日志,生成文件
Django中内置的logger
Django中预定义了django
和django.server
两个日志记录器,所有django.*
的日志记录器记录的日志将由django
处理。
django.request ¶
该logger记录与处理HTTP请求相关的信息,包括状态码5xx
和4xx
1
2
3
4
5
6def http_method_not_allowed(self, request, *args, **kwargs):
logger.warning(
'Method Not Allowed (%s): %s', request.method, request.path,
extra={'status_code': 405, 'request': request}
)
return HttpResponseNotAllowed(self._allowed_methods())
django.server ¶
处理runserver
启动的服务器接收到的HTTP请求相关的日志信息,5XX为error
信息、4xx为warn
信息,其它的为info
信息。如果想要保存访问日志到文件中,可在django_sample/settings/logging.py
文件中创建如下日志配置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
44
45
46from django.utils.log import DEFAULT_LOGGING
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'formatters': {
# 覆盖django.server的format
'django.server': DEFAULT_LOGGING['formatters']['django.server']
},
'handlers': {
'console': {
'class': 'logging.StreamHandler',
},
'file': {
'class': 'logging.handlers.RotatingFileHandler',
'filename': 'logs/sample_log.log',
'backupCount': 5,
'maxBytes': 128,
},
# 覆盖django.server的handler
'django.server': {
'level': 'INFO',
'class': 'logging.handlers.RotatingFileHandler',
'filename': 'logs/access.log',
'backupCount': 5,
'maxBytes': 1024 * 5,
'formatter': 'django.server',
},
},
'root': {
'handlers': ['console'],
'level': 'WARNING',
},
'loggers': {
'sample_log': {
'handlers': ['file'],
'level': 'INFO',
},
# 覆盖默认django.server的logger
'django.server': {
'handlers': ['django.server'],
'level': 'INFO',
'propagate': False,
},
}
}
django.template ¶
记录与Django模版渲染相关的日志,由于模板很少被用到,因此不进行该logger的分析。
django.db.backends ¶
记录代码与数据库交互相关的日志,主要是执行的sql语句、查询参数及sql执行时间,但是不包括ORM框架初始化(e.g. SET TIMEZONE)或事务管理查询(e.g. BEGIN, COMMIT, and ROLLBACK),如果需要查看这些日志,需要在数据库打开查询日志。
该日志只有在Debug=True
时,才会被打印,实现代码在django/db/backends/utils.py
文件中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
def debug_sql(self, sql=None, params=None, use_last_executed_query=False, many=False):
start = time.monotonic()
try:
yield
finally:
stop = time.monotonic()
duration = stop - start
if use_last_executed_query:
sql = self.db.ops.last_executed_query(self.cursor, sql, params)
try:
times = len(params) if many else ''
except TypeError:
# params could be an iterator.
times = '?'
self.db.queries_log.append({
'sql': '%s times: %s' % (times, sql) if many else sql,
'time': '%.3f' % duration,
})
logger.debug(
'(%.3f) %s; args=%s',
duration,
sql,
params,
extra={'duration': duration, 'sql': sql, 'params': params},
)
django.security.* ¶
当发生应用安全相关的异常时,调用该日志记录器记录日志,这里不进行深入分析。
django.db.backends.schema ¶
migrations框架在进行原数据操作时,调用该日志记录器记录日志。
Django提供的更多日志配置
Django提供了AdminEmailHandler
日志处理器,该日志处理将发送日志数据到站点管理员的邮箱,默认情况下,django.*
下所有ERROR
或CRITICAL
的日志都将调用该日志记录器记录日志。
更多日志相关文档可参考官方文档