logging模块

logging模块是Python内置的标准模块,主要用于输出运行日志,能够设置输出日志的等级、日志保存路径、日志文件回滚等;相比print,具有以下优势:html

  1. 多线程支持
  2. 能够经过设置不一样的日志等级,在release版本中只输出重要信息,而没必要显示大量的调试信息;
  3. print将全部信息都输出到标准输出中,严重影响开发者从标准输出中查看其它数据;logging则能够由开发者决定将信息输出到什么地方,以及怎么输出;
  4. logging中能够选择不少消息级别,如debug、info、warning、error以及critical。经过赋予logger或者handler不一样的级别,开发者就能够只输出错误信息到特定的记录文件,或者在调试时只记录调试信息。

logging模块的导入

import logging

最简单的logging日志

import logging
logging.debug('debug message')
logging.info('info message')
logging.warning('warning message')
logging.error('error message')
logging.critical('critical message')  

# 输出为:
# WARNING:root:warning message
# ERROR:root:error message
# CRITICAL:root:critical message

从这个例子中,咱们能够看到默认状况下python

  1. Python的logging模块将日志打印到了标准输出中
  2. 只显示大于等于WARNING级别的日志,这说明默认的日志级别设置为WARNING(日志级别等级CRITICAL > ERROR > WARNING > INFO > DEBUG > NOTSET)
  3. 默认的日志格式为日志级别:日志器名称:日志内容。

关于logging的内容远不止这么简单,且慢慢看下去。程序员

在后面分别会说明日志级别,模块级函数记录日志,logging模块日志流处理流程服务器

日志级别

日志级别分类网络

  • NOTSET:没有设置
  • DEBUG:最详细的日志信息,典型应用场景是在开发过程当中打印一些运行信息。
  • INFO:重要信息,信息详细程度仅次于DEBUG,一般只记录关键节点信息,用于确认一切都是按照咱们预期的那样进行工做
  • WARNING:警告信息,代表会出现潜在错误的情形,当某些不指望的事情发生时记录的信息(如,磁盘可用空间较低),可是此时应用程序仍是正常运行的,有些信息不是错误信息,可是也要给程序员的一些提示。
  • ERROR:错误信息,因为一个更严重的问题致使某些功能不能正常运行时记录的信息,虽然发生错误事件,但仍然不影响系统的继续运行。打印错误和异常信息,若是不想输出太多的日志,可使用这个级别。
  • CRITICAL:严重错误信息,当发生严重错误,致使应用程序不能继续运行时记录的信息

说明多线程

  • 后面的日志等级是从大到小依次下降的,即:CRITICAL > ERROR > WARNING > INFO > DEBUG > NOTSET,而日志的信息量是依次增多的;
  • 开发应用程序或部署开发环境时,可使用DEBUG或INFO级别的日志获取尽量详细的日志信息来进行开发或部署调试;应用上线或部署生产环境时,应该使用WARNING或ERROR或CRITICAL级别的日志来下降机器的I/O压力和提升获取错误日志信息的效率。日志级别的指定一般都是在应用程序的配置文件中进行指定的。
  • 当为某个应用程序指定一个日志级别后,应用程序会记录全部日志级别大于或等于指定日志级别的日志信息,而不是仅仅记录指定级别的日志信息。

logging中记录日志

logging模块中提供了两种记录日志的方式:ide

  • 第一种方式是使用logging提供的模块级别的函数
  • 第二种方式是使用Logging日志系统的四大组件

说明: logging模块提供的模块级别的那些函数实际上也是经过这几个组件的相关实现类来记录日志的,只是在建立这些类的实例时设置了一些默认值。函数

1、模块级函数记录日志

经常使用的记录日志的模块级函数

上面说到了logging的basicConfig函数进行参数的配置。工具

