python 日志库logging

日志级别

NOTSET	(0)
DEBUG (10)
INFO (20)
WARNING (30)
ERROR (40)
CRITICAL (50)

logging 输入日志时,会输出>= 设置的日志级别的日志,例如设置为DEBUG,则会输出DEBUG、INFO、WARNING、ERROR、CRITICAL级别的日志。默认的日志级别为WARNING。

Logging流程

Logger:日志
LogRecord:日志记录器
Handler:处理器(将日志发送到哪里)
Filter:过滤器,决定输入哪些日志
Formatter:格式化器,日志布局

logging工作流程

1、判断 Logger 对象对于设置的级别是否可用,如果可用,则往下执行,否则,流程结束。
2、创建 LogRecord 对象,如果注册到 Logger 对象中的 Filter 对象过滤后返回 False,则不记录日志,流程结束,否则,则向下执行。
3、LogRecord 对象将 Handler 对象传入当前的 Logger 对象,(图中的子流程)如果 Handler 对象的日志级别大于设置的日志级别,再判断注册到 Handler 对象中的 Filter 对象过滤后是否返回 True 而放行输出日志信息,否则不放行,流程结束。
4、如果传入的 Handler 大于 Logger 中设置的级别,也即 Handler 有效,则往下执行,否则,流程结束。
5、判断这个 Logger 对象是否还有父 Logger 对象,如果没有(代表当前 Logger 对象是最顶层的 Logger 对象 root Logger),流程结束。否则将 Logger 对象设置为它的父 Logger 对象,重复上面的 3、4 两步,输出父类 Logger 对象中的日志输出,直到是 root Logger 为止。

组件详细介绍

1.Logger

Logger是一个树形层级结构,输出信息之前都要获得一个Logger(如果没有显示的获取则自动创建并使用root Logger,如第一个例子所示)。
logger = logging.getLogger() 返回一个默认的Logger也即root Logger,并应用默认的日志级别、Handler和Formatter设置。
当然也可以通过Logger.setLevel(lel)指定最低的日志级别,可用的日志级别有:
logging.DEBUG、logging.INFO、logging.WARNING、logging.ERROR、logging.CRITICAL。
Logger.debug()、Logger.info()、Logger.warning()、Logger.error()、Logger.critical()
输出不同级别的日志,只有日志等级大于或等于设置的日志级别的日志才会被输出。

logger1 = logging.getLogger('mylogger')  
logger1.setLevel(logging.DEBUG)  
logger2 = logging.getLogger('mylogger')  
logger2.setLevel(logging.INFO)  
logger3 = logging.getLogger('mylogger.child1') #创建了(root.)mylogger.child1
logger4 = logging.getLogger('mylogger.child1.child2') #创建了(root.)mylogger.child1.child2
logger5 = logging.getLogger('mylogger.child1.child2.child3') #创建了(root.)mylogger.child1.child2.child3

2.Handler

Handler对象负责发送相关的信息到指定目的地,有几个常用的Handler方法:
Handler.setLevel(lel): 指定日志级别,低于lel级别的日志将被忽略
Handler.setFormatter(): 给这个handler选择一个Formatter
Handler.addFilter(filt)、Handler.removeFilter(filt):新增或删除一个filter对象

可以通过addHandler()方法为Logger添加多个Handler:
有多中可用的Handler:

logging.StreamHandler 可以向类似与sys.stdout或者sys.stderr的任何文件对象(file object)输出信息
logging.FileHandler 用于向一个文件输出日志信息
logging.handlers.RotatingFileHandler 类似于上面的FileHandler,但是它可以管理文件大小。
当文件达到一定大小之后,它会自动将当前日志文件改名,然后创建一个新的同名日志文件继续输出
logging.handlers.TimedRotatingFileHandler 和RotatingFileHandler类似,不过,它没有通过判断文件大小来决定何时重新创建日志文件,而是间隔一定时间就自动创建新的日志文件
logging.handlers.SocketHandler 使用TCP协议,将日志信息发送到网络。
logging.handlers.DatagramHandler 使用UDP协议,将日志信息发送到网络。
logging.handlers.SysLogHandler 日志输出到syslog
logging.handlers.NTEventLogHandler 远程输出日志到Windows NT/2000/XP的事件日志
logging.handlers.SMTPHandler 远程输出日志到邮件地址
logging.handlers.MemoryHandler 日志输出到内存中的制定buffer
logging.handlers.HTTPHandler 通过"GET"或"POST"远程输出到HTTP服务器

fh = logging.FileHandler('/tmp/test.log')   
ch = logging.StreamHandler()  

logger.addHandler(fh)  
logger.addHandler(ch)  

3.Formatter

Formatter对象设置日志信息最后的规则、结构和内容,默认的时间格式为%Y-%m-%d %H:%M:%S。

#定义Formatter  
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')  
#为Handler添加Formatter  
fh.setFormatter(formatter)  

