对于log,不会陌生。一般程序中,都会打印一些log让流程更清晰,或者出现错误的时候能够快速通过log找到问题节点,以前写的一些简单的程序,通过print可以打印在屏幕上,通过file.write()写在文档中,但感觉有些模范,不是很专业的样子。Python中有一个专业处理log的模块,叫logging模块。
logging模块的简单应用:
import logging logging.debug('debug') logging.info('info') logging.warning('warning') logging.error('error') logging.critical('critical') >>> WARNING:root:warning ERROR:root:error CRITICAL:root:critical首先我们看到的debug,info,warning,error,critical,这是log五个等级(日志级别等级CRITICAL > ERROR > WARNING > INFO > DEBUG > NOTSET),默认情况下,Python只会显示warning及以上的信息,如果想让所有log全部输出,而且有一定的格式,就需要对logging进行一些设置:
import logging logging.basicConfig( format='%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s', filename='test.log', filemode='w' ) logging.debug('debug') logging.info('info') logging.warning('warning') logging.error('error') logging.critical('critical')现在在控制台上已经没有打印的log了,因为在logging.basicConfig中,定义了一个filename,log都会保存在'test.log'中,打开文件内容如下:
2019-03-18 13:58:33,470 logging模块.py[line:11] WARNING warning2019-03-18 13:58:33,470 logging模块.py[line:12] ERROR error2019-03-18 13:58:33,470 logging模块.py[line:13] CRITICAL critical 这样看起来就很舒服了,在logging.basicConfig中还有其他设置,分别如下:filename:用指定的文件名创建FiledHandler(后边会具体讲解handler的概念),这样日志会被存储在指定的文件中。filemode:文件打开方式,在指定了filename时使用这个参数,默认值为“a”还可指定为“w”。format:指定handler使用的日志显示格式。 datefmt:指定日期时间格式。 level:设置rootlogger(后边会讲解具体概念)的日志级别 stream:用指定的stream创建StreamHandler。可以指定输出到sys.stderr,sys.stdout或者文件(f=open('test.log','w')),默认为sys.stderr。若同时列出了filename和stream两个参数,则stream参数会被忽略。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用户输出的消息Logger 现在出现了一个问题,如果我写了filename参数,就不会在屏幕上显示了,我怎么才能又能在控制台上显示,又能保存在文本中呢?这里就需要用到logger了: import logging logger = logging.getLogger() fh = logging.FileHandler('test.log') #创建一个Handler,用来保存log到文件 ch = logging.StreamHandler() #创建一个Handler,用来显示log到控制台 # 创建一个显示log的格式 fm = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') fh.setFormatter(fm) #在文本中设置成这种格式保存 ch.setFormatter(fm) #在控制台中设置成这种格式显示 logger.addHandler(fh) #将fh的功能添加到logger,logger就有了fh的功能了 logger.addHandler(ch) #将ch的功能添加到logger,logger就又有了ch的功能了 logger.setLevel('DEBUG') #将默认的log等级改为debug logger.debug('debug') logger.info('info') logger.warning('warning') logger.error('error') logger.critical('critical')在控制和test.log中都打印的是:
2019-03-18 14:24:07,722 - root - DEBUG - debug 2019-03-18 14:24:07,722 - root - INFO - info 2019-03-18 14:24:07,722 - root - WARNING - warning 2019-03-18 14:24:07,722 - root - ERROR - error 2019-03-18 14:24:07,722 - root - CRITICAL - critical
这个例子可以看到,logger就好像一个有吸星大法的人,本来没有武功,但是吸收了fh和ch后,就有了他们的功能。logging库提供了多个组件:Logger、Handler、Filter、Formatter。Logger对象提供应用程序可直接使用的接口,Handler发送日志到适当的目的地,Filter提供了过滤日志信息的方法,Formatter指定日志显示格式。
有多中可用的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 日志输出到sysloglogging.handlers.NTEventLogHandler 远程输出日志到Windows NT/2000/XP的事件日志 logging.handlers.SMTPHandler 远程输出日志到邮件地址logging.handlers.MemoryHandler 日志输出到内存中的制定bufferlogging.handlers.HTTPHandler 通过"GET"或"POST"远程输出到HTTP服务器各个Handler的具体用法可查看参考书册:https://docs.python.org/2/library/logging.handlers.html#module-logging.handlers
下面再看一个例子:
import logging logger1 = logging.getLogger('logger') logger2 = logging.getLogger('logger') fh = logging.FileHandler('test.log') #创建一个Handler,用来保存log到文件 ch = logging.StreamHandler() #创建一个Handler,用来显示log到控制台 # 创建一个显示log的格式 fm = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') fh.setFormatter(fm) #在文本中设置成这种格式保存 ch.setFormatter(fm) #在控制台中设置成这种格式显示logger.addHandler(fh) logger.addHandler(ch) logger1.addHandler(fh) logger1.addHandler(ch) logger1.setLevel('DEBUG') #将默认的log等级改为debug logger2.addHandler(fh) logger2.addHandler(ch) logger2.setLevel('INFO') #将默认的log等级改为info logger1.debug('debug') logger1.info('info') logger1.warning('warning') logger1.error('error') logger1.critical('critical') logger2.debug('debug') logger2.info('info') logger2.warning('warning') logger2.error('error') logger2.critical('critical')显示的结果为:
2019-03-18 14:56:31,728 - logger - INFO - info2019-03-18 14:56:31,728 - logger - INFO - info2019-03-18 14:56:31,728 - logger - WARNING - warning2019-03-18 14:56:31,728 - logger - WARNING - warning2019-03-18 14:56:31,729 - logger - ERROR - error2019-03-18 14:56:31,729 - logger - ERROR - error2019-03-18 14:56:31,729 - logger - CRITICAL - critical2019-03-18 14:56:31,729 - logger - CRITICAL - critical2019-03-18 14:56:31,729 - logger - INFO - info2019-03-18 14:56:31,729 - logger - INFO - info2019-03-18 14:56:31,729 - logger - WARNING - warning2019-03-18 14:56:31,729 - logger - WARNING - warning2019-03-18 14:56:31,729 - logger - ERROR - error2019-03-18 14:56:31,729 - logger - ERROR - error2019-03-18 14:56:31,729 - logger - CRITICAL - critical2019-03-18 14:56:31,729 - logger - CRITICAL - critical
什么情况?为什么logger1的debug没有显示?原来logger是一个树状结构,输出信息之前都要获得一个Logger(如果没有显示的获取则自动创建并使用root Logger,如第一个例子所示),logger1和logger2对应的是同一个Logger实例,只要logging.getLogger(name)中名称参数name相同则返回的Logger实例就是同一个,且仅有一个,也即name与Logger实例一一对应。在logger2实例中通过logger2.setLevel(logging.INFO)设置mylogger的日志级别为logging.INFO,所以最后logger1的输出遵从了后来设置的日志级别。
还有一个问题,为什么logger1和logger2都打印了两次?这是因为我们通过logger = logging.getLogger()显示的创建了root logger,而logger1 = logging.getLogger('mylogger')创建了root Logger的孩子(root.logger1)mylogger,logger2同样。而孩子,孙子,重孙……既会将消息分发给他的handler进行处理也会传递给所有的祖先logger处理。所以,把logger干活的地方注释掉,logger.addHandler(fh) 、logger.addHandler(ch) 就OK了,不信可以试试。
孩子,孙子,重孙……可逐层继承来自祖先的日志级别、Handler、Filter设置,也可以通过Logger.setLevel(lel)、Logger.addHandler(hdlr)、Logger.removeHandler(hdlr)、Logger.addFilter(filt)、Logger.removeFilter(filt)。设置自己特别的日志级别、Handler、Filter。若不设置则使用继承来的值。
限制只有满足过滤规则的日志才会输出。 比如我们定义了filter = logging.Filter('a.b.c'),并将这个Filter添加到了一个Handler上,则使用该Handler的Logger中只有名字带 a.b.c前缀的Logger才能输出其日志。
filter = logging.Filter('mylogger')
logger.addFilter(filter)
这是只对logger这个对象进行筛选
如果想对所有的对象进行筛选,则:
filter = logging.Filter('mylogger')
fh.addFilter(filter)
ch.addFilter(filter)
这样,所有添加fh或者ch的logger对象都会进行筛选。
import logging logger = logging.getLogger() # 创建一个handler,用于写入日志文件 fh = logging.FileHandler('test.log') # 再创建一个handler,用于输出到控制台 ch = logging.StreamHandler() formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') fh.setFormatter(formatter) ch.setFormatter(formatter) # 定义一个filter filter = logging.Filter('mylogger') fh.addFilter(filter) ch.addFilter(filter) # logger.addFilter(filter) # logger.addHandler(fh) # logger.addHandler(ch) logger.setLevel(logging.DEBUG) logger.debug('logger debug message') logger.info('logger info message') logger.warning('logger warning message') logger.error('logger error message') logger.critical('logger critical message') logger1 = logging.getLogger('root.mylogger') logger1.setLevel(logging.DEBUG) logger2 = logging.getLogger('mylogger') logger2.setLevel(logging.INFO) logger1.addHandler(fh) logger1.addHandler(ch) logger2.addHandler(fh) logger2.addHandler(ch) logger1.debug('logger1 debug message') logger1.info('logger1 info message') logger1.warning('logger1 warning message') logger1.error('logger1 error message') logger1.critical('logger1 critical message') logger2.debug('logger2 debug message') logger2.info('logger2 info message') logger2.warning('logger2 warning message') logger2.error('logger2 error message') logger2.critical('logger2 critical message')定义一个filter,就是只有mylogger的log才会打印,但是只有ch和fh添加了这个过滤,logger没有添加,所以结果为:
logger warning messagelogger error messagelogger critical message2019-03-18 15:23:36,911 - mylogger - INFO - logger2 info message2019-03-18 15:23:36,911 - mylogger - WARNING - logger2 warning message2019-03-18 15:23:36,911 - mylogger - ERROR - logger2 error message2019-03-18 15:23:36,911 - mylogger - CRITICAL - logger2 critical message
从上面的程序可以看到,前面配置太多了,整个程序很长, 所以可以把配置的相关信息写到配置文件中,比如一个conf文件:
config.conf里面这样写: [loggers] keys=root,simpleExample [handlers] keys=consoleHandler [formatters] keys=simpleFormatter [logger_root] level=DEBUG handlers=consoleHandler [logger_simpleExample] level=DEBUG handlers=consoleHandler qualname=simpleExample propagate=0 [handler_consoleHandler] class=StreamHandler level=DEBUG formatter=simpleFormatter args=(sys.stdout,) [formatter_simpleFormatter] format=%(asctime)s - %(name)s - %(levelname)s - %(message)s 程序就可以这样写: import logging import logging.config logging.config.fileConfig("logging.conf") # 采用配置文件 # create logger logger = logging.getLogger("simpleExample") # "application" code logger.debug("debug message") logger.info("info message") logger.warn("warn message") logger.error("error message") logger.critical("critical message")以上就是logging模块的简单用法。
转载于:https://www.cnblogs.com/pengfy/p/10552699.html
相关资源:python logging 模块