logging.basicConfig函数各个参数测试

  • filename:指定日志文件名
  • filemode:指定日志文件的打开模式,默认为'a'。须要注意的是,该选项要在filename指定时才有效
  • format:指定日志格式字符串,即指定日志输出时所包含的字段信息以及它们的顺序。logging模块定义的格式字段下面会列出。
    • 经常使用参数及做用
      • %(asctime)s:日志事件发生的时间--人类可读时间,如:2003-07-08 16:49:45,896
        %(created)f:日志事件发生的时间--时间戳,就是当时调用time.time()函数返回的值
        %(relativeCreated)d:日志事件发生的时间相对于logging模块加载时间的相对毫秒数(目前还不知道干吗用的)
        %(msecs)d:日志事件发生事件的毫秒部分
        %(levelname)s:该日志记录的文字形式的日志级别('DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL')
        %(levelno)s:该日志记录的数字形式的日志级别(10, 20, 30, 40, 50)
        %(name)s:所使用的日志器名称,默认是'root',由于默认使用的是 rootLogger
        %(message)s:日志记录的文本内容,经过 msg % args计算获得的
        %(pathname)s:调用日志记录函数的源码文件的全路径
        %(filename)s:pathname的文件名部分,包含文件后缀
        %(module)s:filename的名称部分,不包含后缀
        %(lineno)d:调用日志记录函数的源代码所在的行号
        %(funcName)s:调用日志记录函数的函数名
        %(process)d:进程ID
        %(processName)s:进程名称,Python 3.1新增
        %(thread)d:线程ID
        %(thread)s:线程名称

  • datefmt:指定日期/时间格式。须要注意的是,该选项要在format中包含时间字段%(asctime)s时才有效
  • level:设置日志级别,默认为logging.WARNNING;
  • stream:指定将日志的输出流,能够指定输出到sys.stderr,sys.stdout或者文件,默认输出到sys.stderr,stream和filename不能同时提供,不然会引起 ValueError异常;
  • style:Python 3.2中新添加的配置项。指定format格式字符串的风格,可取值为'%'、'{'和'$',默认为'%'
  • handlers:Python 3.3中新添加的配置项。该选项若是被指定,它应该是一个建立了多个Handler的可迭代对象,这些handler将会被添加到root logger。须要说明的是:filename、stream和handlers这三个配置项只能有一个存在,不能同时出现2个或3个,不然会引起ValueError异常。

 简单的日志输出

在一开始写的那个简单的例子其实就是用模块级函数写的日志。除了上面的那种写法,还有另外一种写法。

import logging
logging.log(logging.DEBUG, "This is a debug log.")
logging.log(logging.INFO, "This is a info log.")
logging.log(logging.WARNING, "This is a warning log.")
logging.log(logging.ERROR, "This is a error log.")
logging.log(logging.CRITICAL, "This is a critical log.")

# 输出为:
# WARNING:root:This is a warning log.
# ERROR:root:This is a error log.
# CRITICAL:root:This is a critical log.
View Code

 正如前面一开始说的那样,日志的的默认日志级别为WARNING,只有它和它以上的日志记录被输出。而默认的输出格式是:日志级别:日志器名称:日志内容。而这样输出也是由于

logging模块提供的日志记录函数所使用的日志器设置的日志格式默认是BASIC_FORMAT,其值为:

"%(levelname)s:%(name)s:%(message)s"

 详细的默认值能够看源码,当咱们没有提供任何配置信息的时候,这些函数都会去调用logging.basicConfig(**kwargs)方法,且不会向该方法传递任何参数。继续查看basicConfig()方法的代码就能够找到上面这些问题的答案了。

 稍做配置后的模块级函很多天志输出

import logging
LOG_FORMAT = "%(asctime)s - %(levelname)s - %(message)s"
DATE_FORMAT = "%m/%d/%Y %H:%M:%S %p"

logging.basicConfig(filename='my.log', level=logging.DEBUG, format=LOG_FORMAT, datefmt=DATE_FORMAT)

logging.debug("This is a debug log.")
logging.info("This is a info log.")
logging.warning("This is a warning log.")
logging.error("This is a error log.")
logging.critical("This is a critical log.")

