在web系统的开发环境以及生产环境中,经常要查看日志。记录日志的方法有很多(轻易不要使用print),在django中,我们可以通过 import logging
直接使用Python内建的logging模块来实现.
网上关于logging的资料有不少,大多也都是围绕官方文档来写的,我们可以参考。如果看别人写的文档觉得发懵,那就需要先系统了解一下logging,请看官方文档。稳妥起见,请确认你查看的文档与使用的django版本一致。
快速了解
logging机制中,有四个主要的部分:
Loggers
Loggers中的logger是整个日志机制的入口,一个logger对应一种类型的日志。logger通过设定日志级别(level属性),来配置logger的触发条件。Python中定义了5种日志级别:
由低到高分别为:DEBUG -> INFO -> WARNING -> ERROR -> CRITICAL。
代码中通过 logger = logging.getLogger('django')
获得一个logger的实例,’django’是一种类型的日志。
当一条日志信息发给logger时,日志信息的log level会和logger的log level对比。如果日志信息的log level达到或者是超过logger的log level的级别,这条信息会被发往handler去处理。否则,这条信息会被忽略。一旦logger决定信息要进一步被处理,这条信息会被传给给Handler(logger的handler属性).
所以,logger更像是一个日志处理的分发器。
Handlers
handler是实际上来处理日志记录的地方。它说明的是一个特殊的记录行为,比如,将这个信息写在屏幕上、写到一个文件或者写到一个Network socket中去。这一点是通过handler的class属性来配置的。
与logger一样,handler也有日志级别(level)属性,如果logger传来的记录级别没有handler高,那么这条记录也会被忽略。
为什么logger和handler都要设置level?
因为一个logger可以有多个handler,且每个handler可以有不同的log level。这样一来,一个logger可以接受一类日志的多个级别的信息,并且将不同级别的信息进行不同的处理。
Filters
filters是在信息从logger传递到handler的过程中实施一些过滤行为。
如果filters属性没有被设置,即视为默认情况。默认情况下,任何达到log level的日志信息都会被处理。
如果配置了filter,你可以在logging过程中添加一些额外的过滤功能。比如,只允许某个指定的来源的ERROR信息发给handler.
filters也可以修改日志记录的级别。比如,你可以写一个Filter, 设置在一些特殊情况,能够将ERROR级别的日志记录降级为WARNING级别。
filters可以在Loggers和handlers里面设置。多个filters可以同时作用于一个地方,起到多重过滤的作用。
Formatters
一条日志记录需要以文本的形式展现出来,Formatters定义了文本的格式。一个格式化器一般由python的格式字符串组成。不过,你也可以自定义格式。
使用logging的标准方法如下:
# import the logging library
import logging
# Get an instance of a logger
logger = logging.getLogger(__name__)
def my_view(request, arg1, arg):
...
if bad_mojo:
# Log an error message
logger.error('Something went wrong!')
每当bad_mojo条件满足,都会发送一个error级别的日志记录。实例代码中的__name__
代表logger的名称。
如果logger的定义如下,那么__name__
就是’django’或’django.request’。
'loggers': {
...
'django': {
'handlers': ['console'],
'level': os.getenv('DJANGO_LOG_LEVEL', 'DEBUG'),
'propagate': True,
},
'django.request': {
'handlers': ['file', 'mail_admins'],
'level': 'WARNING',
'propagate': False,
},
...
}
logger的命名规则使用”.”(dot)来标识层级关系,上例中,’django’是’django.request’的上级。这种层级关系的设计,使得低层次logger接收到的日志信息,也可以被上级logger收到。实践中,我们可以定义一个顶级的logger,用来接收所有下级logger的日志信息。
这种传播可以在每个logger基础上进行控制。如果你不想让特定的logger传播到它的上级,可以关闭这个行为。通过设置'propagate': False,
来实现。
根据上述日志的5各级别,记录日志也有5个对应的方法。
此外,还有两种日志方法
logger的配置都是在settings.py中进行的。看下栗子:
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'formatters': {
'verbose': {
'format': '%(levelname)s %(asctime)s %(module)s %(process)d %(thread)d %(message)s'
},
'simple': {
'format': '%(levelname)s %(message)s'
},
},
'filters': {
'special': {
'()': 'project.logging.SpecialFilter',
'foo': 'bar',
},
'require_debug_true': {
'()': 'django.utils.log.RequireDebugTrue',
},
},
'handlers': {
'console': {
'level': 'INFO',
'filters': ['require_debug_true'],
'class': 'logging.StreamHandler',
'formatter': 'simple'
},
'mail_admins': {
'level': 'ERROR',
'class': 'django.utils.log.AdminEmailHandler',
'filters': ['special']
}
},
'loggers': {
'django': {
'handlers': ['console'],
'propagate': True,
},
'django.request': {
'handlers': ['mail_admins'],
'level': 'ERROR',
'propagate': False,
},
'myproject.custom': {
'handlers': ['console', 'mail_admins'],
'level': 'INFO',
'filters': ['special']
}
}
}
所有与日志有关的配置默认在LOGGING ={}
中,distConfig格式。如果我们没有进行特别配置,Django会执行默认的日志配置。
默认配置
如果我们不想使用默认的配置,可以将LOGGING中的'disable_existing_loggers'
设置为True
(默认也是True),这样一来,默认的配置就会被禁用。但是设置'disable_existing_loggers': True,
必须要非常小心,因为可能产生一些令人意外的结果(官网这么说的,还没试过),所以比较建议的方法是'disable_existing_loggers': False,
然后重写部分或者全部的默认logger。
以下代码是将日志文件输出到文件的例子,如果需要参照,注意把'filename'
换成自己的文件路径和文件名。
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,
},
},
}
这种方式在本地开发环境进行调试时比较有帮助,很直观,需要的时候马上就能看到输出,不必切换到文件或者查收邮件。不过在生产环境下最好不要输出到控制台。默认情况下,只有在DEBUG = True
的时候才会将日志输出到控制台,而且只处理INFO以上级别的日志。默认情况下,这种日志并不会输出很多信息,如果你想看到操作数据库的细节,可以在logger的level中设置'level': os.getenv('DJANGO_LOG_LEVEL', 'DEBUG'),
这样就可以了。不过这样产生的日志也有点太多了。
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'),
},
},
}
再回头看看第一段例子代码,这里再拿出来晒一次
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'formatters': {
'verbose': {
'format': '%(levelname)s %(asctime)s %(module)s %(process)d %(thread)d %(message)s'
},
'simple': {
'format': '%(levelname)s %(message)s'
},
},
'filters': {
'special': {
'()': 'project.logging.SpecialFilter',
'foo': 'bar',
},
'require_debug_true': {
'()': 'django.utils.log.RequireDebugTrue',
},
},
'handlers': {
'console': {
'level': 'INFO',
'filters': ['require_debug_true'],
'class': 'logging.StreamHandler',
'formatter': 'simple'
},
'mail_admins': {
'level': 'ERROR',
'class': 'django.utils.log.AdminEmailHandler',
'filters': ['special']
}
},
'loggers': {
'django': {
'handlers': ['console'],
'propagate': True,
},
'django.request': {
'handlers': ['mail_admins'],
'level': 'ERROR',
'propagate': False,
},
'myproject.custom': {
'handlers': ['console', 'mail_admins'],
'level': 'INFO',
'filters': ['special']
}
}
}
这个例子相比之前的会稍微复杂一些。
formatters中有两个格式的配置,'verbose'
和'simple'
,这里'simple'
定义的输出格式很简单,因为只是用来输出一些INFO信息,并不是重要的日志,因此也并不需要复杂的格式。'verbose'
输出的信息就比较丰富了,可以用在ERROR或CRITICAL日志的输出。具体的格式请参考这里。
学习例子代码,不仅仅是学习用法,更重要的是领会意图
handlers中定义了两个handler:'console'
和'mail_admins'
。'console'
配置'class': 'logging.StreamHandler'
,这个我们不用过多去说。重点看看'mail_admins'
,配置'class': 'django.utils.log.AdminEmailHandler'
,那么既然要配置为通过邮件发送日志,那么是不是有必要设置一下发送和接收的邮件呢?
邮箱的配置也在settings.py中:
# 邮件配置
EMAIL_HOST = 'smtp.maildomain.com' # SMTP地址
EMAIL_PORT = 25 # SMTP端口
EMAIL_HOST_USER = '[email protected]' # 发送邮件的邮箱
EMAIL_HOST_PASSWORD = '******' # 我的邮箱密码
EMAIL_SUBJECT_PREFIX = u'[prefix]' # 为邮件Subject-line前缀,默认是'[django]'
EMAIL_USE_TLS = True # 与SMTP服务器通信时,是否启动TLS链接(安全链接)。默认是false
# 管理员站点
SERVER_EMAIL = '[email protected]' # The email address that error messages come from, such as those sent to ADMINS and MANAGERS.
ADMINS = (('receiver', '[email protected]'),) # 接收邮件的邮箱(或邮件组)
Django提供了一些内建的logger,比如上面例子中的'django'
。
'django'
只是一个最上级的logger,但实际上并不接收什么实际的信息,所有的信息都是通过下级logger接收。
django.request
是要受理关于请求(request)的日志记录。如果response是5XX,则被认为是ERROR级别的日志信息,4XX则是WARNING级别。
django.server
是要受理关于服务器端处理请求的结果的日志记录。如果response是5XX,则被认为是ERROR级别的日志信息,4XX则是WARNING级别,其他的都是INFO级别。
django.template
受理关于模板(templates)的信息。语法错误是DEBUG级别,未被捕获的异常会产生WARNING。
django.db.backends
受理代码与数据库交互过程产生的日志信息。只记录application级别的SQL操作信息,并且都会产生DEBUG级别的信息。信息中包含耗时、SQL语句、参数信息。此logger只能在DEBUG = True时才有效。
这个logger并不记录framework-level的信息,如SET TIMEZONE,也不记录事务管理相关的查询,如BEGIN、COMMIT、ROLLBACK等。想知道这方面的信息,只有去查数据库的日志了,Django并不提供。
以上是摘取的例子,其他logger,请自行查阅
未完