zoukankan      html  css  js  c++  java
  • python之logging模块

    网上已经有很多logging模块的资料,在这里只是把看到的几片博客总结一下,也是为了符合自己阅读习惯。

    参考的博客地址:

          http://www.cnblogs.com/dahu-daqing/p/7040764.html

          https://www.cnblogs.com/louis-w/p/8567434.html

          https://www.cnblogs.com/testdjt/p/7834856.html

    具体logging模块的具体作用,在上面两片博客里面都很清楚。

    logging模块的框架:

    •   Loggers: 可供程序直接调用的接口,app通过调用提供的api来记录日志
    •        Handlers: 决定将日志记录分配至正确的目的地
    •        Filters:对日志信息进行过滤, 提供更细粒度的日志是否输出的判断
    •        Formatters: 自定义最终记录打印的格式布局

    loggers:

    loggers 就是程序可以直接调用的一个日志接口,可以直接向logger写入日志信息。logger并不是直接实例化使用的,而是通过logging.getLogger(name)来获取对象,事实上logger对象是单例模式,logging是多线程安全的,也就是无论程序中哪里需要打日志获取到的logger对象都是同一个。但是不幸的是logger并不支持多进程,这个在后面的章节再解释,并给出一些解决方案。

    【注意】loggers对象是有父子关系的,当没有父logger对象时它的父对象是root,当拥有父对象时父子关系会被修正。举个例子,logging.getLogger("abc.xyz") 会创建两个logger对象,一个是abc父对象,一个是xyz子对象,同时abc没有父对象,所以它的父对象是root。但是实际上abc是一个占位对象(虚的日志对象),可以没有handler来处理日志。但是root不是占位对象,如果某一个日志对象打日志时,它的父对象会同时收到日志,所以有些使用者发现创建了一个logger对象时会打两遍日志,就是因为他创建的logger打了一遍日志,同时root对象也打了一遍日志。

    handlers:

    Handlers 将logger发过来的信息进行准确地分配,送往正确的地方。举个栗子,送往控制台或者文件或者both或者其他地方(进程管道之类的)。它决定了每个日志的行为,是之后需要配置的重点区域。

    每个Handler同样有一个日志级别,一个logger可以拥有多个handler也就是说logger可以根据不同的日志级别将日志传递给不同的handler。当然也可以相同的级别传递给多个handlers这就根据需求来灵活的设置了。

    logging中包含的handler主要有以下几种:

    handler名称:位置;作用
    
    StreamHandler:logging.StreamHandler;日志输出到流,可以是sys.stderr,sys.stdout或者文件
    FileHandler:logging.FileHandler;日志输出到文件
    BaseRotatingHandler:logging.handlers.BaseRotatingHandler;基本的日志回滚方式
    RotatingHandler:logging.handlers.RotatingHandler;日志回滚方式,支持日志文件最大数量和日志文件回滚
    TimeRotatingHandler:logging.handlers.TimeRotatingHandler;日志回滚方式,在一定时间区域内回滚日志文件
    SocketHandler:logging.handlers.SocketHandler;远程输出日志到TCP/IP sockets
    DatagramHandler:logging.handlers.DatagramHandler;远程输出日志到UDP sockets
    SMTPHandler:logging.handlers.SMTPHandler;远程输出日志到邮件地址
    SysLogHandler:logging.handlers.SysLogHandler;日志输出到syslog
    NTEventLogHandler:logging.handlers.NTEventLogHandler;远程输出日志到Windows NT/2000/XP的事件日志
    MemoryHandler:logging.handlers.MemoryHandler;日志输出到内存中的指定buffer
    HTTPHandler:logging.handlers.HTTPHandler;通过"GET"或者"POST"远程输出到HTTP服务器

    Filters:

    Filters 提供了更细粒度的判断,来决定日志是否需要打印。原则上handler获得一个日志就必定会根据级别被统一处理,但是如果handler拥有一个Filter可以对日志进行额外的处理和判断。例如Filter能够对来自特定源的日志进行拦截or修改甚至修改其日志级别(修改后再进行级别判断)。

    logger和handler都可以安装filter甚至可以安装多个filter串联起来。

    formatters:

    Formatters 指定了最终某条记录打印的格式布局。Formatter会将传递来的信息拼接成一条具体的字符串,默认情况下Format只会将信息%(message)s直接打印出来。Format中有一些自带的LogRecord属性可以使用,如下表格:

    asctime             %(asctime)s         日志事件发生的时间--人类可读时间,如:2003-07-08 16:49:45,896
    created             %(created)f        日志事件发生的时间--时间戳,就是当时调用time.time()函数返回的值
    relativeCreated     %(relativeCreated)d 日志事件发生的时间相对于logging模块加载时间的相对毫秒数(目前还不知道干嘛用的)
    msecs               %(msecs)d           日志事件发生事件的毫秒部分
    levelname           %(levelname)s       该日志记录的文字形式的日志级别('DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL')
    levelno             %(levelno)s         该日志记录的数字形式的日志级别(10, 20, 30, 40, 50)
    name                %(name)s            所使用的日志器名称,默认是'root',因为默认使用的是 rootLogger
    message             %(message)s         日志记录的文本内容,通过 msg % args计算得到的
    pathname            %(pathname)s        调用日志记录函数的源码文件的全路径
    filename            %(filename)s        pathname的文件名部分,包含文件后缀
    module              %(module)s          filename的名称部分,不包含后缀
    lineno              %(lineno)d          调用日志记录函数的源代码所在的行号
    funcName            %(funcName)s        调用日志记录函数的函数名
    process             %(process)d         进程ID
    processName         %(processName)s     进程名称,Python 3.1新增
    thread              %(thread)d          线程ID
    threadName          %(thread)s          线程名称

    一个Handler只能拥有一个Formatter 因此如果要实现多种格式的输出只能用多个Handler来实现。

    在formatters中levelname中可以设置日志的级别,日志的级别有以下几种,如下:

    日志等级:使用范围
    
    FATAL:致命错误
    CRITICAL:特别糟糕的事情,如内存耗尽、磁盘空间为空,一般很少使用
    ERROR:发生错误时,如IO操作失败或者连接问题
    WARNING:发生很重要的事件,但是并不是错误时,如用户登录密码错误
    INFO:处理请求或者状态变化等日常事务
    DEBUG:调试过程中使用DEBUG等级,如算法中每个循环的中间状态

    低于设置日志级别的信息,将会被忽略不会被记录!

    logging中的两个方法:

    • logging.basicConfig(**kwargs)  
      • 为日志模块配置基本信息。kwargs 支持如下几个关键字参数:
        filename :日志文件的保存路径。如果配置了些参数,将自动创建一个FileHandler作为Handler;
        filemode :日志文件的打开模式。 默认值为’a’,表示日志消息以追加的形式添加到日志文件中。如果设为’w’, 那么每次程序启动的时候都会创建一个新的日志文件;
        format :设置日志输出格式;
        datefmt :定义日期格式;
        level :设置日志的级别.对低于该级别的日志消息将被忽略;
        stream :设置特定的流用于初始化StreamHandler;
    • logging.getLogger([name])

      • 创建Logger对象。日志记录的工作主要由Logger对象来完成。在调用getLogger时要提供Logger的名称(注:多次使用相同名称来调用getLogger,返回的是同一个对象的引用。),Logger实例之间有层次关系,这些关系通过Logger名称来体现,如:

        p = logging.getLogger(“root”)

        c1 = logging.getLogger(“root.c1”)

        c2 = logging.getLogger(“root.c2”)

        例子中,p是父logger, c1,c2分别是p的子logger。c1, c2将继承p的设置。如果省略了name参数, getLogger将返回日志对象层次关系中的根Logger。

    基本使用

    简单的直接在控制台输出:

    In [6]: logging.info("this is  info message!")      
    
    In [7]: logging.warning("This is  warnings messages")
    WARNING:root:This is  warnings messages

    logging中默认的日志级别是warning,因此info信息不会打印,只有在warning级别以上的信息才会打印。

    上述的日志信息为默认的格式信息,下面使用basicconfig对日志信息进行设置。

    In [1]: import logging
    
    In [2]: logging.basicConfig(level=logging.DEBUG, format="%(asctime)s - %(levelname)s - %(name)s - %(process)d - %(message)s")
    
    In [3]: logging.debug("this is debug meg")
    2018-08-04 14:19:50,113 - DEBUG - root - 25188 - this is debug meg
    
    In [4]: logging.info("this is info meg")
    2018-08-04 14:20:03,873 - INFO - root - 25188 - this is info meg
    
    In [5]: logging.warning("this is warning  meg")
    2018-08-04 14:20:25,041 - WARNING - root - 25188 - this is warning  meg

    上述的日志信息,按照自定义的日志格式输出。

    日志输出到文本:

    #!/usr/bin/env python
    #*-* coding:utf-8 *-*
    import logging
    
    LOG_FORMAT = "%(asctime)s - %(name)s - %(process)d - %(message)s"
    DATE_FORMAT = "%Y-%m-%d %H:%M:%S %P"
    
    logging.basicConfig(filename="log.txt", level=logging.ERROR, format=LOG_FORMAT, datefmt=DATE_FORMAT)
    
    logging.debug("This is a debug log.")
    logging.error("this is a error")
    logging.critical("This is a critical log.")
    
    =====执行结果=====
    [root@os1 workgit]# python log.py    #控制台并不会输出任何结果
    [root@os1 workgit]# cat log.txt      #文本输出
    2018-08-04 15:20:29 pm - root - 30511 - this is a error
    2018-08-04 15:20:29 pm - root - 30511 - This is a critical log.
    [root@os1 workgit]#

    日志输出到控制台和文本

    cat log.py
    #!/usr/bin/env python
    #*-* coding:utf-8 *-*
    import logging
    logger = logging.getLogger(__name__)
    logger.setLevel(level = logging.INFO)
    formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
    
    
    handler = logging.FileHandler("log.txt")
    handler.setLevel(logging.DEBUG)   #这里设置了日志级别
    handler.setFormatter(formatter)
    
    console = logging.StreamHandler() #初始化handler
    console.setLevel(logging.INFO)    #设置日志的级别
    console.setFormatter(formatter)   #设置日志格式
    
    logger.addHandler(handler)
    logger.addHandler(console)
    
    
    logger.info("Start print log")
    logger.debug("Do something")
    logger.warning("Something maybe fail.")
    logger.info("Finish")

    #整个过程就是先初始化一个logger,然后初始化handler,最后把handler添加进logger!

    执行结果:

    [root@os1 workgit]# python log.py
    2018-08-04 15:08:12,266 - __main__ - INFO - Start print log
    2018-08-04 15:08:12,267 - __main__ - WARNING - Something maybe fail.
    2018-08-04 15:08:12,267 - __main__ - INFO - Finish
    
    #文本结果
    [root@os1 workgit]# cat log.txt 
    2018-08-04 15:08:12,266 - __main__ - INFO - Start print log
    2018-08-04 15:08:12,267 - __main__ - WARNING - Something maybe fail.
    2018-08-04 15:08:12,267 - __main__ - INFO - Finish

    【疑问】
    在filehandler中设置了日志的默认级别为debug,但是文件中的调试信息并没有打出,handler的setlevel设置与logger的setlevel设置究竟是怎么样生效的?
    未解决!!!!!!!!!!!!

    上面的设置已经实现了在控制台和日志中打印输出日志的功能!

    几点说明

    • 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模块定义的字段冲突。

    这些字段介绍,参考上面的第三个博文。

    ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

    在博文开始的时候,说明了logging模块的四个核心组件,这个四个核心组件彼此之间是怎么工作的以及各自又是怎么工作的?

    四个组件的描述:

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

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

    具体模块的功能介绍,推荐上面博文的第三篇:https://www.cnblogs.com/testdjt/p/7834856.html

    捕捉异常

    在python中使用traceback追踪一些错误的信息,这里使用logging.error!

    #!/usr/bin/env python
    #*-* coding:utf-8 *-*
    import logging
    logger = logging.getLogger(__name__)
    logger.setLevel(level = logging.INFO)
    handler = logging.FileHandler("log.txt")
    handler.setLevel(logging.INFO)
    formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
    handler.setFormatter(formatter)
    
    console = logging.StreamHandler()
    console.setLevel(logging.INFO)
    
    logger.addHandler(handler)
    logger.addHandler(console)
    
    logger.info("Start print log")
    logger.debug("Do something")
    logger.warning("Something maybe fail.")
    try:
        open("sklearn.txt","rb")
    except (SystemExit,KeyboardInterrupt):
        raise
    except Exception:
        logger.error("Faild to open sklearn.txt from logger.error",exc_info = True)
        #logger.error("Faild to open sklearn.txt from logger.error")
    
    logger.info("Finish")
    
    #通过注释可以查看exc_info = True参数输出的信息

    输出的结果如下:

    -----------注释前----------------
    [root@os1 workgit]# python log.py
    Start print log
    Something maybe fail.
    Faild to open sklearn.txt from logger.error
    Traceback (most recent call last):
      File "log.py", line 21, in <module>
        open("sklearn.txt","rb")
    IOError: [Errno 2] No such file or directory: 'sklearn.txt'
    Finish
    -----------注释后-----------------
    [root@os1 workgit]# python log.py
    Start print log
    Something maybe fail.
    Faild to open sklearn.txt from logger.error
    Finish
    
    ----------注释前日志文件中的结果---
    [root@os1 workgit]# cat log.txt 
    2018-08-04 15:47:55,656 - __main__ - INFO - Start print log
    2018-08-04 15:47:55,656 - __main__ - WARNING - Something maybe fail.
    2018-08-04 15:47:55,656 - __main__ - ERROR - Faild to open sklearn.txt from logger.error
    Traceback (most recent call last):
      File "log.py", line 21, in <module>
        open("sklearn.txt","rb")
    IOError: [Errno 2] No such file or directory: 'sklearn.txt'
    2018-08-04 15:47:55,656 - __main__ - INFO - Finish
  • 相关阅读:
    git connection due to public key issue
    ubuntu search and java write to file
    url
    sort by datetime
    work on ui
    PHP工厂方法模式
    PHP的简单工厂模式
    php如何判断数组是一维还是多维
    nginx搭建分布式简单配置
    python的单例模式
  • 原文地址:https://www.cnblogs.com/wxzhe/p/9419135.html
Copyright © 2011-2022 走看看