# 此时会在my.log日志文件中看到下面的输出结果
# 08/12/2018 21:54:39 PM - DEBUG - This is a debug log.
# 08/12/2018 21:54:39 PM - INFO - This is a info log.
# 08/12/2018 21:54:39 PM - WARNING - This is a warning log.
# 08/12/2018 21:54:39 PM - ERROR - This is a error log.
# 08/12/2018 21:54:39 PM - CRITICAL - This is a critical log.
View Code

会上面这些基本的日志记录就算是掌握了。 

说明:

  • logging.basicConfig()函数是一个一次性的简单配置工具使,也就是说只有在第一次调用该函数时会起做用,后续再次调用该函数时彻底不会产生任何操做的,屡次调用的设置并非累加操做。
  • 日志器(Logger)是有层级关系的,上面调用的logging模块级别的函数所使用的日志器是RootLogger类的实例,其名称为'root',它是处于日志器层级关系最顶层的日志器,且该实例是以单例模式存在的。
  • 若是要记录的日志中包含变量数据,可以使用一个格式字符串做为这个事件的描述消息(logging.debug、logging.info等函数的第一个参数),而后将变量数据做为第二个参数*args的值进行传递,如:logging.warning('%s is %d years old.', 'Tom', 10),输出内容为WARNING:root:Tom is 10 years old.
  • logging.debug(), logging.info()等方法的定义中,除了msg和args参数外,还有一个**kwargs参数。它们支持3个关键字参数: exc_info, stack_info, extra,下面对这几个关键字参数做个说明。
关于exc_info, stack_info, extra关键词参数的说明:
  • exc_info: 其值为布尔值,若是该参数的值设置为True,则会将异常异常信息添加到日志消息中。若是没有异常信息则添加None到日志信息中。
  • stack_info: 其值也为布尔值,默认值为False。若是该参数的值设置为True,栈信息将会被添加到日志信息中。
  • extra: 这是一个字典(dict)参数,它能够用来自定义消息格式中所包含的字段,可是它的key不能与logging模块定义的字段冲突。

一个例子:

import logging
LOG_FORMAT = "%(asctime)s - %(levelname)s - %(user)s[%(ip)s] - %(message)s"
DATE_FORMAT = "%m/%d/%Y %H:%M:%S %p"

logging.basicConfig(format=LOG_FORMAT, datefmt=DATE_FORMAT)
logging.warning("Some one delete the log file.", exc_info=True, stack_info=True, extra={'user': 'Tom', 'ip':'192.168.199.22'})

# 打印结果为
# 08/12/2018 22:01:43 PM - WARNING - Tom[192.168.199.22] - Some one delete the log file.
# NoneType: None
# Stack (most recent call last):
#   File "D:/workspace/modue/bin.py", line 6, in <module>
#     logging.warning("Some one delete the log file.", exc_info=True, stack_info=True, extra={'user': 'Tom', 'ip':'192.168.199.22'})
View Code

logging模块日志流处理流程

在说明用logging模块的四大组件记录日志以前, 颇有必要对logging模块所包含的重要组件以及其工做流程作个全面、简要的介绍,这有助于咱们更好的理解咱们所写的代码(将会触发什么样的操做)。

 logging模块的四大组件

logging模块就是经过这些组件来完成日志处理的,上面所使用的logging模块级别的函数也是经过这些组件对应的类来实现的。

这些组件之间的关系描述:

  • 日志器(logger)须要经过处理器(handler)将日志信息输出到目标位置,如:文件、sys.stdout、网络等;
  • 不一样的处理器(handler)能够将日志输出到不一样的位置;
  • 日志器(logger)能够设置多个处理器(handler)将同一条日志记录输出到不一样的位置;
  • 每一个处理器(handler)均可以设置本身的过滤器(filter)实现日志过滤,从而只保留感兴趣的日志;
  • 每一个处理器(handler)均可以设置本身的格式器(formatter)实现同一条日志以不一样的格式输出到不一样的地方。

