日志级别
日志级别Level | 数值 |
---|---|
CRITICAL | 50 |
ERROR | 40 |
WARNING | 30,默认级别 |
INFO | 20 |
DEBUG | 10 |
NOSET | 0 |
日志级别指的是产生日志的事件的严重程度.
设置一个级别后,严重程度低于设置值的日志消息将被忽略
格式化字符串
属性名 | 格式 | 描述 |
---|---|---|
日志消息内容 | %(message)s | 当调用Formatter.format 是设置 |
asctime | %(asctime)s | 创建LogRecord时的可读时间, 默认格式:2021-06-17 16:33:45.145 |
函数名 | %(funcName)s | 日志调用所在函数名 |
日志级别名称 | %(levelname)s | DEBUG, INFO, WARNING, ERROR, CRITICAL |
级别数字 | %(levelno)s | 消息级别的数字:DEBUG, INFO, WARNING, ERROR, CRITICAL |
行号 | %(lineno)d | 日志调用说在源码行号 |
模块 | %(module)s | 模块, filename的名字部分 |
进程ID | %(process)d | 进程ID |
线程ID | %(thread)d | 线程ID |
进程名称 | %(processName)s | 进程名称 |
线程名称 | %(threadName)s | 线程名称 |
logger名称 | %(name) | logger名称 |
import logging
import threading
# 设置日志输出格式
FORMAT = '%(asctime)s %(levelname)s %(thread)d %(message)s'
logging.basicConfig(level=logging.INFO,
format=FORMAT,
datefmt='%Y-%m-%d %H:%M:%S')
def add(x, y):
logging.warning('{}'.format(threading.current_thread()))
logging.info('%s %s %s', x, y, x + y)
# 这里可以使用C风格的 info('%s %s %s', x, y, x + y)
自定义字段
FORMAT = '%(asctime)s %(levelname)s %(thread)d %(message)s %(school)s'
# 添加了自定义school
logging.basicConfig(level=logging.INFO,
format=FORMAT,
datefmt='%Y-%m-%d %H:%M:%S')
# 日志输出, 通过字典传入
logging.info('This Test Log', extra={'school': 'Beijixs'})
输出到文件
FORMAT = '%(asctime)s %(levelname)s %(thread)d %(message)s'
logging.basicConfig(level=logging.INFO,
format=FORMAT,
filemode='a', # 输出文件模式, a追加(默认), w覆盖
filename='test.log', # 输出到文件
datefmt='%Y-%m-%d %H:%M:%S')
logging.info('This is test log')
logger类
构造
使用工厂方法构造一个logger
import logging
logger = logging.getLogger(name="Main")
# getLogger(name=名称)
print(logger.name)
logger是层次结构的, 使用
.
号分割, 如a
, a.b
import logging
logger = logging.getLogger(name="Main")
# getLogger(name=名称)
print(logger.name)
logger.info('Main 记录日志')
logger.parent.info('root 记录日志')
logger 的属性和方法
方法
getEffectiveLevel()
: 获取当前logger
的有效级别setLevel(int)
: 设置当前logger
的级别
logger = logging.getLogger(__name__)
logger.setLevel(logging.WARN)
def add(x, y):
logger.warning('{}'.format(threading.current_thread()))
logger.info('%s %s %s', x, y, x + y)
logger.debug('DEBUG log')
logger.error('ERROR log')
Handler
Handler
控制日志信息的输出目的地,可以是控制台, 文件.可以单独设置level
可以单独设置格式
可以设置过滤器
举例
import time
import logging
import threading
FORMAT = '%(asctime)s %(name)s %(levelname)s %(thread)d %(message)s'
logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)
# 终端输出处理器
hdl = logging.StreamHandler()
hdl.setFormatter(logging.Formatter(FORMAT, datefmt='%Y-%m-%d %H:%M:%S'))
hdl.setLevel(logging.INFO)
# 文件输出处理器
f_hdl = logging.FileHandler('test.log', encoding='utf8')
f_hdl.setLevel(logging.DEBUG)
f_hdl.setFormatter(logging.Formatter(FORMAT, datefmt='%Y-%m-%d %H:%M:%S'))
logger.addHandler(hdl) # 可以添加多个handler
logger.addHandler(f_hdl)
def add(x, y):
logger.warning('{}'.format(threading.current_thread()))
logger.info('%s %s %s', x, y, x + y)
logger.debug('DEBUG log')
logger.error('ERROR log')
logger.critical('CRITICAL log')
add(54, 89)
# logger的日志信息会向上传递
# 父logger如果有处理该级别日志的能力将会继续处理
"""
当前logger能够处理级别的日志, 当前logger会把消息发送给自己的handler, 然后将该消息传递给父logger, 父logger如果能够处理就继续处理并传递给父,直到不能处理
"""
handler的日志级别受到logger的级别限制, 假如: logger的级别为INFO, handler最高也就只能输出到INFO级别的日志, 就算设置为DEBUG,handler也只能记录到INFO
总结
- 每一个
Logger
实例的level
如同入口,让水流进来,如果这个门槛太高,信息就进不来 - 如果
level
没有设置, 就使用父logger
的, 以此类推.直到root
都还没有找到级别,就直接继承root
的,默认是WARNING
- 在某个
logger
上产生某种级别的消息, 首先和logger
的level
检查, 如果消息level
低于logger
的EffectiveLevel
有效级别, 消息丢弃. 不会继续传递给父logger
.如果通过(级别大于等于)检出后, 且propagate
为True
, 消息交给logger
的所有的handler
进行处理, 如果没有handler
或以及被handler
处理过的, 该消息会向上传递给父logger
处理 - 父
logger
拿到消息, 会重复第三天的过程, 直至消息被丢弃或到root
logger
实例化的propagate
属性为True
即允许向父传递logger
消息
Formatter
logging
的Fromatter
类, 它允许指定某个格式的字符串. 如果提供None
, 那么%(message)s
会被作为默认值.
formatter = logging.Formatter('%(asctime)s %(name)s %(levelname)s %(thread)d %(message)s', datefmt='%Y-%m-%d %H:%M:%S')
hdl.setFromatter(formatter)
Filter
可以为Handler
添加一个过滤器, 所以过滤器会影响某一个handler
, 不会影响整个流程
filter = logging.Filter(__name__) # logging.Filter(name=logger名称)
# 如果name为空, 那么所有消息都可以通过
# 如果设置了name, 那么就只有设置的logger name会通过
logger.addFilter(filter) # 可以多添加几个filter
logging
是线程安全的.