Python的logging模块提供了通用的日志系统,熟练使用longgong模块可以方便开发者开发第三方模块或者是自己的Python应用。同样这个模块提供不同的日志级别,并可以采用不同的方式记录日志,比如文件,HTTP、GET/POST,SMTP,Socket等,甚至可以自己实现具体的日志记录方式。下文我将主要介绍如何使用文件方式记录log。
logging模块包括 logger, handler, filter, formatter这四个基本概念。StreamHandler |
FileHandler |
RotatingFileHandler |
TimedRotatingFileHandler |
SocketHandler |
DatagramHandler |
SysLogHandler |
NTEventLogHandler |
SMTPHandler |
MemoryHandler |
HTTPHandler |
formatter:指定日志记录输出的具体格式。使用这个可以更容易的根据错误检错。formatter的构造方法需要两个参数:消息的格式字符串和日期字符串,这两个参数都是可选的。
%(name)s Name of the logger (logging channel) %(levelno)s Numeric logging level for the message (DEBUG, INFO, WARNING, ERROR, CRITICAL) %(levelname)s Text logging level for the message ("DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL") %(pathname)s Full pathname of the source file where the logging call was issued (if available) %(filename)s Filename portion of pathname %(module)s Module (name portion of filename) %(lineno)d Source line number where the logging call was issued (if available) %(created)f Time when the LogRecord was created (time.time() return value) %(asctime)s Textual time when the LogRecord was created %(msecs)d Millisecond portion of the creation time %(relativeCreated)d Time in milliseconds when the LogRecord was created, relative to the time the logging module was loaded (typically at application startup time) %(thread)d Thread ID (if available) %(process)d Process ID (if available) %(message)s The result of record.getMessage(), computed just as the record is emitted
#test.py import logging LOG_FILENAME="./mylog.log" logging.basicConfig(filename=LOG_FILENAME,level=logging.DEBUG) logging.debug("this is a debugmsg!") logging.info("this is a infomsg!") logging.warn("this is a warn msg!") logging.error("this is a error msg!") logging.critical("this is a critical msg!")这样会生成一个mylog.log的日志文件,每运行一次,里面都增加如下内容:
DEBUG:root:this is a debugmsg! INFO:root:this is a infomsg! WARNING:root:this is a warn msg! ERROR:root:this is a error msg! CRITICAL:root:this is a critical msg!
需要说明的是我将level设定为DEBUG级别,所以log日志中只显示了包含该级别及该级别以上的log信息。信息级别依次是:notset、debug、info、warn、error、critical。如果在多个模块中使用这个配置的话,只需在主模块中配置即可,其他模块会有相同的使用效果。
# -* - coding: UTF-8 -* - import logging log_file = "./nomal_logger.log" log_level = logging.DEBUG #建立【logger】 #logger = logging.getLogger() logger = logging.getLogger("loggingmodule.NomalLogger") logger.setLevel(log_level) #建立【文件句柄】和【流句柄】并分别设置级别 filehandler = logging.FileHandler(log_file) filehandler.setLevel(logging.ERROR) streamhandler = logging.StreamHandler() streamhandler.setLevel(logging.ERROR) #建立【格式串】 formatter = logging.Formatter("[%(levelname)s][%(funcName)s][%(asctime)s]%(message)s") #【文件句柄】和【流句柄】使用【格式串】 filehandler.setFormatter(formatter) streamhandler.setFormatter(formatter) #【logger】连接【句柄】 logger.addHandler(filehandler) logger.addHandler(streamhandler) #test logger.debug("this is a debug msg!") logger.info("this is a info msg!") logger.warn("this is a warn msg!") logger.error("this is a error msg!") logger.critical("this is a error msg!")这样会生成一个Log_test.txt的日志文件,每运行一次,里面都增加2行:
[ERROR][<module>][2012-09-04 22:41:19,546]this is a error msg! [CRITICAL][<module>][2012-09-04 22:41:19,562]this is a error msg!上面程序首先生成一个日志对象logger,用它来控制日志的输入。然后生成了一个handler,logging支持很多种Handler,像FileHandler,SocketHandler等待,这里由于我们要写文件,所以用了FileHandler,它的参数就是filename,默认当前路径,当然我们可以自己指定路径。后面还设置日志信息输出的级别。
可以用“python test.py debug”的方式来控制输入级别,代码如下:
#test.py import logging import sys LEVELS={'debug':logging.DEBUG, 'info':logging.INFO, 'warning':logging.WARNING, 'error':logging.ERROR, 'critical':logging.CRITICAL} if len(sys.argv)>1: level_name=sys.argv[1] level=LEVELS.get(level_name,logging.NOTSET) logging.basicConfig(level=level) logging.debug("This is a debug message") logging.info("This is an info message") logging.warning("This is a warning message") logging.error("This is an error message")
使用配置文件形式:Python的logging.config.
Python的logging模块接口仿log4j,概念上一致,使用上相当方便。利用logging.config.fileConfig(),可以将日志的配置用文件来描述,简化了日志的初始化。
这里叙述的是我工作中遇到的问题,需要将多个文件的log信息输入到一个文件中去。我的理解是,在A文件中设置了譬如log文件位置、log文件名字、格式化信息等等,然后在A文件末尾可能用os.system("python B.py")这样又调用了B文件,那么在B文件中要也输出到之前指定的那个log文件,必须重新设置一遍刚才的log信息。这是因为,os.system()相当于系统调用了。与import不一样,import可以理解为将代码插到这个位置。
A.py
import os,sys,time def xixi(): gLogger.info("xixi") def mylog(): import logging.config gLogger = logging.getLogger() logdir = "log/" logfile = "verify.log" os.system("mkdir -p " + logdir) log_file = "./%s/%s"%(logdir,logfile) formatter = logging.Formatter('[%(asctime)s][%(levelname)s] %(message)s') handler = logging.StreamHandler(sys.stdout) handler.setFormatter(formatter) gLogger.addHandler(handler) formatter = logging.Formatter('[%(asctime)s][%(levelname)s] %(message)s') handler = logging.handlers.RotatingFileHandler(log_file) handler.setFormatter(formatter) gLogger.addHandler(handler) gLogger.setLevel(logging.INFO) return gLogger if __name__ == "__main__": gLogger = mylog() xixi() gLogger.info("A.py") os.system("python B.py")
import os,sys,time import logging.config gLogger = logging.getLogger() logdir = "log/" logfile = "verify.log" os.system("mkdir -p " + logdir) log_file = "./%s/%s"%(logdir,logfile) formatter = logging.Formatter('[%(asctime)s][%(levelname)s] %(message)s') handler = logging.StreamHandler(sys.stdout) handler.setFormatter(formatter) gLogger.addHandler(handler) formatter = logging.Formatter('[%(asctime)s][%(levelname)s] %(message)s') handler = logging.handlers.RotatingFileHandler(log_file) handler.setFormatter(formatter) gLogger.addHandler(handler) gLogger.setLevel(logging.INFO) gLogger.info("B1.py") if __name__ == "__main__": gLogger.info("B2.py")
命令
[[email protected]]$python A.py输出
[2012-09-18 23:13:28,301][INFO] xixi [2012-09-18 23:13:28,301][INFO] A.py [2012-09-18 23:13:28,344][INFO] B1.py [2012-09-18 23:13:28,344][INFO] B2.py分析
首先,python A.py 这时,运行到main,调用函数mylog初始化log信息。这时可以用gLogger来输出信息。调用xixi,之所以可以用,因为函数相当于将代码贴过来,这时gLogger已经定义过了。然后再输出A.py的信息。然后系统调用B,B已经事先初始化了。这时gLogger必然处处可用。
我一般是将logging等相关内容写在一个文件里,如下所示:
module_mylog.py
import os,sys def mylog(): import logging.config gLogger = logging.getLogger() logdir = "log/" logfile = "load.log" os.system("mkdir -p " + logdir) log_file = "./%s/%s"%(logdir,logfile) formatter = logging.Formatter('[%(asctime)s][%(levelname)s] %(message)s') handler = logging.StreamHandler(sys.stdout) handler.setFormatter(formatter) gLogger.addHandler(handler) formatter = logging.Formatter('[%(asctime)s][%(levelname)s] %(message)s') handler = logging.handlers.RotatingFileHandler(log_file) handler.setFormatter(formatter) gLogger.addHandler(handler) gLogger.setLevel(logging.INFO) return gLogger gLogger = mylog()其他任何文件要公用这个log时,都只需import就可以:
A.py
from module_myverifylog import gLogger if __name__ == "__main__": gLogger.info("A.py")
B.py
from module_myverifylog import gLogger if __name__ == "__main__": gLogger.info("B.py")