简单点说就是:日志器(logger)是入口,真正干活儿的是处理器(handler),处理器(handler)还能够经过过滤器(filter)和格式器(formatter)对要输出的日志内容作过滤和格式化等处理操做。

 logging日志模块相关类及经常使用的方法介绍

下面介绍下与logging四大组件相关的类:Logger,Handler,Filter,Formatter。

  Logger类  

Logger对象有3个任务要作

  1. 向应用程序代码暴露几个方法,使应用程序能够在运行时记录日志消息;
  2. 基于日志严重等级(默认的过滤设施)或filter对象来决定要对哪些日志进行后续处理;
  3. 将日志消息传送给全部感兴趣的日志handlers。

logger对象最经常使用的方法分为两类:配置方法和消息发送。

建立一个Logger对象

  1. 方法一:建立Logger类的实例
    • logger = logging.Logger
  2. 方法二:经过logging.getLogger([name])方法(经常使用)
    • logger = logging.getLogger() 
    • logging.getLogger()方法有一个可选参数name,该参数表示将要返回的日志器的名称标识,若是不提供该参数,则其值为'root'。若以相同的name参数值屡次调用getLogger()方法,将会返回指向同一个logger对象的引用,由于在建立日志器的时候是用的单例模式。

当咱们建立了一个Logger对象,其实它并没什么功能,须要咱们给它配置添加。

经常使用的配置方法

(下面写的logger都是Logger类的实例化对象):

  • logger.setLevel():设置日志器将会处理的日志消息的最低严重级别,好比:logger.setLevel("DEBUG")
  • logger.addHandler()和logger.removeHandler():为该Logger对象添加 和 移除一个Handler对象
    • import logging
      
      logger = logging.getLogger()    # 得到一个Logger实例化对象
      fh = logging.FileHandler('my_log')  # 建立一个FileHandler对象,用于写入日志文件,参数为日志写到哪一个文件中
      ch = logging.StreamHandler()  # 建立一个StreamHandler对象,用于输出到控制台
      
      logger.addHandler(fh)       # 为Logger对象添加FileHandler对象
      logger.addHandler(ch)       # 为Logger对象添加StreamHandler对象
      logger.removeHandler(fh)    # 为Logger对象删除FileHandler对象
      logger.removeHandler(ch)    # 为Logger对象删除StreamHandler对象
  • logger.addFilter()和logger.removeFilter():
    • 使用参照上面例子,可是它只接收FileHandler对象

建立日志记录

  • Logger.debug(), Logger.info(), Logger.warning(), Logger.error(), Logger.critical():建立一个与它们的方法名对应等级的日志记录
  • Logger.exception():建立一个相似于Logger.error()的日志消息
  • Logger.log():须要获取一个明确的日志level参数来建立一个日志记录

例如:

logger = logging.getLogger()    # 得到一个Logger实例化对象
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')

说明:

  • Logger.exception()与Logger.error()的区别在于:Logger.exception()将会输出堆栈追踪信息,另外一般只是在一个exception handler中调用该方法。
  • Logger.log()与Logger.debug()、Logger.info()等方法相比,虽然须要多传一个level参数,显得不是那么方便,可是当须要记录自定义level的日志时仍是须要该方法来完成。

