python logger使用与按需定制

编程入门 行业动态 更新时间:2024-10-17 23:27:19

python logger使用与<a href=https://www.elefans.com/category/jswz/34/1766832.html style=按需定制"/>

python logger使用与按需定制

python logger使用与按需定制

日志可追踪软件运行时的数据和状态,有利于调试,日志模块在后台开发中不可缺少

logging库

python logging模块提供一系列接口和方法用于日志记录(Tutorial)。
日志优先级分为:

debug : 10
info : 20
warning : 30
error : 40

当通过logger.setLevel()设定级别后,低于该级别的日志将不被打印。
该优先级别支持自定义,例如,可设置KEY = logger.ERROR + 1,如此,KEY的优先级别将最高

简单使用

1 普通打印

import logging
logging.warning('Watch out')  
logging.info('Here is info')     

2 打印至文件

import logging
logging.basicConfig(format='%(levelname)s:%(message)s', level=logging.DEBUG)
logging.debug('debug info')
logging.info('info')
logging.warning('warning info')

note:format中 levelname为日志级别,message为日志信息, asctime为时间, filename为函数模块文件名, funcName为调用日志模块的函数名

高级使用

logging库包含loggers,handlers,filters和formatters四大组件
loggers:暴露接口供应用层逻辑调用
handlers:将loggers产生的log record放到日志容器(文件 or 控制台)
filters:过滤组件
formatter:定义日志格式

一条日志,即logRecord对象,从loggers生成后,经过filters, formatters,通过handlers最后到达容器

应用在使用logging时,通过name调用logger实例,在python一个方便的用法,是将模块名作为Logger名,ex.
logger = logging.getLogger(__name__)
通过logger调用debug(), info()来生成不同级别的日志信息

Logger对象

logger对象的三件事:提供接口供应用实时记录日志(log -> _log -> makeRecord);根据优先级过滤及格式化记录(setLevel | setFilter | setFormatter);将日志定向到用户所需的容器内(addHandler)

logger.exception vs logger.error:前者除了error信息,还附带程序的stack trace信息

logger提供log方法,用于自定义logLevel
ex.

KEY = logger.ERROR + 1logger = logging.getLogger(__name__)logger.log(KEY,*args, **kw)
常用handler

StreamHandler:向sys.stdout或sys.stderr写日志
FileHandler,:向文件输出日志,无该文件时会新建,默认模式为a,尾部追加
RotatingFileHandler:同样是向文件输出日志,但可管理文件大小,当达到一定大小后,会自动创建一个新文件来基础输出,构造函数中的maxBytes控制最大字节数,backupCount控制文件名后缀最大循环(ex. 30,当到达30后会重新以1为后缀,老1文件会被删除)
TimedRotatingFileHandler:不根据大小而根据时间间隔创建。构造函数中的interval表明距离上一个日志文件创建时间的时间间隔,S秒,M分,H小时,D天,W每星期,midnight凌晨。也可通过重写computeRollover()来定制间隔。

日志容器

文件 | HTTP url | email by SMTP
日志最终通过Handler写入到上述容器中。若logging检测用户未定义任何的日志容器,将默认使用root作为logger,将日志以默认的格式写入sys.stderr,即显示在控制台。

日志处理流

ex. 一个常用的logger示例 ( 调用logger对象方法来设置filter, handler, formatter等 )

import logging# create logger, set level
logger = logging.getLogger('log example')
logger.setLevel(logging.DEBUG)# create console handler and set level to debug
ch = logging.StreamHandler()
ch.setLevel(logging.DEBUG)#create formatter
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')# add formatter to ch
ch.setFormatter(formatter)# add ch to logger
logger.addHandler(ch)# `application` logging
logger.debug('debug info')
logger.info('info')
logger.error('error info')
logger.critical('critical info')运行结果
C:\Python27\python.exe D:/PythonEx/logMgr/app_web.py
2017-03-14 20:45:00,469 - log example - DEBUG - debug info
2017-03-14 20:45:00,470 - log example - INFO - info
2017-03-14 20:45:00,470 - log example - ERROR - error info
2017-03-14 20:45:00,470 - log example - CRITICAL - critical info

另一种方法:通过通过.conf文件配置level,handler, filter等,加载配置的api为:logging.config.fileConfig(‘logging.conf’)
一个logging.conf文件可能是:

