详解python logging日志传输
1.生成日志并通过http传输出去(通过HTTPHandler方式):
#生成并发送日志 importlogging fromlogging.handlersimportHTTPHandler importlogging.config defsave(): logger=logging.getLogger(__name__) #生成一个log实例,如果括号为空则返回rootlogger hh=HTTPHandler(host='127.0.0.1:5000',url='/log',method='POST') #用HTTPHandler直接发送日志,而并不是写文件再传文件。 logger.setLevel(logging.INFO) #设置日志最低输出级别为info logger.addHandler(hh) #添加Handler对象给记录器(为logger添加的日志处理器,可以自定义日志处理器让其输出到其他地方) logger.info('存入600元')#输出日志,内容为‘存入600元' save()
2.用flask接收传过来的日志:
#flask接收日志 fromflaskimportFlask,request app=Flask(__name__) @app.route('/log',methods=['POST']) defsay_hello(): #查看传过来的数据格式: print(request.mimetype) #HTTPHandler传过来的是个form表单 print(request.form.to_dict()) return'Hello,Flask!
' if__name__=='__main__': app.run() 输出: #这是传过来的数据格式,是个form表单 application/x-www-form-urlencoded #这是form.to_dict解析出来的内容(直接解析成了字典) {'name':'__main__','msg':'存入600元','args':'()','levelname':'INFO','levelno':'20','pathname':'C:/Users/huawei/Desktop/code/log/send_log.py', 'filename':'send_log.py','module':'send_log','exc_info':'None','exc_text':'None','stack_info':'None','lineno':'15', 'funcName':'save','created':'1593581146.172768','msecs':'172.76811599731445','relativeCreated':'176.5270233154297', 'thread':'13904','threadName':'MainThread','processName':'MainProcess','process':'3656'}
3.logging模块介绍
Python的logging模块提供了通用的日志系统,熟练使用logging模块可以方便开发者开发第三方模块或者是自己的Python应用。同样这个模块提供不同的日志级别,并可以采用不同的方式记录日志,比如文件,HTTP、GET/POST,SMTP,Socket等,甚至可以自己实现具体的日志记录方式。下文我将主要介绍如何使用文件方式记录log。
1.基本概念:
logging模块包括logger,handler,filter,formatter这四个基本概念。
logging模块与log4j的机制是一样的,只是具体的实现细节不同。模块提供logger,handler,filter,formatter。
- logger:提供日志接口,供应用代码使用。logger最长用的操作有两类:配置和发送日志消息。可以通过logging.getLogger(name)获取logger对象,如果不指定name则返回root对象,多次使用相同的name调用getLogger方法返回同一个logger对象。
- handler:将日志记录(logrecord)发送到合适的目的地(destination),比如文件,socket等。一个logger对象可以通过addHandler方法添加0到多个handler,每个handler又可以定义不同日志级别,以实现日志分级过滤显示。
- filter:提供一种优雅的方式决定一个日志记录是否发送到handler。
- formatter:指定日志记录输出的具体格式。formatter的构造方法需要两个参数:消息的格式字符串和日期字符串,这两个参数都是可选的。
与log4j类似,logger,handler和日志消息的调用可以有具体的日志级别(Level),只有在日志消息的级别大于logger和handler的级别。
importlogging logging.basicConfig(level=logging.DEBUG, format='%(asctime)s%(filename)s[line:%(lineno)d]%(levelname)s%(message)s', datefmt='%a,%d%b%Y%H:%M:%S', filename='myapp.log', filemode='w') ################################################################################################# #定义一个StreamHandler,将INFO级别或更高的日志信息打印到标准错误,并将其添加到当前的日志处理对象# console=logging.StreamHandler() console.setLevel(logging.INFO) formatter=logging.Formatter('%(name)-12s:%(levelname)-8s%(message)s') console.setFormatter(formatter) logging.getLogger('').addHandler(console) ################################################################################################# logging.debug('Thisisdebugmessage') logging.info('Thisisinfomessage') logging.warning('Thisiswarningmessage') 屏幕上打印: root:INFOThisisinfomessage root:WARNINGThisiswarningmessage ./myapp.log文件中内容为: Sun,24May200921:48:54demo2.py[line:11]DEBUGThisisdebugmessage Sun,24May200921:48:54demo2.py[line:12]INFOThisisinfomessage Sun,24May200921:48:54demo2.py[line:13]WARNINGThisiswarningmessage
2.主要用法:
logging.StreamHandler:日志输出到流,可以是sys.stderr、sys.stdout或者文件
logging.FileHandler:日志输出到文件
日志回滚方式,实际使用时用RotatingFileHandler和TimedRotatingFileHandler
logging.handlers.BaseRotatingHandler
logging.handlers.RotatingFileHandler
logging.handlers.TimedRotatingFileHandler
logging.handlers.SocketHandler:远程输出日志到TCP/IPsockets
logging.handlers.DatagramHandler:远程输出日志到UDPsockets
logging.handlers.SMTPHandler:远程输出日志到邮件地址
logging.handlers.SysLogHandler:日志输出到syslog
logging.handlers.NTEventLogHandler:远程输出日志到WindowsNT/2000/XP的事件日志
logging.handlers.MemoryHandler:日志输出到内存中的制定buffer
logging.handlers.HTTPHandler:通过"GET"或"POST"远程输出到HTTP服务器
举例:
importlogging importsys #获取logger实例,如果参数为空则返回rootlogger logger=logging.getLogger("AppName") #指定logger输出格式 formatter=logging.Formatter('%(asctime)s%(levelname)-8s:%(message)s') #文件日志 file_handler=logging.FileHandler("test.log") file_handler.setFormatter(formatter)#可以通过setFormatter指定输出格式 #控制台日志 console_handler=logging.StreamHandler(sys.stdout) console_handler.formatter=formatter#也可以直接给formatter赋值 #为logger添加的日志处理器,可以自定义日志处理器让其输出到其他地方 logger.addHandler(file_handler) logger.addHandler(console_handler) #指定日志的最低输出级别,默认为WARN级别 logger.setLevel(logging.INFO) #输出不同级别的log logger.debug('thisisdebuginfo') logger.info('thisisinformation') logger.warn('thisiswarningmessage') logger.error('thisiserrormessage') logger.fatal('thisisfatalmessage,itissameaslogger.critical') logger.critical('thisiscriticalmessage') #2016-10-0821:59:19,493INFO:thisisinformation #2016-10-0821:59:19,493WARNING:thisiswarningmessage #2016-10-0821:59:19,493ERROR:thisiserrormessage #2016-10-0821:59:19,493CRITICAL:thisisfatalmessage,itissameaslogger.critical #2016-10-0821:59:19,493CRITICAL:thisiscriticalmessage #移除一些日志处理器 logger.removeHandler(file_handler)
4.概述:
python的logging模块(logging是线程安全的)给应用程序提供了标准的日志信息输出接口。logging不仅支持把日志输出到文件,还支持把日志输出到TCP/UDP服务器,EMAIL服务器,HTTP服务器,UNIX的syslog系统等。在logging中主要有四个概念:logger、handler、filter和formatter,下面会分别介绍。
1.logger
Logger对象扮演了三重角色:
它给应用程序暴漏了几个方法,以便应用程序能在运行时记录日志。
Logger对象根据日志的级别或根据Filter对象,来决定记录哪些日志。
Logger对象负责把日志信息传递给相关的handler。
在Logger对象中,最常使用的方法分为两类:configuration,messagesending。configuration方法包括:
setLevel(level)
setLevel(level)方法用来设置logger的日志级别,如果日志的级别低于setLevel(level)方法设置的值,那么logger不会处理它。
logging模块内建的日志级别有:
CRITICAL=50
FATAL=CRITICAL
ERROR=40
WARNING=30
WARN=WARNING
INFO=20
DEBUG=10
NOTSET=0(数值越大级别越高)
addFilter(filter)
removeFilter(filter)
addHandler(handler)
removeHandler(handler)
messagesending方法包括:
debug(log_message,[*args[,**kwargs]])
使用DEBUG级别,记录log_message%args。
为了记录异常信息,需要将关键字参数exc_info设置为一个true值。
logger.debug("Houston,wehavea%s","thornyproblem",exc_info=1) info(log_message,[*args[,**kwargs]])
使用INFO级别,记录log_message%args。
为了记录异常信息,需要将关键字参数exc_info设置为一个true值。
logger.info("Houston,wehavea%s","interestingproblem",exc_info=1) warning(log_message,[*args[,**kwargs]])
使用WARNING级别,记录log_message%args。
为了记录异常信息,需要将关键字参数exc_info设置为一个true值。
logger.warning("Houston,wehavea%s","bitofaproblem",exc_info=1) error(log_message,[*args[,**kwargs]])
使用Error级别,记录log_message%args。
为了记录异常信息,需要将关键字参数exc_info设置为一个true值。
logger.error("Houston,wehavea%s","majorproblem",exc_info=1) critical(log_message,[*args[,**kwargs]])
使用CRITICAL级别,记录log_message%args。
为了记录异常信息,需要将关键字参数exc_info设置为一个true值。
logger.critical("Houston,wehavea%s","majordisaster",exc_info=1) exception(message[,*args]) self.error(*((msg,)+args),**{'exc_info':1}) log(log_level,log_message,[*args[,**kwargs]])
使用整型的级别level,记录log_message%args。
为了记录异常信息,需要将关键字参数exc_info设置为一个true值。
logger.log(level,"Wehavea%s","mysteriousproblem",exc_info=1) logging.getLogger([name])
方法返回一个Logger实例的引用,如果提供了name参数,那么它就是这个Logger实例的名称,如果没提供name参数,那么这个Logger实例的名称是root。
可以通过Logger实例的name属性,来查看Logger实例的名称。
Logger实例的名称是使用句号(.)分隔的多级结构。
在这种命名方式中,后面的logger是前面的logger的子(父子logger只是简单的通过命名来识别),比如:有一个名称为foo的logger,那么诸如foo.bar、foo.bar.baz和foo.bam这样的logger都是foo这个logger的子logger。
子logger会自动继承父logger的定义和配置。
使用相同的名称多次调用logging.getLogger([name])方法,会返回同一个logger对象的引用。
这个规则不仅仅在同一个module有效,而且对在同一个Python解释器进程的多个module也有效。
因此应用程序可以在一个module中定义一个父logger,然后在其他module中继承这个logger,而不必把所有的logger都配置一遍
2.handler
handler实例负责把日志事件分发到具体的目的地。logger对象可以使用addHandler()方法,添加零个或多个handler对象到它自身。一个常见的场景是:应用程序可能希望把所有的日志都记录到一个log文件,所有的ERROR及以上级别的日志都记录到stdout,所有的CRITICAL级别的日志都发送到一个email地址。这个场景需要三个独立的handler,每个handler负责把特定级别的日志发送到特定的地方。
下面是logging模块内置的handler:StreamHandler
FileHandler
RotatingFileHandler
TimedRotatingFileHandler
SocketHandler
DatagramHandler
SysLogHandler
NTEventLogHandler
SMTPHandler
MemoryHandler
HTTPHandler
内置的handler提供了下面的配置方法:
- setLevel(level)
- handler对象的setLevel()方法,与logger对象的setLevel()方法一样,也是用于设置一个日志级别,如果日志的级别低于setLevel()方法设置的值,那么handler不会处理它。
- setFormatter(formatter)
- addFilter(filter)
- removeFilter(filter)
应用程序代码不应该直接实例化和使用handler。logging.Handler是一个定义了所有的handler都应该实现的接口和建立了子类能够使用(或重写)的一些默认行为的基类。
自定义Handler自定义的handler必须继承自logging.Handler,且实现下面的方法:
classHandler(Filterer): defemit(self,record): """ Dowhateverittakestoactuallylogthespecifiedloggingrecord. Thisversionisintendedtobeimplementedbysubclassesandso raisesaNotImplementedError. """ raiseNotImplementedError,'emitmustbeimplemented'\ 'byHandlersubclasses' defflush(self): """ Ensureallloggingoutputhasbeenflushed. Thisversiondoesnothingandisintendedtobeimplementedby subclasses. """ pass defclose(self): """ Tidyupanyresourcesusedbythehandler. Thisversiondoesremovesthehandlerfromaninternallist ofhandlerswhichisclosedwhenshutdown()iscalled.Subclasses shouldensurethatthisgetscalledfromoverriddenclose() methods. """ #getthemoduledatalock,aswe'reupdatingasharedstructure. _acquireLock() try:#unlikelytoraiseanexception,butyouneverknow... ifselfin_handlers: del_handlers[self] ifselfin_handlerList: _handlerList.remove(self) finally: _releaseLock()
其中,emit(record)方法负责执行真正地记录日志所需的一切事情,在logging.Handler的子类中必须实现这个方法。close()方法负责清理handler所使用的资源(在Python解释器退出的时候,会调用所有的handler的flush()和close()方法),logging.Handler的子类应该确保在重写close()方法的时候,调用父类的该方法。
下面分析logging.StreamHandler的源代码:
classStreamHandler(Handler): def__init__(self,strm=None): Handler.__init__(self) ifstrmisNone: strm=sys.stderr self.stream=strm defflush(self): ifself.streamandhasattr(self.stream,"flush"): self.stream.flush() defemit(self,record): try: msg=self.format(record) stream=self.stream fs="%s\n" ifnothasattr(types,"UnicodeType"):#ifnounicodesupport... stream.write(fs%msg) else: try: if(isinstance(msg,unicode)and getattr(stream,'encoding',None)): fs=fs.decode(stream.encoding) try: stream.write(fs%msg) exceptUnicodeEncodeError: #Printingtoterminalssometimesfails.Forexample, #withanencodingof'cp1251',theabovewritewill #workifwrittentoastreamopenedorwrappedby #thecodecsmodule,butfailwhenwritingtoa #terminalevenwhenthecodepageissettocp1251. #Anextraencodingstepseemstobeneeded. stream.write((fs%msg).encode(stream.encoding)) else: stream.write(fs%msg) exceptUnicodeError: stream.write(fs%msg.encode("UTF-8")) self.flush() except(KeyboardInterrupt,SystemExit): raise except: self.handleError(record)
在构造函数中,如果提供了strm参数,那么它就是要输出到的流,如果没提供,那么就会将日志输出到标准错误输出流sys.stderr。
- flush()方法的作用是:刷新self.stream内部的I/O缓冲区。每次emit日志之后都会调用这个方法,将日志从I/O缓冲区sync到self.stream。
- emit(record)方法的作用是:将LogRecord对象(record)记录到self.stream。emit(record)方法首先调用基类logging.Handler提供的format(record)方法,该方法会根据设置的Formatter对象来格式化record对象,得到要记录的字符串msg。然后对fs(fs其实就是在msg的尾部增加一个换行'\n')进行一系列的编码解码,将它写入到self.stream。最后再刷新self.stream。在emit(record)调用期间发生的异常,应该调用logging.Handler提供的handleError(record)方法来处理。
3.filter
Filter对象用于对LogRecord对象执行过滤,logger和handler都可以使用filter来过滤record。下面用一个列子来说明Filter基类的作用:
如果使用A.B实例化一个filter,那么它允许名称为A.B,A.B.C,A.B.C.D这样的logger记录的日志通过,不允许名称为A.BB,B.A.B这样的logger记录的日志通过。
如果使用空字符串实例化一个filter,那么它允许所有的事件通过。
Filter基类有一个方法叫filter(record),它用来决定指定的record(LogRecord对象)是否被记录。如果该方法返回0,则不记录record;返回非0则记录record。
Filterer(注意:不是Filter)是logger和handler的基类。它提供了方法来添加和删除filter,并且提供了filter(record)方法用于过滤record,该方法默认允许record被记录,但是任何filter都可以否决这个默认行为,如果想要丢弃record,filter(record)方法应该返回0,否则应该返回非0。
4.formatter
Formatter对象用于把一个LogRecord对象转换成文本,它定义了日志的格式、结构。与logging.Handler类不同,应用程序可以直接实例化Formatter类,如果需要也可以子类化Formatter,以便定
制一些行为。
Formatter的构造函数接受两个参数:第一个参数是用于日志信息的格式化字符串;第二个参数是用于日期的格式化字符串。第二个参数可选的,默认值是%Y-%m-%d%H:%M:%S。
日志信息的格式化字符串用%(
下面是替换字符串和它们所代表的含义:
%(name)s
logger的名称
%(levelno)s
日志级别的数字表现形式
%(levelname)s
日志级别的文本表现形式
%(pathname)s
调用logging的源文件的全路径名
%(filename)s
pathname的文件名部分
%(module)s
模块名(filename的名称部分)
%(lineno)d
调用logging的行号
%(funcName)s
函数名
%(created)f
LogRecord的创建时间(time.time()的返回值)
%(asctime)s
LogRecord的创建时间的文本表现形式
%(msecs)d
创建时间的毫秒部分
%(relativeCreated)d
LogRecord的创建时间,单位是毫秒。这个时间是相对logging模块被加载的时间的(通常就是应用程序启动的时间)。
%(thread)d
线程ID
%(threadName)s
线程名称
%(process)d
进程ID
%(message)s
record.getMessage()的返回结果。
举例:
配置logging
下面是一个简单的例子,它会向标准输出打印日志:
importlogging importsys logger=logging.getLogger(__name__) filter=logging.Filter(__name__) formatter=logging.Formatter("%(asctime)s|%(name)-12s|%(message)s","%F%T") stream_handler=logging.StreamHandler(sys.stdout) stream_handler.addFilter(filter) stream_handler.setLevel(logging.DEBUG) stream_handler.setFormatter(formatter) logger.setLevel(logging.DEBUG) logger.addFilter(filter) logger.addHandler(stream_handler) if__name__=="__main__": logger.info("info") 运行这个脚本,输出结果是: 2015-12-1613:52:17|__main__|info
使用配置文件,配置logging
下面是一个使用配置文件,配置logging的例子:
importlogging importlogging.config logging.config.fileConfig("logging.conf") if__name__=="__main__": logger=logging.getLogger("test_logging.sublogger") logger.info("info")
logging.conf如下:
[loggers] keys=root,logger [handlers] keys=stream_handler [formatters] keys=formatter [logger_root] handlers=stream_handler [logger_logger] handlers=stream_handler level=DEBUG propagate=1 qualname=test_logging [handler_stream_handler] class=StreamHandler args=(sys.stdout,) formatter=formatter level=DEBUG [formatter_formatter] format=%(asctime)s|%(name)-12s|%(message)s datefmt=%F%T
需要解释的地方有两处:第一个是logger_xxxsection中的propagate选项,在logger对象把record传递给所有相关的handler的时候,会(逐级向上)寻找这个logger和它所有的父logger的全部handler。在寻找过程中,如果logger对象的propagate属性被设置为1,那么就继续向上寻找;如果某个logger的propagate属性被设置为0,那么就会停止搜寻。
第二个是logger_xxxsection中的qualname选项,它其实就是logger的名称。
使用配置文件的时候,必须定义rootlogger。
最酷的listen(port)函数
logging.config.listen(port)函数可以让应用程序在一个socket上监听新的配置信息,达到在运行时改变配置,而不用重启应用程序的目的。
监听程序:
importlogging.config importlogging importtime logging.config.fileConfig("logging.conf") logger=logging.getLogger("test_logging.listen") t=logging.config.listen(9999) t.setDaemon(True) t.start() try: whileTrue: logger.info('running.') time.sleep(3) except(KeyboardInterrupt,SystemExit,Exception): logging.config.stopListening()
发送新的配置信息程序:
importsocket importstruct HOST='localhost' PORT=9999 s=socket.socket(socket.AF_INET,socket.SOCK_STREAM) s.connect((HOST,PORT)) print"connected..." data_to_send=open("logging.conf").read() s.send(struct.pack(">L",len(data_to_send))) s.send(data_to_send) print"closing..." s.close()
以上就是详解pythonlogging日志传输的详细内容,更多关于pythonlogging日志传输的资料请关注毛票票其它相关文章!