关于logger的层级结构与有效等级的说明:

  • logger的名称是一个以'.'分割的层级结构,每一个'.'后面的logger都是'.'前面的logger的children,例如,有一个名称为 foo 的logger,其它名称分别为 foo.bar, foo.bar.baz 和 foo.bam都是 foo 的后代。
  • logger有一个"有效等级(effective level)"的概念。若是一个logger上没有被明确设置一个level,那么该logger就是使用它parent的level;若是它的parent也没有明确设置level则继续向上查找parent的parent的有效level,依次类推,直到找到个一个明确设置了level的祖先为止。须要说明的是,root logger老是会有一个明确的level设置(默认为 WARNING)。当决定是否去处理一个已发生的事件时,logger的有效等级将会被用来决定是否将该事件传递给该logger的handlers进行处理。
  • child loggers在完成对日志消息的处理后,默认会将日志消息传递给与它们的祖先loggers相关的handlers。所以,咱们没必要为一个应用程序中所使用的全部loggers定义和配置handlers,只须要为一个顶层的logger配置handlers,而后按照须要建立child loggers就可足够了。咱们也能够经过将一个logger的propagate属性设置为False来关闭这种传递机制。

 

  Handler类 

 Handler对象的做用是(基于日志消息的level)将消息分发到handler指定的位置(文件、网络、邮件等)。在上面的Logger类中,咱们使用了Handler类的一些知识。因此对Handler类应该有了必定的了解,这里会详细的介绍这个类。

Logger对象能够经过addHandler()方法为本身添加0个或者更多个handler对象。好比,一个应用程序可能想要实现如下几个日志需求:

  1. 把全部日志都发送到一个日志文件中;
  2. 把全部严重级别大于等于error的日志发送到stdout(标准输出);
  3. 把全部严重级别为critical的日志发送到一个email邮件地址。

这种场景就须要3个不一样的handlers,每一个handler复杂发送一个特定严重级别的日志到一个特定的位置。

经常使用的Handler

  • logging.StreamHandler:将日志消息发送到输出到Stream,如std.out, std.err或任何file-like对象。
  • logging.FileHandler:将日志消息发送到磁盘文件,默认状况下文件大小会无限增加
  • logging.handlers.RotatingFileHandler:将日志消息发送到磁盘文件,并支持日志文件按大小切割
  • logging.hanlders.TimedRotatingFileHandler:将日志消息发送到磁盘文件,并支持日志文件按时间切割
  • logging.handlers.HTTPHandler:将日志消息以GET或POST的方式发送给一个HTTP服务器
  • logging.handlers.SMTPHandler:将日志消息发送给一个指定的email地址
  • logging.NullHandler:该Handler实例会忽略error messages,一般被想使用logging的library开发者使用来避免'No handlers could be found for logger XXX'信息的出现。

handle对象经常使用配置方法

  • Handler.setLevel():设置handler将会处理的日志消息的最低严重级别
  • Handler.setFormatter():为handler设置一个格式器对象
  • Handler.addFilter() 和 Handler.removeFilter():为handler添加 和 删除一个过滤器对象

  filter类  

Filter能够被Handler和Logger用来作比level更细粒度的、更复杂的过滤功能。Filter是一个过滤器基类,它只容许某个logger层级下的日志事件经过过滤。该类定义以下:

class logging.Filter(name='') filter(record)

好比,一个filter实例化时传递的name参数值为'A.B',那么该filter实例将只容许名称为相似以下规则的loggers产生的日志记录经过过滤:'A.B','A.B,C','A.B.C.D','A.B.D',而名称为'A.BB', 'B.A.B'的loggers产生的日志则会被过滤掉。若是name的值为空字符串,则容许全部的日志事件经过过滤。

filter方法用于具体控制传递的record记录是否能经过过滤,若是该方法返回值为0表示不能经过过滤,返回值为非0表示能够经过过滤。

  formatter类  

Formater对象用于配置日志信息的最终顺序、结构和内容。与logging.Handler基类不一样的是,应用代码能够直接实例化Formatter类。另外,若是你的应用程序须要一些特殊的处理行为,也能够实现一个Formatter的子类来完成。

Formatter类的构造方法定义以下:

logging.Formatter.__init__(fmt=None, datefmt=None, style='%')

可见,该构造方法接收3个可选参数:

  • fmt:指定消息格式化字符串,若是不指定该参数则默认使用message的原始值
  • datefmt:指定日期格式字符串,若是不指定该参数则默认使用"%Y-%m-%d %H:%M:%S"
  • style:Python 3.2新增的参数,可取值为 '%', '{'和 '$',若是不指定该参数则默认使用'%'

