logging⽇志输出乱码info_【Python】logging模块应⽤
编写 Python 程序,经常需要在控制台打印输出,来了解程序的实际执⾏情况。 常⽤的打印输出⽅法 print(),在项⽬内使⽤,有时就不那么⽅便: 1. 调试阶段使⽤ print,发布时需要注释或删除不必要的打印信息,⽆法过滤; 2. 部分应⽤程序后台执⾏,没有控制台实时显⽰,就需要保存⽇志到⽂件; 3. 如果需要格式化输⼊⽇志,则需要重复编写格式代码。 所以 Python 标准库出现了更加灵活、功能丰富的logging 模块,该模块定义函数和类,为应⽤程序和库实现了灵活的事件⽇志记录系统。 logging 模块化设计,主要包含四种组件:Loggers:记录器,提供应⽤程序代码能直接访问使⽤的接⼝;
Handlers:处理器,将记录器产⽣的⽇志发送⾄⽬的地;
Filters:过滤器,提供更好的粒度控制,决定哪些⽇志将会被输出;
Formatters:格式化器,设置⽇志内容的组成结构和消息字段。
⼯作原理
创建 logger 就相当于创建了⼀⽀虚拟的笔,可以创建多个,创建时需要设置⼀个默认输出等级,仅输出 ≥ 默认⽇志等级的信息; 指定Handler 控制 这个笔的输出⽬的地,可以只写⼊控制台,也可以只写⼊⽂件内,还可以同时在控制台与⽂件内都写,等等,根据业务⾃⼰定义,可以分别设置⽇志等级; 创建 Formatter 设置⽇志输出的结构、格式,供处理器使⽤;处理器的 Handler ⽇志经过 Formatter 渲染后,绑定到记录器 logger 内; 程序调⽤ logger 此时程序就可以根据需求去调⽤不同的 logger,⽐如仅输出控制台、写⼊⽇志等
等。 logger 记录器 logger = Logger(name) 通过 Logger(name) 函数实例化,⽤来创建记录器;多次调⽤ getLogger() 相同名称将始终返回对同⼀ Logger 对象的引⽤,也就是说 logger 是单例的。 logger.setLevel(level) 设置⽇志默认级别,如果不设置,默认级别为 warning logger.addHandler() | veHandler() 记录器可以绑定或移除不同的处理器,从⽽实现各种功能。⽐如:logger_a 仅输出到控制台;logger_b 仅把⽇志写⼊⽂件;logger_c 输⼊到控制台的同时写⼊⽂件;logger_d 发送邮
件给指定⽤户等等。 Handlers 处理器 将⽇志分发到不同的⽬的地,可以是⽂件、标准输出、邮件或者通过 socke、http 等协议发送到任何地⽅。 sh = logging.StreamHandler(stream=None) 标准输出 st
dout fh = logging.FileHandler( filename, mode='a', encoding=None, delay=False ) 将⽇志保存到磁盘⽂件,mode 代表写⼊模式,‘a’ 为追加 sh.setFormatter(color_formatter)
fh.setFormatter(formatter) 设置 formatter,定义⽇志输出结构、格式,⼀个处理器仅能绑定⼀个 formattor。这⾥列出的只是常⽤的两个处理器,还有其他⼀些 ,⽐如:
RotatingFileHandler:按照⽂件⼤⼩,⽣成多个⽇志⽂件;
TimeRotatingFileHandler:按照时间,每天⼀个新⽂件等;
SMTPHandler:⽇志发送邮箱;
...
Formatters 格式器 ft = logging.Formatter( fmt=None, datefmt=None, style=' %' ) datefmt :⽇期格式,默认样式:%Y-%m-%d %H:%M:%S style :是格式化风格符,默认是百分号;使⽤⽰例:[%(message)s],这是设置所有输出的⽇志⽤ [] 括起来,这个参数不⽤修改,修改没有任何意义。 formattor 的格式化参数还有很多,如图(源码 485 ⾏):
Filters 过滤器 filter = logging.Filter("test") logger.addFilter(filter) 上⾯代码设置过滤器内的记录器为 “test”,则仅输出“test” 记录器的⽇志; 如果没有创建过这个记录器,就不会输出任何⽇志;也可以对处理器设置过滤器,通过 addFilter 关联⽣效。
⽇志级别 系统默认提供了 6 个⽇志级别,对应不同使⽤场景,如下:
# logging.py __init__ line:89
CRITICAL = 50
FATAL = CRITICAL # 致命错误,程序崩溃
ERROR = 40 # 程序错误,部分功能不能使⽤
WARNING = 30 # 警告信息,可能出现⼀些错误
WARN = WARNING
INFO = 20 # 程序正常运⾏时输出信息
DEBUG = 10 # 程序实现过程中调试信息
NOTSET = 0
另外,记录器和处理器可以分别选择设置默认⽇志级别,逻辑如下:
如果没有给处理器指定⽇志级别,将使⽤记录器的⽇志级别;
如果没有给记录器指定⽇志级别,将使⽤默认 warning 级别;
如果记录器指定⽇志级别⼤于处理器指定⽇志级别,将以记录器⽇志级别为准;
如果记录器指定⽇志级别⼩于处理器指定⽇志级别,将以处理器⽇志级别为准。
Talk is cheap,show me the code.
import os
import logging
import colorlog
class Logger(object):
"""初始化⽇志记录器
usage:
# 默认记录器是 root,会输出到控制台且写⼊到⽇志⽂件 output.log
>>> logger1 = Logger().logger
>>> logger1.debug("this is debug message")
>>> import logging
>>> logging.warning("this is warning message")
# 创建记录器 test,会输出到控制台且写⼊到⽇志⽂件 test.log
# 可以设置 level 级别
>>> logger2 = Logger("test", level=logging.INFO).logger
>>> ("this is error message")
# 仅在控制台输出
>>> logger3 = Logger(Logger.CONSOLE).logger
>>> logger3.info("this is info message")
"""
CONSOLE = "console"
__colors_config = {
'DEBUG': 'white', # cyan white
'INFO': 'bold_blue',
'WARNING': 'yellow',
'ERROR': 'red',
'CRITICAL': 'bold_red',
}
__date_fmt = "%y%m%d %H:%M:%S"
__fmt = "[ %(levelname)1.1s %(asctime)s %(module)s:%(lineno)d ] %(message)s"
__color_fmt = '%(log_color)s[ %(levelname)1.1s %(asctime)s %(module)s:%(lineno)d ]%(black)s %(message)s'
__filepath = os.path.dirname(os.path.dirname(__file__)) + "/"
def __init__(self, filename=None, level=logging.DEBUG):
# 1. 创建记录器
self.logger = Logger(filename) # 不填则为 root
self.logger.setLevel(level)
# 定义输出格式
formatter = logging.Formatter(self.__fmt, datefmt=self.__date_fmt)
color_formatter = colorlog.ColoredFormatter(
fmt=self.__color_fmt,
datefmt=self.__date_fmt,
log_colors=self.__colors_config
)
# 2. 创建处理器并关联输出格式
# 如果没有给处理器指定⽇志级别,将使⽤记录器的⽇志级别
# 如果没有给记录器指定⽇志级别,那么会使⽤默认「warning」级别,处理器此时设置「debug」或「info」都不会输出 # 2.1 创建输出到控制台处理器
console_handler = logging.StreamHandler()
console_handler.setLevel(logging.INFO)
console_handler.setFormatter(color_formatter)
# 2.2 创建输出到⽂件处理器
file_handler = logging.FileHandler(
filename=self.__filepath + "output.log"
if filename is None or Logger.CONSOLE else self.__filepath + filename + ".log"
)
file_handler.setLevel(logging.INFO)
file_handler.setFormatter(formatter)
# 3. 记录器关联处理器
if filename == "console":
self.logger.addHandler(console_handler)
else:
self.logger.addHandler(console_handler)
self.logger.addHandler(file_handler)
# 4. 过滤器 -- 仅输出设置的记录器下的⽇志,也可以对处理器进⾏设置过滤器
# 这⾥设置为 test,就不会再输出⽇志了,因为只输出名为 "test" 记录器的⽇志
# filter_app = logging.Filter("test")
# self.logger.addFil(filter_app) # 关联过滤器
if __name__ == '__main__':
# logging(默认记录器 root) 默认输出
logging.info("this is default logging warning message")
# 修改默认记录器 root,格式化输出
logger1 = Logger().logger
logger1.info("this is logger(root) info message")
# 创建 console 记录器,仅输出到控制台
logger2 = Logger(Logger.CONSOLE).loggerpython单例模式
<("this is logger(console) error message")
# 创建 console 记录器,仅输出 ERROR 级别之上的⽇志
logger4 = Logger(Logger.CONSOLE, level=logging.ERROR).logger
logger4.info("logger4 > test info,You can't see me")
<("logger4 > test error")
注意,我的测试代码,创建了多个记录器,当同时运⾏时,会发现有的⽇志多次打印,这是由于 logger 对象是有⽗⼦关系的。
以 logger1 和 logger2 为例:如果创建 console 记录器,则它的⽗对象就是 root,上⾯由于也创建了 root 记录器,所以⽗对象会同时收到⽇志,造成 console 的 logger 对象打印两遍⽇志。
为了控制台的⽇志更美观、易于分辨,我加⼊了定义⽇志颜⾊的 formattor,⽤到了 colorlog 模块,效果如图:
以上就是关于 logging 模块,我的应⽤与理解,感谢阅读。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论