logging
日志模块,用于记录系统在运行过程中的一些关键信息,以便于对系统的运行状况进行跟踪,所以还是灰常重要滴,下面我就来从入门到放弃的系统学习一下日志既可以在屏幕上显示,又可以在文件中体现。
简单的在屏幕上体现
logging.debug('This is debug message') logging.info('This is info message') logging.warning('This is warning message') 屏幕上打印: WARNING:root:This is warning message
默认情况下,logging将日志打印到屏幕,日志级别为WARNING;
日志级别大小关系为:CRITICAL > ERROR > WARNING > INFO > DEBUG > NOTSET,当然也可以自己定义日志级别
输出到指定文件
import logging 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='test.log', filemode='w') logging.debug('debug message') logging.info('info message') logging.warning('warning message') logging.error('error message') logging.critical('critical message')
logging.basicConfig函数各参数:
level :设置日志的级别.对低于该级别的日志消息将被忽略
format :设置日志输出格式,format可以输出很多有用信息,如下所示:
%(levelno)s: 打印日志级别的数值
%(levelname)s: 打印日志级别名称
%(pathname)s: 打印当前执行程序的路径,其实就是sys.argv[0]
%(filename)s: 打印当前执行程序名
%(funcName)s: 打印日志的当前函数
%(lineno)d: 打印日志的当前行号
%(asctime)s: 打印日志的时间
%(thread)d: 打印线程ID
%(threadName)s: 打印线程名称
%(process)d: 打印进程ID
%(message)s: 打印日志信息
datefmt :定义日期格式
filename: 指定日志存储路径及文件名
filemode :日志文件的打开模式。 默认值为’a',表示日志消息以追加的形式添加到日志文件中。如果设为’w', 那么每次程序启动的时候都会更新旧的日志文件
stream: 指定将日志的输出流,可以指定输出到sys.stderr,sys.stdout或者文件,默认输出到sys.stderr,当stream和filename同时指定时,stream被忽略
注意:
basicConfig是为root logger创建的一个句柄(StreamHandler),如果root logger已经存在一个句柄时,basicConfig()函数并不会创建新的句柄,就是logging.basicConfig无论配置几个,都只能在一个文件中记录 ,想要在多个文件中记录的话,就需要自己写logging.FileHandler
日志同时输出到文件和屏幕
import logging # 1、创建一个logger logger = logging.getLogger('mylogger') logger.setLevel(logging.DEBUG) # 2、创建一个handler,用于写入日志文件 fh = logging.FileHandler('test.log') fh.setLevel(logging.DEBUG) # 再创建一个handler,用于输出到控制台 ch = logging.StreamHandler() ch.setLevel(logging.DEBUG) # 3、定义handler的输出格式(formatter) formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') # 4、给handler添加formatter fh.setFormatter(formatter) ch.setFormatter(formatter) # 5、给logger添加handler logger.addHandler(fh) logger.addHandler(ch) # 6、记录日志 logger.debug("debug message") logger.info("info message") logger.warn("warn message") logger.error("error message") logger.critical("critical message")
这里的logger.setlevel应该是设置的全局日志等级,后面的handler就可以不用设置了,当然也可以在handler重写日志等级,且日志等级必须大于这个全局日志等级才会生效
典型的日志记录的步骤是这样的:
- 创建logger
- 创建handler
- 定义formatter
- 给handler添加formatter
- 给logger添加handler
logging中几个比较中要的类
除了基本的Handler类,比较有用的子类有:
1) StreamHandler实例发送错误到流(类似文件的对象)
2) FileHandler实例发送错误到磁盘文件
3) BaseRotatingHandler是所有轮徇日志的基类,不能直接使用。但是可以使用 RotatingFileHandler和TimeRotatingFileHandler
4) RotatingFileHandler实例发送信息到磁盘文件,并且限制最大的日志文件大小,并适时轮徇
5) TimeRotatingFileHandler实例发送错误信息到磁盘,并在适当的事件间隔进行轮徇
6) SocketHandler实例发送日志到TCP/IP socket。
7) DatagramHandler实例发送错误信息通过UDP协议
8) SMTPHandler实例发送错误信息到特定的email地址
9) SysLogHandler实例发送日志到UNIX syslog服务,并支持远程syslog服务
10)NTEventLogHandler实例发送日志到WindowsNT/2000/XP事件日志
11)MemoryHandler实例发送日志到内存中的缓冲区,并在达到特定条件时清空
12)HTTPHandler实例发送错误信息到HTTP服务器,通过GET或POST方法
现实应用:
#/usr/bin/python #coding:utf-8 import logging import sys,os import pdb class MyLog(object): ''' Usage: mylog = MyLog() logger = mylog.getLog() logger.debug('test...') logger.info('test...') logger.warning('test...') logger.error('test...') logger.critical('test...') 默认日志输出到日志文件 缺省日志名字:调用者文件名后缀变为log 例如:my.py 调用mylog模块 则日志名字my.log 如果需要设置自己的日志名字则:mylog.setLog(filename,loglevel) 如果同时向输出到屏幕则需要使用:mylog.openConsole(self,is_control = True,log_level = None)方法 ''' debug = logging.DEBUG info = logging.INFO warning = logging.WARNING error = logging.ERROR critical = logging.CRITICAL def __init__(self,log_level = logging.WARNING): self.log_level = log_level try: self.default_log_file = os.path.basename(sys.argv[0]).split('.')[0] + '.log' except: self.default_log_file = os.path.basename(sys.argv[0]) + '.log' logging.basicConfig(level=log_level, format ='%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s', datefmt ='%a, %d %b %Y %H:%M:%S', filename = self.default_log_file, filemode ='w') @classmethod def _getFormatter(cls): fmt = logging.Formatter(fmt ='%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s', datefmt ='%a, %d %b %Y %H:%M:%S') return fmt def getLog(self): if not hasattr(self,'_logger'): self._logger = logging.getLogger() return self._logger def setLog(self,log_filename = None,log_level = None): if not hasattr(self,'_logger'): self._logger = logging.getLogger() else: #_handler存在,则移除旧_handler if hasattr(self,'_handler'): self._logger.removeHandler(self._handler) #实例化一个_hander if log_filename is None: log_filename = self.default_log_file self._handler=logging.FileHandler(log_filename) #set log Formatter fmt = MyLog._getFormatter() self._handler.setFormatter(fmt) self._logger.addHandler(self._handler) if log_level is None: log_level = self.log_level self._logger.setLevel(log_level) def openConsole(self,is_control = True,log_level = None): ''' 默认输出屏幕级别与输出到日志的级别一致 如果关闭屏幕输出:is_control 设置为False ''' if not hasattr(self,'_logger'): self._logger = logging.getLogger() if is_control: fmt = MyLog._getFormatter() self._console = logging.StreamHandler() if log_level is not None: try: self._console.setLevel(log_level) except: raise Exception('log_level is Error!') self._console.setFormatter(fmt) self._logger.addHandler(self._console) else: if hasattr(self,'_console'): self._logger.removeHandler(self._console) if __name__=='__main__': #pdb.set_trace() mylog = MyLog() mylog.setLog('11.log',logging.DEBUG) logger = mylog.getLog() mylog.openConsole() logger.info('Test...') mylog.openConsole(False) logger.info('close console...')