logging日志处理流程

下图大体表述了日志流的处理流程

咱们来描述下上面这个图的日志流处理流程:

  • 1)(在用户代码中进行)日志记录函数调用,如:logger.info(...),logger.debug(...)等;
  • 2)判断要记录的日志级别是否知足日志器设置的级别要求(要记录的日志级别要大于或等于日志器设置的级别才算知足要求),若是不知足则该日志记录会被丢弃并终止后续的操做,若是知足则继续下一步操做;
  • 3)根据日志记录函数调用时掺入的参数,建立一个日志记录(LogRecord类)对象;
  • 4)判断日志记录器上设置的过滤器是否拒绝这条日志记录,若是日志记录器上的某个过滤器拒绝,则该日志记录会被丢弃并终止后续的操做,若是日志记录器上设置的过滤器不拒绝这条日志记录或者日志记录器上没有设置过滤器则继续下一步操做--将日志记录分别交给该日志器上添加的各个处理器;
  • 5)判断要记录的日志级别是否知足处理器设置的级别要求(要记录的日志级别要大于或等于该处理器设置的日志级别才算知足要求),若是不知足记录将会被该处理器丢弃并终止后续的操做,若是知足则继续下一步操做;
  • 6)判断该处理器上设置的过滤器是否拒绝这条日志记录,若是该处理器上的某个过滤器拒绝,则该日志记录会被当前处理器丢弃并终止后续的操做,若是当前处理器上设置的过滤器不拒绝这条日志记录或当前处理器上没有设置过滤器测继续下一步操做;
  • 7)若是能到这一步,说明这条日志记录通过了层层关卡容许被输出了,此时当前处理器会根据自身被设置的格式器(若是没有设置则使用默认格式)将这条日志记录进行格式化,最后将格式化后的结果输出到指定位置(文件、网络、类文件的Stream等);
  • 8)若是日志器被设置了多个处理器的话,上面的第5-8步会执行屡次;
  • 9)这里才是完整流程的最后一步:判断该日志器输出的日志消息是否须要传递给上一级logger(以前提到过,日志器是有层级关系的)的处理器,若是propagate属性值为1则表示日志消息将会被输出处处理器指定的位置,同时还会被传递给parent日志器的handlers进行处理直到当前日志器的propagate属性为0中止,若是propagate值为0则表示不向parent日志器的handlers传递该消息,到此结束。

可见,一条日志信息要想被最终输出须要依次通过如下几回过滤:

  • 日志器等级过滤;
  • 日志器的过滤器过滤;
  • 日志器的处理器等级过滤;
  • 日志器的处理器的过滤器过滤;

须要说明的是: 

1.关于上面第9个步骤,若是propagate值为1,那么日志消息会直接传递交给上一级logger的handlers进行处理,此时上一级logger的日志等级并不会对该日志消息进行等级过滤。

2.使用getLogger()方法的时候,若是要返回的日志器名字相同,返回的为同一个日志器,由于它的内部是用单例生成的,因此当多个对象使用同一日志器而后进行设置时,新的设置会覆盖旧的设置。而当输出的时候,有几个对象要显示,就显示几遍日志。

3.日志器是一个树形结构,好比:getLogger("mylogger.sontree")mylogger是root的孩子,sontree是mylogger的孩子,root的孙子。

4.当咱们使用子层的日志器的时候,它会默认往上去找父级直到找到root,有几层显示几遍日志:

  例如:

import logging

logger = logging.getLogger()    # 得到一个Logger实例化对象,因为没写参数返回的root日志器
ch = logging.StreamHandler()  # 建立一个StreamHandler对象,用于输出到控制台
logger.addHandler(ch)       # 为Logger对象添加StreamHandler对象
logger.setLevel("ERROR")

