1. 概述
django框架的日志通过python内置的logging模块实现的,日记可以记录自定义的一些信息描述,也可以记录系统运行中的一些对象数据,还可以记录包括堆栈跟踪、错误代码之类的详细信息
logging主要由4部分组成:Loggers(日志级别)、Handlers(处理日志)、Filters(过滤日志)和Formatters(保存格式)
2. settings中完整的配置
LOGGING = {
# 固定值
'version':1,
# 格式器,详细见第6点
'formatters': {},
# 过滤器,详细见第5点
'filters':{},
# 处理器,详细见第4点
'handlers':{},
# 记录器,详细见第3点
'loggers':{},
# 根记录器,配置等同普通记录器,但是没有propagate配置项
'root':{},
# 默认为False。True:是将配置解释为现有配置的增量。False:配置会覆盖已有默认配置
'incremental':True,
# 默认为True。禁用任何现有的非root记录器。如果设置了incremental=True,则此配置无效
'disable_existing_loggers': False
}
3. Loggers
这个类是logging系统的入口
python定义了日志的5个级别,分别对应python程序中日志信息的不同严重性(严重程度从上到下越来越严重,也就是级别越高):
- DEBUG:用于调试的最低级的系统信息
- INFO:一般性的系统信息
- WARNING:一些警告性的信息,发生了一些小问题,这些问题不影响系统的正常运行,但是也不建议出现
- ERROR:系统出现错误了,该错误会影响系统的正常运行,记录错误相关的信息
- CRITICAL:非常严重的问题,譬如可能引起系统崩溃的问题等
在使用logger记录日志时,每条日志消息还有日志级别,当logger记录该日志消息时,会将消息的级别和logger配置的日志级别进行比较,只有消息的级别达到或超过logger配置的日志级别,才会将该日志消息传递给handler进一步处理,否则该日志消息会被忽略
PS:一般开发环境时,会启用DEBUG级别,而在生产环境中,启用WARNING或ERROR级别
3.1 settings中配置
通过在settings中配置LOGGING配置项实现日志配置,共4个配置项(都是可选的,不过一般会指定handler):
- level:指定记录日志的级别,没有配置则处理所有级别的日志
- propagate:设置该记录器的日志是否传播到父记录器,不设置则是True
- filters:指定过滤器列表
- handlers:指定处理器列表
示例如下:
LOGGING = {
'version': 1, # 固定值,现在只有这一个版本
'disable_existing_loggers': False, # 设置已存在的logger不失效
'loggers': {
'': {
'handlers': ['console'],
},
'django': {
'handlers': ['console'],
'propagate': True,
},
'django.request': {
'handlers': ['mail_admins'],
'level': 'ERROR',
'propagate': False,
},
'myproject.custom': {
'handlers': ['console', 'mail_admins'],
'level': 'INFO',
'filters': ['special']
}
}
}
说明:
配置了4个 logger, 分别对应2个不同的handler(console输出日志到控制台,mail_admins输出日志到邮件)
- ‘’:默认的记录器,不指定特定名称,那么就是使用这个记录器,没有配置level,那么就是处理所有级别的日志,传递所有级别的日志到console控制器
- django:传递所有级别的日志到console控制器
- django.request:django记录器的子记录器,处理ERROR级别及以上的日志,propagate设置为 False,表明不传播日志给 “django”,该logger传递日志到mail_admins控制器
- myproject.custom:处理INFO级别及以上的日志,应用了一个 special 的过滤器来过滤日志,传递日志到2个控制器([‘console’, ‘mail_admins’])处理
django框架有个默认的配置:DEFAULT_LOGGING,一旦配置了自己的LOGGING后,那么所有的默认的LOGGER全部都失效,失效不等于没有记录器了,而是说记录器不起作用了,即不会记录日志,也不会将日志传播给父记录器。因此你应该非常小心使用,因为你会感觉你丢了日志一样,可以手动设置同名的logger实现覆盖,如:
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'handlers': {
'file': {
'level': 'DEBUG',
'class': 'logging.FileHandler',
'filename': '/path/to/django/debug.log',
},
},
'loggers': {
# 覆盖了 django 记录器,所有django的记录日志最后全部写入到文件中
'django': {
'handlers': ['file'],
'level': 'DEBUG',
'propagate': True,
},
},
}
disable_existing_loggers默认是True,除非设置disable_existing_loggers为False,那么默认配置的记录器才会起作用
LOGGING = {
'disable_existing_loggers': False,
}
配置还可以使用系统变量,如下示例中读取 DJANGO_LOG_LEVEL 环境变量:
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'),
},
},
}
3.2 logger的简单使用
在诸如views.py中:
import logging
# 生成一个以当前文件名为名字的logger实例
logger = logging.getLogger(__name__)
# __name__ 也可以换成在settings中配置的 logger 名称,生成指定的logger
logger_custom = logging.getLogger('myproject.custom')
def index(request):
logger.debug("进入index视图函数")
logger_custom.debug("进入index视图函数")
-
getLogger(_name_):记录器名使用模块名,这是基于每个模块过滤和处理日志记录调用,对应默认记录器,即名称为 ‘’ 的
-
getLogger(‘myproject.custom’):通过 “.” 符号分隔的方式,定义记录器名称的层次结构,‘myproject’ 是跟记录器, ‘myproject.custom’是子记录器,甚至 ‘myproject.custom.child’ 是孙辈的记录器。使用层次结构的作用是因为子记录器是可以传播日志记录给父记录器的,最终传播到根记录器。默认就是传播的,如果不想传播,那么需要在logger的配置中使用’propagate’: False
3.3常用方法
Logger.debug(msg)、Logger.info(msg)、Logger.warning(msg)、Logger.error(msg)、Logger.critical(msg) ,分别对应5个不同的日志级别
Logger.log(level, msg) 记录日志,手动指定level,level的选择是CRITICAL = 50,ERROR = 40,WARNING = 30,INFO = 20,DEBUG = 10,需要填写数字,譬如 50或logging.DEBUG
Logger.exception():等同于Logger.error(msg, exc_info=True),输出异常的堆栈信息
3.4 不常用方法
Logger.setLevel(lel):指定最低的日志级别,低于lel的级别将被忽略。debug是最低的内置级别,critical为最高
Logger.addFilter(filt)、Logger.removeFilter(filt):添加或删除指定的filter Logger.addHandler(hdlr)、Logger.removeHandler(hdlr):增加或删除指定的handler
3.5 django内置logger
内置的logger在django项目运行中会自动记录日志,与我们手动创建的logger的执行没有关系,除非我们也创建相同的logger
django框架调用的地方在:django.core.servers.basehttp中(如WSGIRequestHandler)
-
django:django框架中所有消息的记录器,一般使用它的子记录器,而不是它发布消息,因为默认情况下子记录器的日志会传播到根记录器django,除非设置 ‘propagate’: False
-
django.request:记录与请求处理相关的消息。5XX响应作为ERROR消息; 4XX响应作为WARNING消息引发。记录到django.security记录器的请求不会记录到django.request中
发送给此记录器的消息具有以下额外上下文:
- status_code:与请求关联的HTTP响应代码
- request:生成日志消息的请求对象。
-
django.server:记录与runserver命令调用的服务器接收的请求的处理相关的消息。5XX响应记录为ERROR 消息,4XX响应记录为WARNING消息,其他所有响应记录为INFO。
发送给此记录器的消息具有以下额外上下文:
- status_code:与请求关联的HTTP响应代码。
- request:生成日志消息的请求对象。
-
django.template:记录与模板呈现相关的消息
-
django.db.backends:记录代码和数据库交互相关的消息,例如,请求执行的每个SQL语句都会记录为DEBUG级别的日志。这个记录器只有在settings.DEBUG设置为True时才启用,并且不记录事务管理(如:BEGIN, COMMIT, 和 ROLLBACK)
发送给此记录器的消息具有以下额外上下文:
- duration:执行SQL语句所花费的时间。
- sql:已执行的SQL语句。
- params:SQL调用中使用的参数
-
django.security.*:记录任何SuspiciousOperation和其他安全相关错误(django.security.csrf )的消息,SuspiciousOperation子类型有:
DisallowedHost
DisallowedModelAdminLookup
DisallowedModelAdminToField
DisallowedRedirect
InvalidSessionKey
RequestDataTooBig
SuspiciousFileOperation
SuspiciousMultipartForm
SuspiciousSession
TooManyFieldsSent使用如:django.security.DisallowedHost
-
django.db.backends.schema:记录数据库迁移过程中的日志,但是不记录执行的查询SQL语句等,发送给此记录器的消息具有以下额外上下文:
- sql:已执行的SQL语句。
- params:SQL调用中使用的参数
4. Handlers
这个类是确定logger中消息发生的引擎程序,描述特定的日志记录行为,譬如控制台打印、写入日志文件、通过网络进行发送等
与logger一样,handler也具有日志级别,如果日志记录的日志级别未达到或超过handler的级别,则handler将忽略该消息。
一个logger可以有多个handler,每个handler可以有不同的日志级别和记录方法
4.1 settings中配置
4个参数(如下),加上对应class类的初始化参数
- class(必需):处理程序类的名称
- level(可选的):处理程序的级别
- formatter(可选的):处理程序的格式化程序
- filters(可选的):处理程序的过滤器的列表
LOGGING = {
'handlers': {
'console': {
'level': 'INFO',
'filters': ['require_debug_true'],
'class': 'logging.StreamHandler',
'formatter': 'simple',
},
'mail_admins': {
'level': 'ERROR',
'class': 'django.utils.log.AdminEmailHandler',
'filters': ['special']
}
},
}
说明:
配置了2个handler,使用了2个filter:require_debug_true和special, 使用了1个formatter:simple
- console:使用logging.StreamHandler,记录INFO级别及以上的日志到sys.stderr,使用simple格式化输出内容
- mail_admins:使用django.utils.log.AdminEmailHandler,将ERROR及以上的日志通过special过滤器过滤后,发送邮件到管理员邮箱(需要配置settings中的一些其他的信息,譬如ADMINS等)
4.2 不常用方法
Handler.setLevel(level):指定被处理的信息级别,低于level级别的信息将被忽略
Handler.setFormatter(fmt):给这个handler选择一个格式fmt
Handler.addFilter(filt)、Handler.removeFilter(filt):新增或删除一个filter对象
4.3 内置处理器
-
python3的logging中的handler:
-
StreamHandler:输出到stream,未指定则使用sys.stderr输出到控制台
-
FileHandler:继承自StreamHandler,输出到文件,默认情况下,文件无限增长
初始化参数:filename,mode =‘a’,encoding = None,delay = False
delay如果为True,那么会延迟到第一次调用emit写入数据时才打开文件
配置:
'handlers': { 'file': { 'level': 'DEBUG', 'class': 'logging.FileHandler', 'filename': '/path/to/django/app.log', #参数配置在这里,多个参数按顺序继续配置即可, 如果要添加encoding,那么在下面添加 encoding: 'utf-8' 即可 }, }
-
NullHandler:没有任何输出,避免出现错误:No handlers could be found for logger XXX
-
WatchedFileHandler:自动重开log文件,配合别的会自动切分的log文件使用
-
RotatingFileHandler:自动按大小切分的log文件
初始化参数:filename,mode =‘a’,maxBytes = 0,backupCount = 0,encoding = None,delay = False
maxBytes:最大字节数,超过时创建新的日志文件,如果backupCount或maxBytes有一个为0,那么就一直使用一个文件
backupCount:最大文件个数,新文件的扩展名是指定的文件后加序号".1"等,譬如:backupCount=5,基础文件名为:app.log,那么达到指定maxBytes之后,会关闭文件app.log,将app.log重命名为app.log.1,如果app.log.1存在,那么就顺推,先将 app.log.1重命名为app.log.2,再将现在的app.log命名为app.log.1,最大创建到app.log.5(旧的app.log.5会被删除),然后重新创建app.log文件进行日志写入,也就是永远只会对app.log文件进行写入。
-
TimedRotatingFileHandler:按时间自动切分的log文件,文件后缀 %Y-%m-%d_%H-%M-%S
初始化参数:filename, when=‘h’, interval=1, backupCount=0, encoding=None, delay=False, utc=False, atTime=None
when:时间间隔类型,不区分大小写
'S':秒 'M':分钟 'H':小时 'D':天 'W0'-'W6':星期几(0 = 星期一) 'midnight':如果atTime未指定,则在 0点0分0秒 翻转,否则在atTime时间翻转
interval:间隔的数值
backupCount: 文件个数
encoding:编码
delay:True是写入文件时才打开文件,默认False,实例化时即打开文件
utc:False则使用当地时间,True则使用UTC时间
atTime:必须是datetime.time实例,指定文件第一次切分的时间,when设置为S,M,H,D时,该设置会被忽略
-
SocketHandler:通过TCP套接字发送日志记录消息
初始化参数:host, port
-
DatagramHandler:通过UDP套接字发送日志记录消息
-
SysLogHandler :发送记录消息到远程或本地Unix系统日志
-
NTEventLogHandler:发送日志消息到本地的Windows NT,Windows 2000或Windows XP事件日志
-
SMTPHandler:通过email发送日志记录消息
初始化参数:mailhost, fromaddr, toaddrs, subject, credentials=None, secure=None, timeout=5.0
mailhost:发件人邮箱服务器地址(默认25端口)或地址和指定端口的元组,如:(‘smtp.163.com’, 25)
fromaddr:发件人邮箱
toaddrs:收件人邮箱列表
subject:邮件标题
credentials:如果邮箱服务器需要登录,则传递(username, password)元组
secure:使用TLS加密协议
-
MemoryHandler :在内存中的日志记录缓冲,定期将其发送到目标处理程序,只要缓冲区已满,或者发生某个严重程度或更高的事件,就会发生发送
-
HTTPHandler:发送记录消息到Web服务器,使用GET或POST
-
QueueHandler:发送记录消息到队列中,适合多进程(multiprocessing)场景
-
QueueListener:从队列中接收消息,适合多进程(multiprocessing)场景,用于和QueueHandler搭配
-
-
django内置的handler:
-
AdminEmailHandler:会将收到的每一条日志消息发送一个邮件到ADMINS指定的邮箱地址
必须settings中设置 DEBUG=False 才起作用
示例配置:
DEBUG=False EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend' EMAIL_HOST = 'smtp.qq.com' # smtp地址 EMAIL_PORT = 25 EMAIL_HOST_USER = 'xxxx@qq.com' # smtp 服务器的用户名 EMAIL_HOST_PASSWORD = 'xxxxxxxx' # smtp服务器的密码 SERVER_EMAIL = 'xxxx@qq.com' ADMINS = [('Terry', 'xxxx@qq.com')]
ADMINS默认是[],示例配置:[(‘John’, ‘john@example.com’), (‘Mary’, ‘mary@example.com’)]
如果日志记录包含request属性,则请求的完整详细信息将包含在电子邮件中。
如果客户端的IP地址在INTERNAL_IPS设置中,则电子邮件主题将包含短语“内部IP” ; 如果没有,它将包括“外部IP”。
如果日志记录包含堆栈跟踪信息,则该堆栈跟踪将包含在电子邮件中。
初始化参数:include_html=False, email_backend=None
include_html:该值设置为True,并且settings中设置DEBUG=True时,则会在邮件中包括含有调试网页的全部内容的HTML附件,该附件包含完整的回溯,包含堆栈每个级别的局部变量的名称和值,以及Django设置的值,会比较敏感,谨慎使用
email_backend:指定邮件后台,没设置使用django默认的
简单配置如下:
'handlers': { 'mail_admins': { 'level': 'ERROR', 'class': 'django.utils.log.AdminEmailHandler', } },
覆盖django默认的邮件后台,配置:
'handlers': { 'mail_admins': { 'level': 'ERROR', 'class': 'django.utils.log.AdminEmailHandler', 'email_backend': 'django.core.mail.backends.filebased.EmailBackend', 'include_html': True # 信息中包含html页面 } },
-
4.4自定义处理器
任意自定义的class,继承自logging.Handler,并且实现emit和flush方法(如下logging内置的StreamHandler类):
from logging import Handler
class StreamHandler(Handler):
terminator = '\n'
def __init__(self, stream=None):
Handler.__init__(self)
if stream is None:
stream = sys.stderr
self.stream = stream
def flush(self):
self.acquire()
try:
if self.stream and hasattr(self.stream, "flush"):
self.stream.flush()
finally:
self.release()
def emit(self, record):
try:
msg = self.format(record)
stream = self.stream
stream.write(msg)
stream.write(self.terminator)
self.flush()
except Exception:
self.handleError(record)
5. Filters
过滤器filter用于提供对日志记录从logger传递到handler的附加控制
默认情况下,logger和handler将处理满足日志级别要求的任何日志消息,但是,通过安装filter,可以在日志记录过程中添加其他条件。例如,可以安装仅允许ERROR级别 来自特定源的消息的filter。
filter还可用于在发出之前修改日志记录。例如,如果满足一组特定条件,可以编写一个过滤器,将ERROR日志记录降级为WARNING记录。
filter可以安装在logger或handler上; 可以在链中使用多个filter来执行多个过滤操作。
5.1 settings中配置
LOGGING = {
'filters': {
'special': {
'()': 'project.logging.SpecialFilter',
'foo': 'bar',
},
'require_debug_true': {
'()': 'django.utils.log.RequireDebugTrue',
},
},
}
说明:
配置了2个过滤器
-
special:使用自定义的SpecialFilter类,传入初始化参数 foo=‘bar’
-
require_debug_true:使用类:RequireDebugTrue
5.2 内置过滤器
-
python内置的过滤器:
日志过滤器的父类:Filter,一般不直接使用
-
django内置的过滤器:
-
CallbackFilter:
初始化参数:callback
callback:为每个记录调用callback,如果callback返回True,则允许日志通过,如果callback返回False,则不允许该日志通过
示例:
# callback函数: def skip_unreadable_post(record): if record.exc_info: exc_type, exc_value = record.exc_info[:2] if isinstance(exc_value, UnreadablePostError): return False return True # filter配置: 'filters': { 'skip_unreadable_posts': { '()': 'django.utils.log.CallbackFilter', 'callback': skip_unreadable_post, } },
-
RequireDebugFalse:仅在settings.DEBUG为False时传递记录
'filters': { 'require_debug_false': { '()': 'django.utils.log.RequireDebugFalse', } }
-
RequireDebugTrue:仅在settings.DEBUG为True时传递记录
-
5.3 自定义过滤器
1. 在子应用下创建一个py文件,自定义class,继承自logging.Filter,并且实现filter方法(如下django内置的RequireDebugFalse类)
class RequireDebugFalse(logging.Filter):
def filter(self, record):
return not settings.DEBUG
class ExistWordFilter(logging.Filter):
def __init__(self, name='', exist_word=None):
super().__init__(name)
self.exist_word = exist_word
def filter(self, record):
if not self.exist_word or self.exist_word in record.msg:
return True
else:
return False
-
setting中配置过滤
'filters': { 'require_debug_true': { '()': 'django.utils.log.RequireDebugTrue', }, 'require_debug_false': { '()': 'django.utils.log.RequireDebugFalse', }, 'exist_word_filter': { '()': 'logger_app.log.ExistWorFilter', 'exist_word':'测试' }, }, 'handlers': { 'console': { 'level': 'DEBUG', 'filters': ['require_debug_true','exist_word_filter'], 'class': 'logging.StreamHandler', 'formatter': 'simple' },
-
视图函数
from django.http.response import HttpResponse from django.shortcuts import render # 这是views视图模块 import logging # 这个 logger 只会在控制台输出 warning及以上的日志 logger = logging.getLogger('logger_study.file') # 这个logger 会输出 info及以上的日志 # logger_server = logging.getLogger('django.server') def logger_test(request): logger.debug('logger debug 测试') logger.info('logger info 测试') logger.warning('logger warning') logger.error('logger error') return HttpResponse('日志测试响应成功')
-
运行结果
控制台输出
[DEBUG][2021-08-02 13:45:03,896] logger debug 测试 [INFO][2021-08-02 13:45:03,896] logger info 测试
只有信息中包含“测试”的日志信息才在控制台输出。
6.Formatters
日志记录最终需要呈现为文本,formatter程序描述该文本的确切格式。formatter通常由包含LogRecord属性的Python格式化字符串组成 ; 但是,也可以编写自定义formatter来实现特定的格式化行为。
6.1 settings中配置:
3个参数(具体看后面的Formatter类):
- ():指定格式器的类,不指定的话,默认使用logging.Formatter
- format:格式化字符串
- style:样式选择
- datefmt:日期格式化字符串,使用的是python中时间日期格式化符号
LOGGING = {
'formatters': {
'verbose': {
'()': 'logging.Formatter',
'format': '{levelname} {asctime} {module} {process:d} {thread:d} {message}',
'style': '{',
},
'simple': {
'format': '{levelname} {message}',
'style': '{',
},
}
}
说明:
配置了2个格式器:
- simple:只输出简单的:日志级别名称 日志消息
- verbose:输出:日志级别名称 生成日志消息的时间 模块 进程 线程 日志消息
6.2 内置格式器
-
python内置的格式器
-
Formatter:默认格式器
初始化参数:fmt=None, datefmt=None, style=‘%’
- fmt:格式化字符串,指定输出格式,如:‘{levelname}{process:d}{message}’
%(name)s:记录器logger的名称 %(levelno)s:日志级别对应的数字 %(levelname)s:日志级别名称 %(pathname)s:日志记录调用的源文件的完整路径 %(filename)s:日志记录调用的源文件名 %(module)s:模块名 %(lineno)d:日志调用的行数 %(funcName)s:函数名 %(created)f:日志创建时间,time.time() %(asctime)s:日志创建时间,文本类型 %(msecs)d:日志创建时间的毫秒部分 %(relativeCreated)d:日志创建时间 - 加载日志模块的时间 的 毫秒数 %(thread)d:线程ID %(threadName)s:线程名 %(process)d:进程ID %(processName)s:进程名 %(message)s:日志消息
-
datefmt:日期格式化字符串,为None则使用ISO8601格式化,如:‘2010-01-01 08:03:26,870’
-
style:‘%’,‘{’ 或 ‘$’,3选一:
- ‘%’:默认是这个,使用python的 % 格式化 , 如: %(levelname)s
- ‘{’:使用 str.format格式化(django框架使用这个), 如:{levelname}
- ‘$’:使用类 string.Template 格式化,如:$levelname
-
BufferingFormatter:一种适于格式化批量记录的格式器,对每条记录都使用指定的格式器进行格式化
初始化参数:linefmt=None
linefmt:指定格式器,设置为None使用上述的默认Formatter
-
-
django内置的格式器
- ServerFormatter:根据status_code不同,将日志消息格化式成不同的颜色的消息
7. logging.LogRecord对象
每次logger记录日志时,都会自动创建该LogRecord实例
- 初始化参数:name,level,pathname,lineno,msg,args,exc_info,func = None,sinfo = None
说明:
- name - 用于记录此LogRecord表示的事件的记录器的名称
- level - 日志记录事件的数字级别(DEBUG是10,INFO是20,以此类推),并将转换为LogRecord的两个属性: 对于数值levelno和对应的级别名称levelname
- pathname - 进行日志记录调用的源文件的完整路径名
- lineno - 进行日志记录调用的源文件中的行号
- msg - 事件描述消息,可能是带有可变数据占位符的格式字符串
- args - 要合并到msg参数中以获取事件描述的可变数据
- exc_info - 包含当前异常信息的异常元组,或者None(没有可用的异常信息)
- func - 调用日志记录调用的函数或方法的名称
- sinfo - 表示当前线程中堆栈基础的堆栈信息的文本字符串,直到日志记录调用
-
方法
- getMessage:返回用户提供的参数与消息合并后的消息
-
属性
首先是第6点Formatters中内置格式器Formatters类的 fmt 属性中,描述的类似:%(name)s 中的 name 就是 该对象的属性
其他属性如下:
- args:元组或字典(只有一个参数是),用于合并日志消息
- exc_info:异常元组(类似:sys.exc_info),没有异常则是None
- msg:日志记录调用中传递的格式字符串,可以合并args生成日志消息
- stack_info:在当前线程中从堆栈底部堆栈帧信息(如果有的话),直到并包括记录调用的堆栈帧
示例如下:
LOGGING = {
'version': 1, # 固定值,现在只有这一个版本
'disable_existing_loggers': False, # 设置已存在的logger不失效
'loggers': {
'': {
'handlers': ['console'],
},
'django': {
'handlers': ['console'],
'propagate': True,
},
'django.request': {
'handlers': ['mail_admins'],
'level': 'ERROR',
'propagate': False,
},
'myproject.custom': {
'handlers': ['console', 'mail_admins'],
'level': 'INFO',
'filters': ['special']
}
}
}
说明:
配置了4个 logger, 分别对应2个不同的handler(console输出日志到控制台,mail_admins输出日志到邮件)
- ‘’:默认的记录器,不指定特定名称,那么就是使用这个记录器,没有配置level,那么就是处理所有级别的日志,传递所有级别的日志到console控制器
- django:传递所有级别的日志到console控制器
- django.request:django记录器的子记录器,处理ERROR级别及以上的日志,propagate设置为 False,表明不传播日志给 “django”,该logger传递日志到mail_admins控制器
- myproject.custom:处理INFO级别及以上的日志,应用了一个 special 的过滤器来过滤日志,传递日志到2个控制器([‘console’, ‘mail_admins’])处理
django框架有个默认的配置:DEFAULT_LOGGING,一旦配置了自己的LOGGING后,那么所有的默认的LOGGER全部都失效,失效不等于没有记录器了,而是说记录器不起作用了,即不会记录日志,也不会将日志传播给父记录器。因此你应该非常小心使用,因为你会感觉你丢了日志一样,可以手动设置同名的logger实现覆盖,如:
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'handlers': {
'file': {
'level': 'DEBUG',
'class': 'logging.FileHandler',
'filename': '/path/to/django/debug.log',
},
},
'loggers': {
# 覆盖了 django 记录器,所有django的记录日志最后全部写入到文件中
'django': {
'handlers': ['file'],
'level': 'DEBUG',
'propagate': True,
},
},
}
disable_existing_loggers默认是True,除非设置disable_existing_loggers为False,那么默认配置的记录器才会起作用
LOGGING = {
'disable_existing_loggers': False,
}
配置还可以使用系统变量,如下示例中读取 DJANGO_LOG_LEVEL 环境变量:
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'),
},
},
}
8. 项目中的日志应用
8.2 logger的简单使用
-
getLogger(_name_):记录器名使用模块名,这是基于每个模块过滤和处理日志记录调用,对应默认记录器,即名称为 ‘’ 的
-
getLogger(‘myproject.custom’):通过 “.” 符号分隔的方式,定义记录器名称的层次结构,‘myproject’ 是跟记录器, ‘myproject.custom’是子记录器,甚至 ‘myproject.custom.child’ 是孙辈的记录器。使用层次结构的作用是因为子记录器是可以传播日志记录给父记录器的,最终传播到根记录器。默认就是传播的,如果不想传播,那么需要在logger的配置中使用’propagate’: False
views视图
from django.shortcuts import render,HttpResponse
import logging
logger = logging.getLogger(__name__)
def logger_test(request):
# 日志级别从低到高
logger.debug('日志测试 debug 级别')
logger.info('日志测试 info 级别')
logger.warning('日志测试 warning 级别')
logger.error('日志测试 error 级别')
logger.critical('日志测试 critical 级别')
return HttpResponse('学习Django框架中的日志')
settings配置信息
# 日志配置
import os
BASE_LOG_DIR = os.path.join(BASE_DIR, "log")
LOGGING = {
'version': 1,
'disable_existing_loggers': False, # 是否对默认的日志文件进行覆盖
'formatters': {
# 一般应用文件
'standard': {
'format': '[%(levelname)s][%(asctime)s][%(filename)s:%(lineno)d:%(funcName)s] %(message)s'
},
# 应用于控制台
'simple':{
'format':'[%(levelname)s][%(asctime)s] %(message)s'
}
},
'filters': {
'require_debug_true': {
'()': 'django.utils.log.RequireDebugTrue',
},
},
'handlers': {
'console': {
'level': 'DEBUG',
'filters': ['require_debug_true'],
'class': 'logging.StreamHandler',
'formatter': 'simple'
},
'file': {
'level': 'INFO', # 最低级别。如果低于他,会显示在控制台中,但不保存在文件中
'class': 'logging.handlers.RotatingFileHandler',
'filename': os.path.join(BASE_LOG_DIR, "log_study.log"),
'maxBytes': 1024 * 1024 * 50, # 日志大小 50M
'backupCount': 3,
'formatter': 'standard',
'encoding': 'utf-8',
},
},
'loggers': {
'': {
# 部署到生产环境之后可以把'console'移除
'handlers': ['console', 'file'],
'level': 'DEBUG',
},
},
}
8.3 propagate使用
什么是progagate:
由于文件存在子父级,如果默认选择False,当保存到子级时,不进行向父级传播,只存在于子级。
# 日志配置
import os
BASE_LOG_DIR = os.path.join(BASE_DIR, "log")
print(os.path.join(BASE_LOG_DIR, "log_study.log"))
LOGGING = {
'version': 1,
'disable_existing_loggers': False, # 是否对默认的日志文件进行覆盖
'formatters': {
# 一般应用文件
'standard': {
'format': '[%(levelname)s][%(asctime)s][%(filename)s:%(lineno)d:%(funcName)s] %(message)s'
},
# 应用于控制台
'simple':{
'format':'[%(levelname)s][%(asctime)s] %(message)s'
}
},
'filters': {
'require_debug_true': {
'()': 'django.utils.log.RequireDebugTrue',
},
},
'handlers': {
'console': {
'level': 'DEBUG', # 控制台打印的最低级别
'filters': ['require_debug_true'],
'class': 'logging.StreamHandler',
'formatter': 'simple'
},
'file': {
'level': 'INFO', # 最低级别。保存在文件中
'class': 'logging.handlers.RotatingFileHandler',
'filename': os.path.join(BASE_LOG_DIR, "log_study.log"),
'maxBytes': 1024 * 1024 * 50, # 日志大小 50M
'backupCount': 3,
'formatter': 'standard',
'encoding': 'utf-8',
},
},
# 'loggers': {
# '': {
# # 部署到生产环境之后可以把'console'移除
# 'handlers': ['console', 'file'],
# 'level': 'DEBUG',
# },
# },
'loggers': {
'': {
# 部署到生产环境之后可以把'console'移除
'handlers': ['console','file'],
'level': 'DEBUG',
},
'logger_study.file': {
# 部署到生产环境之后可以把'console'移除
'handlers': ['file'],
'level': 'DEBUG',
'propagate':False,
},
},
}
views
from django.shortcuts import render,HttpResponse
import logging
def logger_propagate_test(request):
logger = logging.getLogger("logger_study.file")
# 日志级别从低到高
logger.debug('日志测试 debug 级别 propagate')
logger.info('日志测试 info 级别 propagate')
logger.warning('日志测试 warning 级别 propagate')
logger.error('日志测试 error 级别 propagate')
logger.critical('日志测试 critical 级别 propagate')
return HttpResponse('学习Django框架中的日志 propagate')
8.4 TimedRotatingFileHandler处理器
# 日志配置
import os
BASE_LOG_DIR = os.path.join(BASE_DIR, "log")
print(os.path.join(BASE_LOG_DIR, "log_study.log"))
LOGGING = {
'version': 1,
'disable_existing_loggers': False, # 是否对默认的日志文件进行覆盖
'formatters': {
# 一般应用文件
'standard': {
'format': '[%(levelname)s][%(asctime)s][%(filename)s:%(lineno)d:%(funcName)s] %(message)s'
},
# 应用于控制台
'simple':{
'format':'[%(levelname)s][%(asctime)s] %(message)s'
}
},
'filters': {
'require_debug_true': {
'()': 'django.utils.log.RequireDebugTrue',
},
},
'handlers': {
'console': {
'level': 'DEBUG', # 控制台打印的最低级别
'filters': ['require_debug_true'],
'class': 'logging.StreamHandler',
'formatter': 'simple'
},
'file': {
'level': 'DEBUG', # 最低级别。保存在文件中
'class': 'logging.handlers.RotatingFileHandler',
'filename': os.path.join(BASE_LOG_DIR, "log_study.log"),
# 'maxBytes': 1024 * 1024 * 50, # 日志大小 50M,如果超出,会新创建
'maxBytes':1024,
'backupCount': 3, # 最多创建几个文件,如果超出这个数量,会删掉之前的
'formatter': 'standard',
'encoding': 'utf-8',
},
'timed_file': {
# 这项值是修改的最多的
'level': 'INFO',
'class': 'logging.handlers.TimedRotatingFileHandler',
'filename': os.path.join(BASE_LOG_DIR, "logging_study_timed.log"),
'when': 's', # 秒
'interval': 10, # 几秒生成一个日志文件
'backupCount': 5, # 文件最大的个数
'formatter': 'standard',
'encoding': 'utf-8',
},
},
# 'loggers': {
# '': {
# # 部署到生产环境之后可以把'console'移除
# 'handlers': ['console', 'file'],
# 'level': 'DEBUG',
# },
# },
'loggers': {
'': {
# 部署到生产环境之后可以把'console'移除
'handlers': ['console','timed_file'],
'level': 'DEBUG',
},
'logger_study.file': {
# 部署到生产环境之后可以把'console'移除
'handlers': ['file'],
'level': 'DEBUG',
'propagate':False,
},
},
}
from django.shortcuts import render,HttpResponse
import logging
from django.core.servers.basehttp import WSGIRequestHandler
def logger_test(request):
logger = logging.getLogger(__name__)
# 日志级别从低到高
logger.debug('日志测试 debug 级别')
logger.info('日志测试 info 级别')
# for i in range(10):
logger.warning('日志测试 warning 级别')
logger.error('日志测试 error 级别')
logger.critical('日志测试 critical 级别')
return HttpResponse('学习Django框架中的日志')
8.5 SMTPHandler处理器——发送日志到邮箱
# 日志配置
import os
BASE_LOG_DIR = os.path.join(BASE_DIR, "log")
print(os.path.join(BASE_LOG_DIR, "log_study.log"))
LOGGING = {
'version': 1,
'disable_existing_loggers': False, # 是否对默认的日志文件进行覆盖
'formatters': {
# 一般应用文件
'standard': {
'format': '[%(levelname)s][%(asctime)s][%(filename)s:%(lineno)d:%(funcName)s] %(message)s'
},
# 应用于控制台
'simple':{
'format':'[%(levelname)s][%(asctime)s] %(message)s'
}
},
'filters': {
'require_debug_true': {
'()': 'django.utils.log.RequireDebugTrue',
},
},
'handlers': {
'console': {
'level': 'DEBUG', # 控制台打印的最低级别
'filters': ['require_debug_true'],
'class': 'logging.StreamHandler',
'formatter': 'simple'
},
'file': {
'level': 'DEBUG', # 最低级别。保存在文件中
'class': 'logging.handlers.RotatingFileHandler',
'filename': os.path.join(BASE_LOG_DIR, "log_study.log"),
# 'maxBytes': 1024 * 1024 * 50, # 日志大小 50M,如果超出,会新创建
'maxBytes':1024,
'backupCount': 3, # 最多创建几个文件,如果超出这个数量,会删掉之前的
'formatter': 'standard',
'encoding': 'utf-8',
},
'timed_file': {
# 这项值是修改的最多的
'level': 'INFO',
'class': 'logging.handlers.TimedRotatingFileHandler',
'filename': os.path.join(BASE_LOG_DIR, "logging_study_timed.log"),
'when': 's', # 秒
'interval': 10, # 几秒生成一个日志文件
'backupCount': 5, # 文件最大的个数
'formatter': 'standard',
'encoding': 'utf-8',
},
'smtp':{
'level': 'ERROR', # 发送邮件级别
'class': 'logging.handlers.SMTPHandler',
'formatter': 'standard',
'mailhost': ('smtp.qq.com', 25), # 用哪个邮箱,邮箱端口多少
'fromaddr': '95506@qq.com', # 发件人
'toaddrs': ['27124660@qq.com'],# 收件人
'subject': 'logging_study系统日志消息', # 主体
'credentials': ('13395506@qq.com', 'WHOQCGRAOOIBHH') # 开头服务时的密码
},
},
# 'loggers': {
# '': {
# # 部署到生产环境之后可以把'console'移除
# 'handlers': ['console', 'file'],
# 'level': 'DEBUG',
# },
# },
'loggers': {
'': {
# 部署到生产环境之后可以把'console'移除
'handlers': ['console','smtp'],
'level': 'DEBUG',
},
'logger_study.file': {
# 部署到生产环境之后可以把'console'移除
'handlers': ['file'],
'level': 'DEBUG',
'propagate':False,
},
},
}
8.6 自定义过滤器
import logging
class ExistWordFilter(logging.Filter):
def __init__(self, name='', exist_word=None):
super().__init__(name)
self.exist_word = exist_word
def filter(self, record):
if not self.exist_word or self.exist_word in record.msg:
return True
else:
return False
# 日志配置
import os
BASE_LOG_DIR = os.path.join(BASE_DIR, "log")
print(os.path.join(BASE_LOG_DIR, "log_study.log"))
LOGGING = {
'version': 1,
'disable_existing_loggers': False, # 是否对默认的日志文件进行覆盖
'formatters': {
# 一般应用文件
'standard': {
'format': '[%(levelname)s][%(asctime)s][%(filename)s:%(lineno)d:%(funcName)s] %(message)s'
},
# 应用于控制台
'simple':{
'format':'[%(levelname)s][%(asctime)s] %(message)s'
}
},
'filters': {
# 控制台debug输出
'require_debug_true': {
'()': 'django.utils.log.RequireDebugTrue',
},
# 控制台debug不进行输出:要再下面的console中配置filters
'require_debug_false': {
'()': 'django.utils.log.RequireDebugFalse',
},
# 自定义过滤器
'exist_word_filter':{
'()': 'logger_app.logtest.ExistWordFilter',
'exist_word':'测试', # 过滤的单词,必须存在这个,才能够过滤
}
},
'handlers': {
'console': {
'level': 'DEBUG', # 控制台打印的最低级别
'filters': ['require_debug_true',"exist_word_filter"],
'class': 'logging.StreamHandler',
'formatter': 'simple'
},
'file': {
'level': 'DEBUG', # 最低级别。保存在文件中
'class': 'logging.handlers.RotatingFileHandler',
'filename': os.path.join(BASE_LOG_DIR, "log_study.log"),
# 'maxBytes': 1024 * 1024 * 50, # 日志大小 50M,如果超出,会新创建
'maxBytes':1024,
'backupCount': 3, # 最多创建几个文件,如果超出这个数量,会删掉之前的
'formatter': 'standard',
'encoding': 'utf-8',
},
'timed_file': {
# 这项值是修改的最多的
'level': 'INFO',
'class': 'logging.handlers.TimedRotatingFileHandler',
'filename': os.path.join(BASE_LOG_DIR, "logging_study_timed.log"),
'when': 's', # 秒
'interval': 10, # 几秒生成一个日志文件
'backupCount': 5, # 文件最大的个数
'formatter': 'standard',
'encoding': 'utf-8',
},
'smtp':{
'level': 'ERROR', # 发送邮件级别
'class': 'logging.handlers.SMTPHandler',
'formatter': 'standard',
'mailhost': ('smtp.qq.com', 25), # 用哪个邮箱,邮箱端口多少
'fromaddr': '95506@qq.com', # 发件人
'toaddrs': ['27124660@qq.com'],# 收件人
'subject': 'logging_study系统日志消息', # 主体
'credentials': ('133955006@qq.com', 'WHOQCGRAOOIBHH') # 开头服务时的密码
},
},
# 'loggers': {
# '': {
# # 部署到生产环境之后可以把'console'移除
# 'handlers': ['console', 'file'],
# 'level': 'DEBUG',
# },
# },
'loggers': {
'': {
# 部署到生产环境之后可以把'console'移除
'handlers': ['console','smtp'],
'level': 'DEBUG',
},
'logger_study.file': {
# 部署到生产环境之后可以把'console'移除
'handlers': ['file'],
'level': 'DEBUG',
'propagate':False,
},
},
}
8.7测试record记录
from django.shortcuts import render
import logging
from django.core.servers.basehttp import WSGIRequestHandler
from django.http.response import HttpResponse
from logging.handlers import RotatingFileHandler
from logging import Filter
def logger_test(request):
logger = logging.getLogger(__name__)
# 日志级别从低到高
logger.debug('日志测试 debug 级别')
logger.info('日志测试 info 级别')
# for i in range(10):
logger.warning('日志测试 warning 级别')
logger.error('日志 error 级别')
logger.critical('日志 critical 级别')
# 测试默认的DEFAULT_LOGGING
logger_server = logging.getLogger('django.server')
logger_server.debug('logger_server debug 测试')
logger_server.info('logger_server info 测试-------------------')
logger_server.warning('logger_server warning 测试')
logger_server.error('logger_server error 测试')
return HttpResponse('学习Django框架中的日志')