[loggers]
keys=root,example[handlers]
keys=consoleHandler,rotateFileHandler[formatters]
keys=simpleFormatter[formatter_simpleFormatter]
format=[%(asctime)s](%(levelname)s)%(name)s : %(message)s[logger_root]
level=DEBUG
handlers=consoleHandler #root logger control console log[logger_example]
level=DEBUG
handlers=rotateFileHandler #example logger create log to file
qualname=example[handler_consoleHandler]
class=StreamHandler
level=DEBUG
formatter=simpleFormatter
args=(sys.stdout,)[handler_rotateFileHandler]
class=handlers.RotatingFileHandler
level=DEBUG
formatter=simpleFormatter
args=('test.log'),'a',10000,10)

note : args必须为元组tuple
ex.

import logging
import logging.configlogging.config.fileConfig("logging.conf")#create logger
logger = logging.getLogger("example")# "application" logging
logger.debug("debug info")
logger.info("info")
logger.error("error info")

运行
C:\Python27\python.exe D:/PythonEx/logMgr/app_web.py
[2017-03-15 11:38:50,519 : DEBUG :example : debug info]
[2017-03-15 11:38:50,519 : INFO :example : info]
[2017-03-15 11:38:50,519 : ERROR :example : error info]

定制Logger

以后台开发为例,程序大多是多线程的。日志虽然是重要的运维和调试部分,但在操作系统中读写文件耗时,考虑到性能,有必要收集日志后定时处理。
在handler之前加入日志收集线程,这里采用gevent.Greenlet:

class gCollector(gevent.Greenlet):   """Collect records    """    time_interval = 30     records_count_upper = 100    def __init__(self, handlerClass):super(gCollector, self).__init__()self._handler_class = handlerClassself._logRecords = []self._interval = self.time_intervaldef _run(self):while True:try:logLen = len(self._logRecords)if logLen:self._interval -= 1if self._interval<=0 or logLen >= self.records_count_upper: #each 10s or to max upper limitlogRecords = self._logRecordsself._logRecords = []self._interval = self.time_intervalfor log in logRecords:self._handler_class.emit(log) #call handler handleself._handler_class.flush() #stream handler need flushgevent.sleep(1)except:defaultLogger.exception("") #with traceback infodef emit(self, record):self._logRecords.append(record) #collect records

定义上述的收集线程后,需要定制Handler来继承该类 。如下:

class myFileHandler(gCollector, logging.FileHandler):'''My File Handler'''def __init__(self, filename, delay=True):super(gCollector, self).__init__(logging.FileHandler)super(myFileHandler, self).__init__(filename, delay=delay)def emit(self, record):logging.FileHandler.emit(self, record)

需要注意的是,使用gCollector增加收集和定时写入后,日志的生成时间和写入disk的时间不再一致。对于TimeRotateFileHandler等在每次写入时会判定是否需要重新生成日志文件的类,如下:

def shouldRollover(self, record):"""Determine if rollover should occur.record is not used, as we are just comparing times, but it is needed sothe method signatures are the same"""t = int(time.time())if t >= self.rolloverAt:return 1#print "No need to rollover: %d, %d" % (t, self.rolloverAt)return 0

上述实现时,在判断时使用当前时刻,这不适用我们的gCollector订制,这个函数也应该被重写,使用record.created代替time.time():

def shouldRollover(self, record):if record.created >= self.rolloverAtreturn 1return 0

额外,也可基于已有的Handler按照需求进行定制,例如重新制定日志文件的生成规则,对于TimedRotateHandler,通过传入when和interval就可以确定每个日志文件的生成时间,但若我们希望在每天某个时刻生成一个文件(例如零点),就需要修改computeRollover函数。下述代码是固定时刻为每日5点:

def computeRollover(self, currentTime):'''Rewrite default func<computeRollover>'''timeStruct = time.localtime(currentTime)timeStruct = time.strptime("%s-%s-%s 5" % (timeStruct.tm_year, timeStruct.tm_mon, timeStruct.tm_mday), "%Y-%m-%d %H")return TimedRotatingFileHandlerputeRollover(self, int(time.mktime(timeStruct)))

更多推荐

python logger使用与按需定制

本文发布于:2024-03-07 10:14:41,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1717545.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:按需   python   logger

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!