logger1 = logging.getLogger("my_log")    # 得到一个名为Logger实例化对象
logger1.addHandler(ch)  # # 为Logger对象添加StreamHandler对象
logger1.setLevel("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.debug('logger1 debug message')
logger1.info('logger1 info message')
logger1.warning('logger1 warning message')
logger1.error('logger1 error message')
logger1.critical('logger1 critical message')

# 输出结果为:
# logger error message
# logger critical message
# logger1 debug message
# logger1 debug message
# logger1 info message
# logger1 info message
# logger1 warning message
# logger1 warning message
# logger1 error message
# logger1 error message
# logger1 critical message
# logger1 critical message
View Code

例子中,logger不写参数获取的日志器为root,它是正常显示的,而logger1获取的则是名为my_log的日志器,它除了打印本身的,还向上去找,找到了它的父级就是顶级的root,因此也打印了一遍,这样就看到了后面打印了两遍的日志。你能够再按上面说到建立一个my_log日志器的子日志器进行测试。

5.上面提到了会重复打印的问题,那么我不想这样怎么办。那就是把例子中的logger.addHandler(ch)  注释了,不让父日志器的handler工做就好了。因此咱们须要注意的就是每当一个日志器的父日志器有输出的时候,它就会重复输出一次。

 用logging模块的四大组件记录日志

如今,咱们对logging模块的重要组件及整个日志流处理流程都应该有了一个比较全面的了解,下面咱们来看一个例子。

需求

如今有如下几个日志记录的需求:

  • 1)要求将全部级别的全部日志都写入磁盘文件中
  • 2)all.log文件中记录全部的日志信息,日志格式为:日期和时间 - 日志级别 - 日志信息
  • 3)error.log文件中单独记录error及以上级别的日志信息,日志格式为:日期和时间 - 日志级别 - 文件名[:行号] - 日志信息
  • 4)要求all.log在天天凌晨进行日志切割

分析

  • 1)要记录全部级别的日志,所以日志器的有效level须要设置为最低级别--DEBUG;
  • 2)日志须要被发送到两个不一样的目的地,所以须要为日志器设置两个handler;另外,两个目的地都是磁盘文件,所以这两个handler都是与FileHandler相关的;
  • 3)all.log要求按照时间进行日志切割,所以他须要用logging.handlers.TimedRotatingFileHandler; 而error.log没有要求日志切割,所以可使用FileHandler;
  • 4)两个日志文件的格式不一样,所以须要对这两个handler分别设置格式器;

代码实现

import logging
import logging.handlers
import datetime

logger = logging.getLogger('mylogger')
logger.setLevel(logging.DEBUG)

rf_handler = logging.handlers.TimedRotatingFileHandler('all.log', when='midnight', interval=1, backupCount=7, atTime=datetime.time(0, 0, 0, 0))
rf_handler.setFormatter(logging.Formatter("%(asctime)s - %(levelname)s - %(message)s"))

f_handler = logging.FileHandler('error.log')
f_handler.setLevel(logging.ERROR)
f_handler.setFormatter(logging.Formatter("%(asctime)s - %(levelname)s - %(filename)s[:%(lineno)d] - %(message)s"))

logger.addHandler(rf_handler)
logger.addHandler(f_handler)

logger.debug('debug message')
logger.info('info message')
logger.warning('warning message')
logger.error('error message')
logger.critical('critical message')

all.log文件输出

2018-08-13 11:00:58,246 - DEBUG - debug message
2018-08-13 11:00:58,247 - INFO - info message
2018-08-13 11:00:58,247 - WARNING - warning message
2018-08-13 11:00:58,247 - ERROR - error message
2018-08-13 11:00:58,247 - CRITICAL - critical message

error.log文件输出

2018-08-13 11:00:58,247 - ERROR - bin.py[:21] - error message
2018-08-13 11:00:58,247 - CRITICAL - bin.py[:22] - critical message

 

【转】 向日志输出中添加上下文信息:http://www.cnblogs.com/yyds/p/6897964.html

参考文档:

http://www.javashuo.com/article/p-wqlkttny-y.html

相关文章
相关标签/搜索