logging 日志模块
简单应用:
import logging
logging.warning('警告!')
# 输出结果:
WARNING:root:警告!
1. 日志等级
- debug 调试 10 最详细的日志信息,典型应用场景是 问题诊断
- info 普通信息 20 信息详细程度仅次于DEBUG,用于确认一切都是按照我们预期的那样进行工作
- warning 警告信息 30 当某些不期望的事情发生时记录的信息,但是此时应用程序还是正常运行的
- error 错误信息 40 由于一个更严重的问题导致某些功能不能正常运行时记录的信息
- critical 严重错误信息 50 当发生严重错误,导致应用程序不能继续运行时记录的信息
2. 日志形式
一条日志信息一般包括以下几点:
- 事件发生的时间
- 事件发生的位置
- 事件的严重程度 – 日志级别(DEBUG -> INFO -> WARNING -> ERROR -> CRITICAL)
- 事件的内容
2.1 logging模块的四大组件
组件 | 说明 |
---|---|
Logger | 日志记录器:提供应用程序代码直接使用的接口 |
Handler | 日志处理器:用于将日志记录发送到指定的目的位置 |
Filter | 日志过滤器:提供更细粒度的日志过滤功能,用于决定哪些日志记录将会被输出(其它的日志记录将会被忽略) |
Formatter | 日志格式化:用于控制日志信息的最终输出格式 |
这些组件之间的关系描述:
- 不同的处理器(handler)可以将日志输出到不同的位置;
- 日志器(logger)可以设置多个处理器(handler)将同一条日志记录输出到不同的位置;
- 每个处理器(handler)都可以设置自己的过滤器(filter)实现日志过滤,从而只保留感兴趣的日志;
- 每个处理器(handler)都可以设置自己的格式器(formatter)实现同一条日志以不同的格式输出到不同的地方。
简单点说就是:日志器(logger)是入口,真正干活儿的是处理器(handler),处理器(handler)还可以通过过滤器(filter)和格式器(formatter)对要输出的日志内容做过滤和格式化等处理操作。
2.1.1 Logger日志记录器
Logger对象有3个任务要做:
- 向应用程序代码暴露几个方法,使应用程序可以在运行时记录日志消息;
- 基于日志严重等级(默认的过滤设施)或filter对象来决定要对哪些日志进行后续处理;
- 将日志消息传送给所有感兴趣的日志handlers。
Logger对象最常用的方法分为两类:配置方法 和 消息发送方法
最常用的配置方法如下:
方法 | 描述 |
---|---|
Logger.setLevel() | 设置日志器将会处理的日志消息的最低严重级别 |
Logger.addHandler() 和 Logger.removeHandler() | 为该logger对象添加 和 移除一个handler对象 |
Logger.addFilter() 和 Logger.removeFilter() | 为该logger对象添加 和 移除一个filter对象 |
logger对象配置完成后,可以使用下面的方法来创建日志记录:
方法 | 描述 |
---|---|
Logger.debug(), Logger.info(), Logger.warning(), Logger.error(), Logger.critical() | 创建一个与它们的方法名对应等级的日志记录 |
Logger.exception() | 创建一个类似于Logger.error()的日志消息 |
Logger.log() | 需要获取一个明确的日志level参数来创建一个日志记录 |
一个Logger对象呢?一种方式是通过Logger类的实例化方法创建一个Logger类的实例,但是我们通常都是用第二种方式–logging.getLogger()方法。
logging.getLogger()方法有一个可选参数name,该参数表示将要返回的日志器的名称标识,如果不提供该参数,则其值为’root’。若以相同的name参数值多次调用getLogger()方法,将会返回指向同一个logger对象的引用。
多次使用注意不能创建多个logger,否则会出现重复输出日志现象。
2.1.2 Handler 日志处理器
Handler对象的作用是(基于日志消息的level)将消息分发到handler指定的位置(文件、网络、邮件等)。Logger对象可以通过addHandler()方法为自己添加0个或者更多个handler对象。比如,一个应用程序可能想要实现以下几个日志需求:
- 1)把所有日志都发送到一个日志文件中;
- 2)把所有严重级别大于等于error的日志发送到stdout(标准输出);
- 3)把所有严重级别为critical的日志发送到一个email邮件地址。这种场景就需要3个不同的handlers,每个handler复杂发送一个特定严重级别的日志到一个特定的位置。
Handler.setLevel(lel):指定被处理的信息级别,低于lel级别的信息将被忽略
Handler.setFormatter():给这个handler选择一个格式
Handler.addFilter(filt)、Handler.removeFilter(filt):新增或删除一个filter对象
需要说明的是,应用程序代码不应该直接实例化和使用Handler实例。因为Handler是一个基类,它只定义了素有handlers都应该有的接口,同时提供了一些子类可以直接使用或覆盖的默认行为。下面是一些常用的Handler:
Handler | 描述 |
---|---|
logging.StreamHandler | 将日志消息发送到输出到Stream,如std.out, std.err或任何file-like对象。 |
logging.FileHandler | 将日志消息发送到磁盘文件,默认情况下文件大小会无限增长 |
logging.handlers.RotatingFileHandler | 将日志消息发送到磁盘文件,并支持日志文件按大小切割 |
logging.hanlders.TimedRotatingFileHandler | 将日志消息发送到磁盘文件,并支持日志文件按时间切割 |
logging.handlers.HTTPHandler | 将日志消息以GET或POST的方式发送给一个HTTP服务器 |
logging.handlers.SMTPHandler | 将日志消息发送给一个指定的email地址 |
logging.NullHandler | 该Handler实例会忽略error messages,通常被想使用logging的library开发者使用来避免’No handlers could be found for logger XXX’信息的出现。 |
2.1.3 Formater 日志格式化
Formater对象用于配置日志信息的最终顺序、结构和内容。与logging.Handler基类不同的是,应用代码可以直接实例化Formatter类。另外,如果你的应用程序需要一些特殊的处理行为,也可以实现一个Formatter的子类来完成。
Formatter类的构造方法定义如下:
logging.Formatter.__init__(fmt=None, datefmt=None, style='%')
可见,该构造方法接收3个可选参数:
- fmt:指定消息格式化字符串,如果不指定该参数则默认使用message的原始值
- datefmt:指定日期格式字符串,如果不指定该参数则默认使用"%Y-%m-%d %H:%M:%S"
- style:Python 3.2新增的参数,可取值为 ‘%’, ‘{‘和 ‘$’,如果不指定该参数则默认使用’%’
一般直接用logging.Formatter(fmt, datefmt)
日志格式参数:
| %(name)s Name of the logger (logging channel) 记录器的名称
| %(levelno)s Numeric logging level for the message (DEBUG, INFO,
| WARNING, ERROR, CRITICAL) 数字形式的日志记录级别
| %(levelname)s Text logging level for the message ("DEBUG", "INFO",
| "WARNING", "ERROR", "CRITICAL") 日志记录级别的文本名称
| %(pathname)s Full pathname of the source file where the logging
| call was issued (if available) 执行日志记录调用的源文件的路径名称
| %(filename)s Filename portion of pathname 执行日志记录调用的源文件的文件名称
| %(module)s Module (name portion of filename) 执行日志记录调用的模块名称
| %(lineno)d Source line number where the logging call was issued
| (if available) 执行日志记录调用的行号
| %(funcName)s Function name 执行日志记录调用的函数名称
| %(created)f Time when the LogRecord was created (time.time()
| return value) 执行日志记录的时间
| %(asctime)s Textual time when the LogRecord was created 日期和时间
| %(msecs)d Millisecond portion of the creation time 毫秒部分
| %(relativeCreated)d Time in milliseconds when the LogRecord was created,
| relative to the time the logging module was loaded
| (typically at application startup time)
| %(thread)d Thread ID (if available) 线程ID
| %(threadName)s Thread name (if available) 线程名称
| %(process)d Process ID (if available) 进程ID
| %(message)s The result of record.getMessage(), computed just as
| the record is emitted 记录的消息
3. 日志记录一般流程
1、创建一个logger
2、设置下logger的日志的等级
3、创建合适的Handler(FileHandler要有路径)
4、设置下每个Handler的日志等级
5、创建下日志的格式
6、向Handler中添加上面创建的格式
7、将上面创建的Handler添加到logger中
8、打印输出logger.debug\logger.info\logger.warning\logger.error\logger.critical
3.1 示例
import os
import logging
from logging import StreamHandler, FileHandler, Formatter, Filter
def logs(*args):
log_name, path, msg = args
# 拼接写入路径和文件
filename = os.path.join(path, '{}_log.txt'.format(log_name))
# 设置日志名
logger = logging.getLogger(log_name)
# 设置记录器的信息等级:DEBUG -> INFO -> WARNING -> ERROR -> CRITICAL
logger.setLevel(logging.INFO) # 记录器可以记录INFO及以上等级信息
# 创建日志处理器1
handler1 = StreamHandler() # 流处理器,标准输出控制台
# 设置级别和格式
handler1.setLevel(logging.INFO)
handler1.setFormatter(Formatter(fmt='[ %(asctime)s - %(levelname)s] %(message)s', datefmt='%Y-%m-%d %H:%M:%S'))
# 创建日志处理器2
handler2 = FileHandler(filename, encoding='utf-8') # 输出至文件
handler2.setLevel(logging.INFO)
handler2.setFormatter(Formatter(
fmt="[ %(asctime)s - %(levelname)s > line %(lineno)s at %(pathname)s > ] %(message)s",
datefmt='%Y-%m-%d %H:%M:%S'
))
# 向记录器添加处理器
logger.addHandler(handler1)
logger.addHandler(handler2)
# # 输出信息
logger.info(msg)
# return logger
if __name__ == '__main__':
path = r'D:\02_project\Test\pingmesh'
logs('om', path, '天气太热')
3.2 logging.config
实现日志记录
import os
import **logging.config**
def logging_config():
# 定义字典
LOGGING_CONFIG = {
"version": 1,
"disable_existing_logger": True, # 删除已存在其他日志的Handler
"formatters": {
"standard": {
# "format": "{levelname} {asctime} {module} {process:d} {thread:d} {message}",
"format": "{levelname} {asctime} {filename} ----> {message}",
"style": "{",
},
"simple": {
"format": "%(levelname)s %(message)s",
"style": "%",
},
"test": {
"format": "$levelname $message",
"style": "$",
},
},
"handlers": {
"console": {
"class": "logging.StreamHandler",
"formatter": "simple",
},
"sms_file": {
"class": "logging.handlers.TimedRotatingFileHandler",
"formatter": "standard",
"filename": os.path.join('logs', 'sms.log'),
"when": 'D', # 根据天拆分日志
"interval": 1, # 1天
"backupCount": 3, # 备份个数
"encoding": "utf-8",
},
# "error_file": {
# "class": "logging.handlers.TimedRotatingFileHandler",
# "formatter": "standard",
# "filename": os.path.join('logs', 'error.log'),
# "when": 'D', # 根据天拆分日志
# "interval": 1, # 1天
# "backupCount": 3, # 备份个数
# "encoding": "utf-8",
# },
},
"loggers": {
"sms": {
"handlers": ["sms_file", ], # 意味着通过sms的loggers,写日志时, 级别 -> info 且 会写入到sms_file文件中
"level": "INFO",
"propagate": False # 通过sms写日志时,同时也会找到他的父亲
},
# "error": {
# "handlers": ["error_file", ],
# "level": "ERROR",
# "propagate": True
# },
},
# "root": {
# "handlers": ["console", ],
# "level": "ERROR",
# "propagate": True
# },
}
# 2.根据自定义对logging进行配置
logging.config.dictConfig(LOGGING_CONFIG)
return logging
if __name__ == '__main__':
logging = logging_config()
# 3.写日志
logger_object = logging.getLogger("sms")
logger_object.info("今天太热了")
# logger_object = logging.getLogger("error")
# logger_object.error(666666)
注意:
存在一个问题:日志文件不能修改或删除,否则就无法继续记录日志