logging模块主要是方便我们用来记录日志信息的,下面将列出logging模块的使用。
LEVEL | value | describe |
---|---|---|
NOTEST | 0 | 不设置级别,按照父logger的级别显示日志,如果是root logger,那么就会显示所有的日志 |
DEBUG | 10 | 程序的详细debug信息,调试代码会用到 |
INFO | 20 | 确定程序是否按照正常的运行 |
WARNING | 30 | 程序发出警告,但是还能正常运行 |
ERROR | 40 | 程序发生错误,无法运行程序 |
CRITICAL(FATAL) | 50 | 程序出现致命错误,无法运行 |
import logging
def simple_using():
logging.debug('debug text')
logging.warning('warning text')
logging.info('info text')
logging.error('error text')
logging.critical('critical text')
上面的代码只会在控制台显示3条信息
WARNING:root:warning text
ERROR:root:error text
CRITICAL:root:critical text
这是因为logging默认的LEVEL
是WARNING
,logging只会显示级别大于等于WARNING
的日志信息。所以这里就只会显示这样三条。当然,我们也可以改变LEVEL。
这个函数是用来配置root logger的参数的。
def file_name_demo():
# 只有日志级别大于等于INFO的,才会被写入log文件中
logging.basicConfig(filename='log.log')
logging.debug('debug text')
logging.info('info text')
logging.warning('warning text')
logging.error('error text')
logging.critical('critical text')
运行这段函数,在当前目录下就会生成一个log.log的文件,里面会写入下面几条信息。
WARNING:root:warning text
ERROR:root:error text
CRITICAL:root:critical text
a
,也就是追加def file_mode_demo():
logging.basicConfig(filename='log.log', filemode='a')
# logging.basicConfig(filename='log.log', filemode='w')
logging.debug('debug text')
logging.info('info text')
logging.warning('warning text')
logging.error('error text')
logging.critical('critical text')
也就是每次写入日志都是添加到文件最后面,如果设置为w
,那么每次写入都会从头开始写入,就会覆盖前面的日志。
首先给出所有的日志格式
语法 | 释义 |
---|---|
%(name)s | logger的名字 |
%(levelno)s | 数字形式的日志级别 |
%(levelname)s | 文本形式的日志级别 |
%(pathname)s | 调用日志输出函数的模块的完整路径名,可能没有 |
%(filename)s | 调用日志输出函数的模块的文件名 |
%(module)s | 调用日志输出函数的模块名 |
%(funcName)s | 调用日志输出函数的函数名 |
%(lineno)d | 调用日志输出函数的语句所在的代码行 |
%(created)f | LogRecord的创建时间,也就是当前时间,time.time() |
%(msecs)d | LogRecord的创建时间的毫秒部分 |
%(relativeCreated)d | 输出日志信息的,自logger创建以来的毫秒数 |
%(asctime)s | 字符窜形式的当前时间,默认格式为"2003-07-08 19:30:43, 896", 都好后面是毫秒 |
%(thread)d | 线程ID,可能没有 |
%(threadName)s | 线程名,可能没有 |
%(process)d | 进程ID,可能没有 |
%(message)s | 用户输出的消息 |
def format_demo():
logging.basicConfig(level=logging.DEBUG,format='%(name)s %(levelname)s: %(asctime)s: %(message)s')
logging.debug('debug text')
logging.info('info text')
logging.warning('warning text')
logging.error('error text')
logging.critical('critical text')
显示信息为:
root DEBUG: 2019-09-05 16:01:01,746: debug text
root INFO: 2019-09-05 16:01:01,759: info text
root WARNING: 2019-09-05 16:01:01,759: warning text
root ERROR: 2019-09-05 16:01:01,759: error text
root CRITICAL: 2019-09-05 16:01:01,759: critical text
def datefmt_demo():
logging.basicConfig( level=logging.DEBUG, format='%(name)s %(levelname)s: %(asctime)s: %(message)s', datefmt='%Y-%m-%d %H:%M:%S %p')
logging.debug('debug text')
logging.info('info text')
logging.warning('warning text')
logging.error('error text')
logging.critical('critical text')
符号 | 描述 |
---|---|
%y | 两位数的年份表示 (00-99) |
%Y | 四位数的年份标书 |
%m | 月份(01-12) |
%d | 天(0-31) |
%H | 24小时制小时数(01-23) |
%I | 12小时制小时数(01-12) |
%M | 分钟(00-59) |
%S | 秒数(00-59) |
%a | 本地简化星期名称 |
%A | 本地完整星期名称 |
%b | 本地简化月份名称 |
%B | 本地完整月份名称 |
%c | 本地相应的日期表示和时间表示 |
%j | 年内的一天(001 - 366) |
%p | 本地AM或PM的等价符 |
%U | 一年中的星期数(00-53),星期天为星期的开始 |
%W | 一年中的星期数(00-53),星期一为星期的开始 |
%w | 星期(0-6),星期天为星期的开始 |
%x | 本地相应的日期表示 |
%X | 本地相应的时间表示 |
%Z | 当前时区的名称 |
%% | %号本身 |
有三种风格:%, {, $, 默认就是%这种,下面给出了三种风格的使用方法
def style_demo():
# % style
# logging.basicConfig(level=logging.DEBUG, format='%(name)s %(levelname)s: %(asctime)s: %(message)s', style='%')
# logging.debug('debug text')
# logging.info('info text')
# logging.warning('warning text')
# logging.error('error text')
# logging.critical('critical text')
# { style
# logging.basicConfig(level=logging.DEBUG, format='{name} {levelname}: {asctime}: {message}', style='{')
# logging.debug('debug text')
# logging.info('info text')
# logging.warning('warning text')
# logging.error('error text')
# logging.critical('critical text')
# $ style
logging.basicConfig(level=logging.DEBUG, format='$name $levelname: $asctime: $message', style='$')
logging.debug('debug text')
logging.info('info text')
logging.warning('warning text')
logging.error('error text')
logging.critical('critical text')
def level_demo():
logging.basicConfig(level=logging.DEBUG)
logging.debug('debug text')
logging.info('info text')
logging.warning('warning text')
logging.error('error text')
logging.critical('critical text')
def handles_demo():
# 将日志输出到终端的handle
stream_handle = logging.StreamHandler()
# 将日志输出到handles.log文件中的log
file_handle = logging.FileHandler(filename='handles.log')
logging.basicConfig(level=logging.DEBUG, handlers=[stream_handle, file_handle])
logging.debug('debug text')
logging.info('info text')
logging.warning('warning text')
logging.error('error text')
logging.critical('critical text')
handle是什么?
每条会显示的日志信息都会交给handle处理,由handle决定将日志存放到哪里,怎样存放。
所以上面的日志信息既会在终端显示也会存放到handles.log文件中。
在一个项目中,我们可能根据不同的情况打印不同的日志,这样的话我们就需要多个日志模型。而我们前面一直使用logging明显不够用了。这个时候我们就需要使用logger对象了。其实前面我们直接使用logging.debug这些的时候,使用的也是一个logger对象,这是root logger。也就是根logger,我们自己新建的logger,其实都是root logger 的子logger,如果没有设置level的话,就会使用父logger的level。
logging.getLogger([name])
: 得到一个Logger对象
logger.setLevel(logging.ERROR)
: 设置日志级别为ERROR,即只有日志级别大于等于ERROR的日志才会输出
logger.addHandler(handler_name)
: 为Logger实例增加一个处理器
logger.removeHandler(handler_name)
: 为Logger实例删除一个处理器
剩下的用法就和logging.debug,logging.info。。。这些用法一样了,只不过我们需要将logging改成logger
内置handle的使用
sys.stderr
。将日志输出到传入的流中sys.stderr:输出信息到终端
def stream_handler_demo():
logger = logging.getLogger('stream_handle')
# 设置logger的全局最低级别
logger.setLevel(logging.DEBUG)
# 设置handle
stream_h = logging.StreamHandler()
# 设置handler的最低级别
stream_h.setLevel(logging.DEBUG)
logger.addHandler(stream_h)
logger.debug('debug text')
logger.info('info text')
logger.warning('warning text')
logger.error('error text')
logger.critical('critical text')
上面的信息就会被打印在终端显示
def file_handle_demo():
logger = logging.getLogger('file_handle')
logger.setLevel(logging.DEBUG)
file_h = logging.FileHandler('file_handle.log', mode='a', encoding='utf-8')
file_h.setLevel(logging.DEBUG)
logger.addHandler(file_h)
logger.debug('debug text')
logger.info('info text')
logger.warning('warning text')
logger.error('error text')
logger.critical('critical text')
其实FileHandle底层也是使用的StreamHandle,传入了一个文件流进入。
和logging.basicConfig中参数配置format的参数一样。
import logging
def formatter_demo():
logger = logging.getLogger('formatter')
logger.setLevel(logging.DEBUG)
file_h = logging.FileHandler('formatter.log', mode='a', encoding='utf-8')
file_h.setLevel(logging.DEBUG)
formatter = logging.Formatter('%(name)s %(levelname)s: %(asctime)s: %(message)s', datefmt='%Y-%m-%d %H:%M:%S %p', style='%')
file_h.setFormatter(formatter)
logger.addHandler(file_h)
logger.debug('debug text')
logger.info('info text')
logger.warning('warning text')
logger.error('error text')
logger.critical('critical text')
logging.filter可以对日志信息进行过滤,让我们完成更复杂的存储。
首先我们定义一个myFilter类,继承自logging.Filter类。然后重写filter方法。
filter方法:
class myFilter(logging.Filter):
def filter(self, record):
if record.levelno >= 30:
return True
else:
return False
def filter_demo():
# 生成handle
stream_handle = logging.StreamHandler()
# 生成filter
filter = myFilter()
# handle中添加filter
stream_handle.addFilter(filter)
# 使用root logger
logging.basicConfig(level=logging.DEBUG, handlers=[stream_handle])
logging.debug('debug text')
logging.info('info text')
logging.warning('warning text')
logging.error('error text')
logging.critical('critical text')
也就是说,所有的日志都会先经过filter,再交给handle处理,我们就可以在filter中做一些过滤条件了。
有时候python自带的handle满足不了我们的需求,现在我的版本是python3.7,就只有一个StreamHandle和FileHandle这两个handle,但是在实际开发中,他们是远远满足不了我们的需求的,所以我们就可以自定义handle。
自定义handle我们只需要注意两个点。
class myHandle(logging.Handler):
def emit(self, record):
# 为了方便我们查看效果,这里我就打印了一下30个*
print("*"*30)
print(record)
def my_handle_demo():
# 给root logger添加我们自定义的handle
logging.basicConfig(handlers=[myHandle()])
logging.debug('debug text')
logging.info('info text')
logging.warning('warning text')
logging.error('error text')
logging.critical('critical text')
自定义Formatter我们需要注意下面几点:
import logging
import sys
class myHandle(logging.Handler):
def emit(self, record):
# 在这里我们可以使用self.format()方法得到我们定义的myFormatter中的format方法.
msg = self.format(record)
sys.stderr.write(msg)
class myFormatter(logging.Formatter):
def format(self, record):
return 'hello world\n'
def my_formatter_demo():
# 生产handle
handle = myHandle()
# 给handle设置formatter
handle.setFormatter(myFormatter())
# 给root logger 添加handle
logging.basicConfig(handlers=[handle])
logging.debug('debug text')
logging.info('info text')
logging.warning('warning text')
logging.error('error text')
logging.critical('critical text')
我们看到这个函数的名字我们也能大概猜到这个函数是干嘛的。从dict中读取logger的配置
from logging.config import dictConfig
import logging
def dict_config_demo():
config = {
'version': 1, # 这个参数只能为1,必选参数
'incremental': False, # 可选参数,默认为False。如果这里定义的对象已经存在,那么这里对这些对象的定义是否应用到已存在的对象上。值为False表示,已存在的对象将会被重新定义。
'disable_existing_loggers': False, # 可选参数,默认值为True。该选项用于指定是否禁用已存在的日志器loggers,如果incremental的值为True则该选项将会被忽略
# 定义filters,每个键都为一个filter
'filters': {
# filter的名字,下面handle出会使用到
'filter1': {
# 必选项,构造filter的类,必须是logging.Filter或者其子类,而且必须为完整路径
'()': 'logging.Filter',
},
'filter2': {
'()': 'logging.Filter',
},
},
# 定义的formatters
'formatters': {
# formatters的名称,handle处会使用到
'formatter1': {
# formatter的类,必选参数,必须为logging.Formatter或者其子类
'()': 'logging.Formatter',
# Formatter的format参数
'format': '[{name}] {message}',
# Formatter的style参数
'style': '{',
# Formatter的datefmt参数
'datefmt': '%Y-%m-%d %H:%M:%S %p'
}
},
# 定义handle
'handlers': {
# handle的名字,下面logger处使用
'handle0': {
# handle的level
'level': 'INFO',
# 指定handle的filter,这里的就是用到了上面的filter1
'filters': ['filter1'],
# 指定handle的class
'class': 'logging.StreamHandler',
},
'handle1': {
'level': 'INFO',
'class': 'logging.StreamHandler',
# 指定formatter,上面的formatter1
'formatter': 'formatter1',
},
'handle2': {
'level': 'ERROR',
'filters': ['filter1'],
'class': 'logging.FileHandler',
'filename':'dict.txt'
}
},
'loggers': {
# logger的name
'logger1': {
# 指定logger的handles
'handlers': ['handle0', 'handle1'],
# 指定logger的级别
'level': 'ERROR',
# 可选参数,是否将logger消息传递给parent logger,值为True或者False
'propagate': False,
},
'logger2': {
'handlers': ['handle2'],
'level': 'INFO',
# 可选参数,是否将logger消息传递给parent logger,值为True或者False
'propagate': False,
},
},
'root': {
'handlers': ['handle0', 'handle1'],
'level': 'INFO',
},
}
# 使用配置创建logger
dictConfig(config=config)
# 得到logger1这个logger
logger = logging.getLogger('logger1')
logger.debug('debug text')
logger.info('info text')
logger.warning('warning text')
logger.error('error text')
logger.critical('critical text')
上面,我们就通过dict来配置了logger,一般这种方法也是最常用的
总结: