python的logging模块提供了通用的日志系统,可以方便第三方模块或应用使用。
简单使用
1 #!/usr/bin/python3 2 3 import logging 4 5 6 logging.debug(‘debug message‘) 7 logging.info(‘info message‘) 8 logging.warn(‘warn message‘) 9 logging.error(‘error message‘) 10 logging.critical(‘critical message‘)
1.
2. 设置级别 logger.setLevel(logging.DEBUG):
Logging中有NOTSET < DEBUG <
INFO < WARNING < ERROR < CRITICAL这几种级别,日志会记录设置级别以上的日志
3. Handler,常用的是StreamHandler和FileHandler
可以简单理解为一个是console和文件日志,一个打印在调试窗口上,一个记录在一个文件上
4. formatter,定义了最终log信息的顺序,结构和内容
我喜欢用这样的格式 ‘[%(asctime)s] [%(levelname)s] %(message)s‘, ‘%Y-%m-%d %H:%M:%S‘,
该模块提供不同的日志级别,并可以采用不同的方式记录日志,比如文件,HTTP GET/POST,SMTP,Socket等。
Logging模块提供Logger,handler,filter,formatter。
- Logger:
- 初始化 logger = logging.getLogger("endlesscode"),获取logger对象,getLogger()方法后面最好加上所要日志记录的模块名字,配置文件和打印日志格式中的%(name)s 对应的是这里的模块名字,如果不指定name则返回root对象。
- 记录器,暴露了应用程序代码能直接使用的接口提供日志接口,供应用代码使用。
- Logger最常用的操作有两类:配置和发送日志消息。
- 多次使用相同的name调用getLogger方法返回同一个looger对象;
- handler:
- 处理器,将(记录器产生的)日志记录发送至合适的目的地。
- 将日志记录(log record)发送到合适的目的地(destination),比如文件,socket等。
- 一个logger对象可以通过addHandler方法添加0到多个handler,每个handler又可以定义不同日志级别,以实现日志分级过滤显示。
- filter:
- 过滤器,提供了更好的粒度控制,它可以决定输出哪些日志记录。。
- formatter:
- 格式化器,指明了最终输出中日志记录的布局。
- 指定日志记录输出的具体格式。formatter的构造方法需要两个参数:消息的格式字符串和日期字符串,这两个参数都是可选的。
Logger 记录器
Logger是一个树形层级结构,在使用接口debug,info,warn,error,critical之前必须创建Logger实例
创建方法:
logger = logging.getLogger(logger_name)
Handler 处理器
Handler处理器类型有很多种,比较常用的有三个,StreamHandler,FileHandler,NullHandler,详情可以访问Python logging.handlers
创建StreamHandler之后,可以通过使用以下方法设置日志级别,设置格式化器Formatter,增加或删除过滤器Filter。
创建方法: sh = logging.StreamHandler(stream=None)
FileHandler
创建方法:
fh = logging.FileHandler(filename, mode=‘a‘, encoding=None, delay=False)
Formatter 格式化器
使用Formatter对象设置日志信息最后的规则、结构和内容,默认的时间格式为%Y-%m-%d %H:%M:%S。
创建方法:
formatter = logging.Formatter(fmt=None, datefmt=None)
其中,fmt是消息的格式化字符串,datefmt是日期字符串。如果不指明fmt,将使用‘%(message)s‘。如果不指明datefmt,将使用ISO8601日期格式。
有用的format格式
格式 | 描述 |
---|---|
%(levelno)s | 打印日志级别的数值 |
%(levelname)s | 打印日志级别名称 |
%(pathname)s | 打印当前执行程序的路径 |
%(filename)s | 打印当前执行程序名称 |
%(funcName)s | 打印日志的当前函数 |
%(lineno)d | 打印日志的当前行号 |
%(asctime)s | 打印日志的时间 |
%(thread)d | 打印线程id |
%(threadName)s | 打印线程名称 |
%(process)d | 打印进程ID |
%(message)s | 打印日志信息 |
Filter 过滤器
Handlers和Loggers可以使用Filters来完成比级别更复杂的过滤。Filter基类只允许特定Logger层次以下的事件。例如用‘A.B’初始化的Filter允许Logger ‘A.B’, ‘A.B.C’, ‘A.B.C.D’, ‘A.B.D’等记录的事件,logger‘A.BB’, ‘B.A.B’ 等就不行。 如果用空字符串来初始化,所有的事件都接受。
创建方法:
filter = logging.Filter(name=‘‘)
配置文件格式说明
%(name)s Logger的名字 %(levelname)s 文本形式的日志级别 %(message)s 用户输出的消息 %(asctime)s 字符串形式的当前时间。默认格式是 “2003-07-08 16:49:45,896”。逗号后面的是毫秒 %(levelno)s 数字形式的日志级别 %(pathname)s 调用日志输出函数的模块的完整路径名,可能没有 %(filename)s 调用日志输出函数的模块的文件名 %(module)s 调用日志输出函数的模块名 %(funcName)s 调用日志输出函数的函数名 %(lineno)d 调用日志输出函数的语句所在的代码行 %(created)f 当前时间,用UNIX标准的表示时间的浮 点数表示 %(relativeCreated)d 输出日志信息时的,自Logger创建以 来的毫秒数 %(thread)d 线程ID。可能没有 %(threadName)s 线程名。可能没有 %(process)d 进程ID。可能没有
实例代码:
1、在调试窗口上只打出error以上级别的日志,但是在日志中打出debug以上的信息
1 import logging 2 3 logger = logging.getLogger("simple_example") 4 logger.setLevel(logging.DEBUG) 5 6 # 建立一个filehandler来把日志记录在文件里,级别为debug以上 7 fh = logging.FileHandler("spam.log") 8 fh.setLevel(logging.DEBUG) 9 10 # 建立一个streamhandler来把日志打在CMD窗口上,级别为error以上 11 ch = logging.StreamHandler() 12 ch.setLevel(logging.ERROR) 13 14 # 设置日志格式 15 formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s") 16 ch.setFormatter(formatter) 17 fh.setFormatter(formatter) 18 19 # 将相应的handler添加在logger对象中 20 logger.addHandler(ch) 21 logger.addHandler(fh) 22 23 # 开始打日志 24 logger.debug("debug message") 25 logger.info("info message") 26 logger.warn("warn message") 27 logger.error("error message") 28 logger.critical("critical message")
将Logging封装成一个类
这样每次使用的时候,只要实例化一个对象就可以了
1 #! /usr/bin/python3 2 3 import logging 4 5 6 class Logger: 7 def __init__(self, path, clevel=logging.DEBUG, Flevel=logging.DEBUG): 8 self.logger = logging.getLogger(path) 9 self.logger.setLevel(logging.DEBUG) 10 fmt = logging.Formatter(‘[%(asctime)s] [%(levelname)s] %(message)s‘, ‘%Y-%m-%d %H:%M:%S‘) 11 12 # 设置CMD日志 13 sh = logging.StreamHandler() 14 sh.setFormatter(fmt) 15 sh.setLevel(clevel) 16 17 # 设置文件日志 18 fh = logging.FileHandler(path) 19 fh.setFormatter(fmt) 20 fh.setLevel(Flevel) 21 self.logger.addHandler(sh) 22 self.logger.addHandler(fh) 23 24 def debug(self, message): 25 self.logger.debug(message) 26 27 def info(self, message): 28 self.logger.info(message) 29 30 def war(self, message): 31 self.logger.warn(message) 32 33 def error(self, message): 34 self.logger.error(message) 35 36 def cri(self, message): 37 self.logger.critical(message) 38 39 40 if __name__ == ‘__main__‘: 41 logyyx = Logger(‘yyx.log‘, logging.ERROR, logging.DEBUG) 42 logyyx.debug(‘一个debug信息‘) 43 logyyx.info(‘一个info信息‘) 44 logyyx.war(‘一个warning信息‘) 45 logyyx.error(‘一个error信息‘) 46 logyyx.cri(‘一个致命critical信息‘)
调试运行结果:
/usr/bin/python3.5 /home/rxf/python3_1000/1000/python3_server/logging封装/封装为一个类.py [2017-11-07 10:25:50] [ERROR] 一个error信息 [2017-11-07 10:25:50] [CRITICAL] 一个致命critical信息
日志文件内的内容:
[2017-11-07 10:25:50] [DEBUG] 一个debug信息 [2017-11-07 10:25:50] [INFO] 一个info信息 [2017-11-07 10:25:50] [WARNING] 一个warning信息 [2017-11-07 10:25:50] [ERROR] 一个error信息 [2017-11-07 10:25:50] [CRITICAL] 一个致命critical信息
多模块使用logging
logging模块保证在同一个python解释器内,多次调用logging.getLogger(‘log_name‘)都会返回同一个logger实例,即使是在多个模块的情况下。所以典型的多模块场景下使用logging的方式是在main模块中配置logging,这个配置会作用于多个的子模块,然后在其他模块中直接通过getLogger获取Logger对象即可。
logging.conf:
[loggers] keys=root,main [handlers] keys=consoleHandler,fileHandler [formatters] keys=fmt [logger_root] level=DEBUG handlers=consoleHandler [logger_main] level=DEBUG qualname=main handlers=fileHandler [handler_consoleHandler] class=StreamHandler level=DEBUG formatter=fmt args=(sys.stdout,) [handler_fileHandler] class=logging.handlers.RotatingFileHandler level=DEBUG formatter=fmt args=(‘tst.log‘,‘a‘,20000,5,) [formatter_fmt] format=%(asctime)s - %(name)s - %(levelname)s - %(message)s datefmt=
主模块main.py
1 import logging 2 import logging.config 3 import mod 4 5 logging.config.fileConfig(‘logging.conf‘) 6 root_logger = logging.getLogger(‘root‘) 7 root_logger.debug(‘test root logger...‘) 8 9 logger = logging.getLogger(‘main‘) 10 logger.info(‘test main logger‘) 11 logger.info(‘start import module \‘mod\‘...‘) 12 13 14 logger.debug(‘let\‘s test mod.testLogger()‘) 15 mod.testLogger() 16 17 root_logger.info(‘finish test...‘)
子模块mod.py
import logging import submod logger = logging.getLogger(‘main.mod‘) logger.info(‘logger of mod say something...‘) def testLogger(): logger.debug(‘this is mod.testLogger...‘) submod.tst()
子子模块submod.py:
import logging logger = logging.getLogger(‘main.mod.submod‘) logger.info(‘logger of submod say something...‘) def tst(): logger.info(‘this is submod.tst()...‘)
然后运行python main.py,控制台输出:
/usr/bin/python3.5 /home/rxf/python3_1000/1000/python3_server/logging封装/main.py 2017-11-07 10:53:49,228 - root - DEBUG - test root logger... 2017-11-07 10:53:49,228 - main - INFO - test main logger 2017-11-07 10:53:49,228 - main - INFO - start import module ‘mod‘... 2017-11-07 10:53:49,229 - main - DEBUG - let‘s test mod.testLogger() 2017-11-07 10:53:49,229 - main.mod - DEBUG - this is mod.testLogger... 2017-11-07 10:53:49,229 - main.mod.submod - INFO - this is submod.tst()... 2017-11-07 10:53:49,229 - root - INFO - finish test...
再看一下tst.log,logger配置中的输出的目的地:
2017-11-07 10:56:54,189 - main - INFO - test main logger 2017-11-07 10:56:54,189 - main - INFO - start import module ‘mod‘... 2017-11-07 10:56:54,189 - main - DEBUG - let‘s test mod.testLogger() 2017-11-07 10:56:54,189 - main.mod - DEBUG - this is mod.testLogger... 2017-11-07 10:56:54,190 - main.mod.submod - INFO - this is submod.tst()...
tst.log中没有root logger输出的信息,因为logging.conf中配置了只有main logger及其子logger使用RotatingFileHandler,而root logger是输出到标准输出。
学习思考思路参考:
Logging工作流程
logging模块使用过程
- 第一次导入logging模块或使用reload函数重新导入logging模块,logging模块中的代码将被执行,这个过程中将产生logging日志系统的默认配置。
- 自定义配置(可选)。logging标准模块支持三种配置方式: dictConfig,fileConfig,listen。其中,dictConfig是通过一个字典进行配置Logger,Handler,Filter,Formatter;fileConfig则是通过一个文件进行配置;而listen则监听一个网络端口,通过接收网络数据来进行配置。当然,除了以上集体化配置外,也可以直接调用Logger,Handler等对象中的方法在代码中来显式配置。
- 使用logging模块的全局作用域中的getLogger函数来得到一个Logger对象实例(其参数即是一个字符串,表示Logger对象实例的名字,即通过该名字来得到相应的Logger对象实例)。
- 使用Logger对象中的debug,info,error,warn,critical等方法记录日志信息。
logging模块处理流程
logging_flow.png
- 判断日志的等级是否大于Logger对象的等级,如果大于,则往下执行,否则,流程结束。
- 产生日志。第一步,判断是否有异常,如果有,则添加异常信息。第二步,处理日志记录方法(如debug,info等)中的占位符,即一般的字符串格式化处理。
- 使用注册到Logger对象中的Filters进行过滤。如果有多个过滤器,则依次过滤;只要有一个过滤器返回假,则过滤结束,且该日志信息将丢弃,不再处理,而处理流程也至此结束。否则,处理流程往下执行。
- 在当前Logger对象中查找Handlers,如果找不到任何Handler,则往上到该Logger对象的父Logger中查找;如果找到一个或多个Handler,则依次用Handler来处理日志信息。但在每个Handler处理日志信息过程中,会首先判断日志信息的等级是否大于该Handler的等级,如果大于,则往下执行(由Logger对象进入Handler对象中),否则,处理流程结束。
- 执行Handler对象中的filter方法,该方法会依次执行注册到该Handler对象中的Filter。如果有一个Filter判断该日志信息为假,则此后的所有Filter都不再执行,而直接将该日志信息丢弃,处理流程结束。
- 使用Formatter类格式化最终的输出结果。 注:Formatter同上述第2步的字符串格式化不同,它会添加额外的信息,比如日志产生的时间,产生日志的源代码所在的源文件的路径等等。
- 真正地输出日志信息(到网络,文件,终端,邮件等)。至于输出到哪个目的地,由Handler的种类来决定。
参考文章:http://www.jb51.net/article/88449.htmhttp://www.jianshu.com/p/feb86c06c4f4