format参数中可能用到的格式化串:
%(name)s Logger的名字
%(levelno)s 数字形式的日志级别
%(levelname)s 文本形式的日志级别
%(pathname)s 调用日志输出函数的模块的完整路径名,可能没有
%(filename)s 调用日志输出函数的模块的文件名
%(module)s 调用日志输出函数的模块名
%(funcName)s 调用日志输出函数的函数名
%(lineno)d 调用日志输出函数的语句所在的代码行
%(created)f 当前时间,用UNIX标准的表示时间的浮 点数表示
%(relativeCreated)d 输出日志信息时的,自Logger创建以 来的毫秒数
%(asctime)s 字符串形式的当前时间。默认格式是 “2003-07-08 16:49:45,896”。逗号后面的是毫秒
%(thread)d 线程ID。可能没有
%(threadName)s 线程名。可能没有
%(process)d 进程ID。可能没有
%(message)s 用户输出的消息

4.Filter

限制只有满足过滤规则的日志才会输出。
比如我们定义了filter = logging.Filter(‘a.b.c’),并将这个Filter添加到了一个Handler上,
则使用该Handler的Logger中只有名字带a.b.c前缀的Logger才能输出其日志。

# 只输出mylogger.child1.child2的日志
filter = logging.Filter('mylogger.child1.child2')  
fh.addFilter(filter)  

日志输出格式

级别:Logger实例名称:日志内容

定义Log两种方法

第一种:就是实例化logger = logging.logger 然后手动给logger添加addHandler, addFilter, handler.setFormatter 添加格式,这样的形式来获取logger
第二种:就是使用 logging.config.dictConfig 来从配置文件生成logger

# 获取root logger 
root_logger = logging.getLogger() 
print 'root logger', root_logger, id(root_logger) 
root_logger = logging.root 
print 'root logger', root_logger, id(root_logger)

#结果
root logger <logging.RootLogger object at 0x7f7aa0fdd6d0> 140164663727824
root logger <logging.RootLogger object at 0x7f7aa0fdd6d0> 140164663727824
# 他们是同一个root logger
import logging
# 没有创建logger, 默认是root logger, 直接打印在屏幕
logging.basicConfig(level=logging.DEBUG, format='%(asctime)s:%(name)s:%(levelname)s:%(message)s')
# logging.warning('this is warning')
# logging.info('this is info')

                                                                                                       
logger = logging.getLogger('apps')
apps_handler = logging.FileHandler(filename="apps.log")
logger.addHandler(apps_handler)
logger.setLevel(logging.DEBUG)
logger.info('shis')
print logger.handlers
print logger

上面流程讲解:

首先 logging.basicConfig 配置的是root logger 的StreamHandler 的格式,即打印在终端的内容
然后新的apps logger 都是root logger的子,输出的时候,同时会输出到root logger, 除非 logger.propagate = False。
所以如果没有 logging.basicConfig这个对root logger的配置,app logger就会只发送内容到自己的handlers

其他参考代码

import logging
# 没有创建logger, 默认是root logger, 直接打印在屏幕
# root logger 默认的level 是warning,所以这里设置成debug,才能打印info的日志
# 设置了root logger的format,包括时间,logger名字,levelname等
logging.basicConfig(level=logging.DEBUG, format='%(asctime)s:%(name)s:%(levelname)s:%(message)s')
logging.warning('this is warning')
logging.info('this is info')

"""
#输出:
2018-01-30 15:50:28,761:root:WARNING:this is warning
2018-01-30 15:50:28,761:root:INFO:this is info
"""

# 创建一个新的apps 的logger, 如果logger不设置,就会用root logger那套(打印到屏幕和上面的格式)
# 因为它是会默认传播到祖先logger
logger = logging.getLogger('apps')
logger.setLevel(logging.DEBUG)
# 是否传播这个日志到祖先logger, 如果设置了False 就不会传到root logger(祖先Logger)的
# 默认StreamHandler那里, 也就是不会打印在页面上
logger.propagate = False
# 添加handler, 决定日志落地到哪里,可以多个
# 这个是记录在文件的Handler
apps_handler = logging.FileHandler(filename="apps.log")
# 设置这个handler的处理格式, 实例化一个Formatter对象
apps_formatter = logging.Formatter('%(asctime)s:%(name)s:%(levelname)s:%(message)s')
apps_handler.setFormatter(apps_formatter)
logger.addHandler(apps_handler)
# 日志会打印到apps.log, 并且不会输出到屏幕(如果logger.propagate=True就会)
logger.debug('shit')

# 定义一个新的logger
child_logger = logging.getLogger('apps.owan')
# 因为这个child_logger 是apps.owan, 它是继承了apps这个logger
# 这个child_logger.propagate 默认是True
# 所以还是会传到它的祖先logger 也就是apps
child_logger.info('haha')
# 所以这个info 是会传播到apps 所以apps.log会出现这个日志。
# 这里充分说明logger的继承关系
tail apps.log
# apps ,apps.owan 的logger都把日志写到apps.log了, 而且格式一样
# 因为child_logger没有设置handler 和 formatter, 默认传播到祖先那里apps 的logger
2018-01-30 16:00:10,258:apps:DEBUG:apps logger coming
2018-01-30 16:00:10,258:apps.owan:INFO:i am apps child_logger
发布了7 篇原创文章 · 获赞 0 · 访问量 1132

猜你喜欢

转载自blog.csdn.net/weixin_43199103/article/details/93335133