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

    日志级别

     日志级别指的是产生日志的事件的严重程度。设置一个级别后,严重程度低于设置值的日志消息将被忽略。数字越高,级别越高。ERROR服务可能就停了,需要处理了。

    debug(),info(),warning(),error()和critical()方法。

    import threading
    import time
    import logging
    
    logging.basicConfig(level=logging.INFO)
    
    def add(x,y):
        logging.info(" {} {}".format(threading.enumerate(),x+y))
    
    t = threading.Timer(1,add,args = (4,5))
    t.start()
    
    结果为:
    INFO:root: [<_MainThread(MainThread, stopped 19268)>, <Timer(Thread-1, started 19292)>] 9

    如果改成debug()就不会显示了,因为它的级别更低。所以低于你所设定的级别信息,它就不会打印了。高于会显示。所以上面的例子warning()会显示。

    格式字符串(重要)

     

     注意:funcName,threadName,processName都是小驼峰。后面跟的s是字符串显示,d是数字显示。

    举例

    默认级别

    import threading
    import time
    import logging
    
    FORMAT = "%(asctime)s %(thread)d %(message)s"
    logging.basicConfig(level=logging.INFO,format=FORMAT)
    
    def add(x,y):
        logging.info(" {} {}".format(threading.enumerate(),x+y))
    
    t = threading.Timer(1,add,args = (4,5))
    t.start()
    
    结果为:
    2019-11-25 15:23:54,841 12600  [<_MainThread(MainThread, stopped 17084)>, <Timer(Thread-1, started 12600)>] 9

    上面的时间打出来不好看,可以在后面这样改。

    import threading
    import time
    import logging
    
    FORMAT = "%(asctime)s %(thread)d %(message)s"#格式字符串
    logging.basicConfig(level=logging.INFO,format=FORMAT,datefmt="%Y-%m-%d-%H-%M-%S")
    
    def add(x,y):
        logging.info(" {} {}".format(threading.enumerate(),x+y))
    
    t = threading.Timer(1,add,args = (4,5))
    t.start()
    
    结果为:
    2019-11-25-15-26-49 18492  [<_MainThread(MainThread, stopped 18628)>, <Timer(Thread-1, started 18492)>] 9

    后面的数字是毫秒。

    还可以将参数输出出来。

    import threading
    import time
    import logging
    
    FORMAT = "%(asctime)s %(thread)d %(message)s"#这里只能用C风格的
    logging.basicConfig(level=logging.INFO,format=FORMAT,datefmt="%Y-%m-%d-%H-%M-%S")
    
    def add(x,y):
        logging.info(" {} {}".format(threading.enumerate(),x+y))
        logging.info("%s %s",x,y)
    
    t = threading.Timer(1,add,args = (4,5))
    t.start()
    
    结果为:
    2019-11-25-15-31-15 18420  [<_MainThread(MainThread, stopped 18988)>, <Timer(Thread-1, started 18420)>] 9
    2019-11-25-15-31-15 18420 4 5
    import logging
    
    FORMAT = "%(asctime)-15s	Thread info: %(thread)d %(threadName)s %(message)s"#模块常量,一般大写。
    logging.basicConfig(format = FORMAT)
    
    logging.info(" i'm {}".format(20))#info不显示,因为级别比warning级别低。
    logging.warning(" i'm {}.".format(20))#warning 默认级别
    
    结果为:
    2019-11-25 11:07:42,021    Thread info: 9608 MainThread  i'm 20.

    构建消息

    import logging
    
    FORMAT = "%(asctime)-15s	Thread info: %(thread)d %(threadName)s %(message)s"#15是左右对齐的意思。
    logging.basicConfig(format = FORMAT,level = logging.INFO)
    
    logging.info(" i'm {}".format(20))#单一字符串
    logging.info(" i'm %d %s ,20, 'years old")#c风格
    
    结果为:
    2019-11-25 11:11:52,208    Thread info: 5768 MainThread  i'm 20
    2019-11-25 11:11:52,208    Thread info: 5768 MainThread  i'm %d %s ,20, 'years old

    上例是最基本的使用方法,大多数时候,使用的是info,正常运行信息的输出。

    日志级别和格式字符串扩展的例子。

    import logging
    
    FORMAT = "%(asctime)-15s	Thread info: %(thread)d %(threadName)s %(message)s %(school)s"
    logging.basicConfig(format = FORMAT,level = logging.WARNING)
    
    d = {"school":"xpc"}
    logging.info("i am %s %s",20,"years old.",extra =d )
    logging.warning("i am %s %s",20,"years old.",extra =d )
    
    
    结果为:
    2019-11-25 11:16:31,293    Thread info: 10472 MainThread i am 20 years old. xpc

    修改日期格式

    import logging
    
    logging.basicConfig(format = "%(asctime)s %(message)s",datefmt="%Y %m %d %I %M:%S")
    
    logging.warning("this event was logged")
    
    
    结果为:
    2019 11 25 11 23:30 this event was logged

    输出到文件

    import logging
    
    logging.basicConfig(format = "%(asctime)s %(message)s",filename="f:/xpc.log")
    for _ in range(5):
        logging.warning("this event was logged")

    上面的代码会直接在F盘中的xpc.log写入日志,如下图。

     Logger类

    logging模块加载的时候,会创建一个root logger。根logger对象的默认级别是WARNING.

    调用logging.basicConfig来调整级别,就是对这个根logger的级别进行修改。

    构造

    logging.getLogger([name = None])

    使用工厂方法返回一个Logger实例。指定name,返回一个名称为name的Logger实例。如果再次使用相同的名字,是实例化一个对象,未指定name,返回根Logger实例。

    import threading
    import time
    import logging
    
    FORMAT = "%(asctime)s %(thread)d %(message)s"
    logging.basicConfig(level=logging.INFO,format=FORMAT,datefmt="%Y-%m-%d-%H-%M-%S")
    
    def add(x,y):
        logging.info(" {} {}".format(threading.enumerate(),x+y))
        logging.info("%s %s",x,y)
    
    t = threading.Timer(1,add,args = (4,5))
    t.start()
    
    log = logging.getLogger("abc")#abc是名字,如果不给,默认是root。
    print(log.name)
    
    结果为:
    abc
    2019-11-25-15-45-44 19236  [<_MainThread(MainThread, stopped 18536)>, <Timer(Thread-1, started 19236)>] 9
    2019-11-25-15-45-44 19236 4 5
    import threading
    import time
    import logging
    
    log = logging.getLogger("abc")#abc是名字,如果不给,默认是root。
    print(log.name)
    print(log,type(log))
    
    结果为:
    abc
    <Logger abc (WARNING)> <class 'logging.Logger'>

    层次结构

    Logger是层次结构的,使用.点号分隔,如“a”,"a.b","a.b.c.d",a是a.b的父parent,a.b是a的子child,对于foo来说,名字为foo.bar,foo.bar.baz,foo.bam都是foo的后代。

    import logging
    
    root = logging.getLogger()#根logger
    print(root.name,type(root),root.parent,id(root))#根root没有父
    
    logger= logging.getLogger(__name__)#模块级logger
    print(logger.name,type(logger),id(logger.parent),id(logger))
    
    loggchild = logging.getLogger(__name__+".child")#模块名.child,这是子logger
    print(loggchild.name,type(loggchild),id(loggchild.parent),id(loggchild))
    
    
    
    结果为:
    root <class 'logging.RootLogger'> None 36706064
    __main__ <class 'logging.Logger'> 36706064 32088752
    __main__.child <class 'logging.Logger'> 32088752 32088976

    level级别设置

    import logging
    
    FORMAT = "%(asctime) - 15s	 Thread info: %(thread)d %(threadName)s %(message)s"
    logging.basicConfig(format = FORMAT,level=logging.INFO)
    
    logger = logging.getLogger(__name__)#创建一个新的logger,未设定级别
    print(logger.name,type(logger))
    print(logger.getEffectiveLevel())#level 20
    
    logger.info("hello1")
    logger.setLevel(28)#重修修改level
    
    print(logger.getEffectiveLevel())#level 28
    logger.info("hello2")#被拦截
    logging.warning("hello 3 warning")
    
    root = logging.getLogger()#根logger
    root.info("hello4 info root")#输出成功
    
    结果为:
    __main__ <class 'logging.Logger'>
    2019-11-25 11:46:01,791     Thread info: 12848 MainThread hello1
    20
    28
    2019-11-25 11:46:01,791     Thread info: 12848 MainThread hello 3 warning
    2019-11-25 11:46:01,791     Thread info: 12848 MainThread hello4 info root

    每一个logger创建后,都有一个等效的level。logger对象可以在创建后动态的修改自己的level。

    Handler

    Handler控制日志信息的输出目的地,可以是控制台、文件。

    可以单独设置level

    可以单独设置格式

    可以设置过滤器

    Handler类继承

     日志输出其实Handler做的,也就是真正干活的是Handler。

    在logging.basicConfig中,有如下的源码:

    if handlers is None:
        filename = kwargs.pop("filename", None)
        mode = kwargs.pop("filemode", 'a')
        if filename:
            h = FileHandler(filename, mode)
        else:
            stream = kwargs.pop("stream", None)
            h = StreamHandler(stream)
        handlers = [h]

    如果设置文件名,则为根logger加一个输出到文件的Handler;如果没有设置文件名,则为根logger加一个streamhandler,默认输出到sys.stderr.

    也就是说,根logger一定会至少有一个handler的。

    思考

    创建的Handler的初试level是什么?

    import logging
    
    FORMAT = "%(asctime)s %(name)s %(message)s"
    logging.basicConfig(format=FORMAT,level=logging.INFO)
    
    logger = logging.getLogger("xpczss")
    print(logger.name,type(logger))
    logger.info("line 1")
    
    handler = logging.FileHandler("F:/xpczss.log","w+")#创建Handler
    logger.addHandler(handler)#给logger对象绑定一个handleer
    
    #注意看看控制台,再看看xpczss.log文件,对比差异
    #思考这是怎么打印的?
    logger.info("line 2")
    
    结果为:
    控制台打印:
    xpczss <class 'logging.Logger'>
    2019-11-25 13:57:03,111 xpczss line 1
    2019-11-25 13:57:03,163 xpczss line 2
    
    f盘xpczss.log打印为:
    line 2

    日志流

    level的继承

    import logging
    
    FORMAT = "%(asctime)s %(name)s %(message)s"
    logging.basicConfig(format=FORMAT,level=logging.INFO)
    root = logging.getLogger()
    
    log1 = logging.getLogger("s")
    log1.setLevel(logging.INFO)#分别取INFO,WARNING,ERROR试一试
    
    #没有设置任何的handler,level
    #log2有效级别就是log1的ERROR
    log2 = logging.getLogger("s.s1")
    log2.warning("log2 warning")
    
    结果为:
    INFO,WARING下有打印,结果为:
    2019-11-25 14:04:15,303 s.s1 log2 warning
    
    ERROR没有打印,空白。

    logger实例,如果设置了level,就用它和信息的级别比较,否则,继承最近的祖先的level。

    继承关系及信息传递

    • 每一个logger实例的level如同入口,让水流进来,如果这个门槛太高,信息就进不来,例如log3.warning("log3),如果log3定义的级别高,就不会有信息通过log3.
    • 如果level没有设置,就用父logger的,如果父logger的level没有设置,继续找父的父的,最终可以找到root上,如果root设置了就用它的,如果root没有设置,root的默认值是WARNING.
    • 消息传递流程
    1. 如果消息在某一个logger对象上产生,这个logger就是当前logger,首先消息level要和当前logger的EffectiveLevel比较,如果低于当前的logger的EffectiveLevel,则流程结束;否则生成log记录。
    2. 日志记录会交给当前logger的所有handler处理,记录还要和每一个handler的级别分别比较,低的不处理,否则按照handler输出日志记录。
    3. 当前logger的所有handler处理完后,就要看自己的propagate属性,如果是true表示向父logger传递这个日志记录,否则到此流程结束。
    4. 如果日志记录传递到了父logger,不需要和logger的level比较,而是直接交给父的所有handler,父logger成为当前logger。重复2、3步骤,直到当前logger的父logger是None退出,也就是说当前logger最后一般是root logger(否则能到root logger要看中间的logger是否允许propagate)
    • logger实例初始的propagate属性为true,即允许向父logger传递消息。
    • logging.basicConfing,如果root没有handler,就默认创建一个streamhandler,如果设置了filename,就创建一个filehandler.如果设置了format参数,就会用它生成一个formatter对象,并把这个formatter加入到刚才创建的handler上,然后把这些handler加入到root.handlers列表上,level是设置给root.logger的。如果root.handlers列表不为空,logging.basicConfig调用什么都不做。

    官方日志流转图

    参考logging.Logger类的callHandlers方法。

    实例

    import logging
    
    logging.basicConfig(format="%(asctime)s %(name)s %(message)s",level=logging.INFO)
    
    root = logging.getLogger()
    root.setLevel(logging.ERROR)
    print("root",root.handlers)
    h0 = logging.StreamHandler()
    h0.setLevel(logging.WARNING)
    root.addHandler(h0)
    print("root",root.handlers)
    for h in root.handlers:
        print("root handler = {},foramt = {}".format(h,h.formatter) )
    
    log1 = logging.getLogger("s")
    log1.setLevel(logging.WARNING)
    h1 = logging.FileHandler("F:/abcd.log")
    h1.setLevel(logging.WARNING)
    log1.addHandler(h1)
    print("log1",log1.handlers)
    
    log2 = logging.getLogger("s.s1")
    log2.setLevel(logging.CRITICAL)
    h2 = logging.FileHandler("F:/abcd.log")
    h2.setLevel(logging.WARNING)
    log2.addHandler(h2)
    print("log2",log2.handlers)
    
    log3 = logging.getLogger("s.s1.s2")
    log3.setLevel(logging.INFO)
    print(log3.getEffectiveLevel())
    log3.warning("log3")
    print("log3",log3.handlers)
    
    
    控制台输出为:
    root [<StreamHandler <stderr> (NOTSET)>]
    root [<StreamHandler <stderr> (NOTSET)>, <StreamHandler <stderr> (WARNING)>]
    2019-11-25 14:33:03,401 s.s1.s2 log3
    log3
    root handler = <StreamHandler <stderr> (NOTSET)>,foramt = <logging.Formatter object at 0x0000000001E8ABE0>
    root handler = <StreamHandler <stderr> (WARNING)>,foramt = None
    log1 [<FileHandler F:abcd.log (WARNING)>]
    log2 [<FileHandler F:abcd.log (WARNING)>]
    20
    log3 []
    
    abcd输出为:
    log3
    log3

    Formatter

    logging的Formatter类,它允许制定某个格式的字符串,如果提供None,那么%(message)s将会作为默认值。修改上面的例子,让它看的更明显。

    import logging
    
    logging.basicConfig(format="%(name)s  %(asctime)s %(message)s",level=logging.INFO)
    
    root = logging.getLogger()
    root.setLevel(logging.ERROR)
    print("root",root.handlers)
    h0 = logging.StreamHandler()
    h0.setLevel(logging.WARNING)
    root.addHandler(h0)
    print("root", root.handlers)
    for h in root.handlers:
        print("root handler = {},formatter = {}".format(h,h.formatter))
    
    log1 = logging.getLogger("s")
    log1.setLevel(logging.ERROR)
    h1 = logging.FileHandler("F:/abcd.log")
    h1.setLevel(logging.WARNING)
    print("log1 formatter",h1.formatter)#没有设置formatter使用缺省值%(message)s
    log1.addHandler(h1)
    print("log1",log1.handlers)
    
    log2 = logging.getLogger("s.s1")
    log2.setLevel(logging.CRITICAL)
    h2 = logging.FileHandler("F:/abcd.log")
    h2.setLevel(logging.WARNING)
    print("log2 formatter",h2.formatter)
    #handler默认无formatter
    f2 = logging.Formatter("log2 %(name)s %(asctime)s %(message)s")
    h2.setFormatter(f2)
    print("log2 formatter",h2.formatter)
    log2.addHandler(h2)
    print("log2",log2.handlers)
    
    log3 = logging.getLogger("s.s1.s2")
    log3.setLevel(logging.INFO)
    print(log3.getEffectiveLevel())
    log3.warning("log3")
    print("log3",log3.handlers)
    
    控制台输出为:
    root [<StreamHandler <stderr> (NOTSET)>]
    root [<StreamHandler <stderr> (NOTSET)>, <StreamHandler <stderr> (WARNING)>]
    root handler = <StreamHandler <stderr> (NOTSET)>,formatter = <logging.Formatter object at 0x00000000026EABE0>
    root handler = <StreamHandler <stderr> (WARNING)>,formatter = None
    log1 formatter None
    log1 [<FileHandler F:abcd.log (WARNING)>]
    log2 formatter None
    log2 formatter <logging.Formatter object at 0x00000000028F0C50>
    log2 [<FileHandler F:abcd.log (WARNING)>]
    20
    log3 []
    s.s1.s2  2019-11-25 14:43:56,329 log3
    log3
    
    abcd日志输出为:
    log3
    log3
    log2 s.s1.s2 2019-11-25 14:43:56,329 log3
    log3

    Filter

    可以为handler增加过滤器,所以这种过滤器只影响某一个handle,不会影响整个处理流程,但是,如果过滤器增加到logger上,就会影响流程。

    import logging
    
    FORMAT = "%(asctime) -15s	Thread info: %(thread)d %(threadName)s %(message)s"
    logging.basicConfig(format=FORMAT,level=logging.INFO)
    
    
    log1 = logging.getLogger("s")
    log1.setLevel(logging.WARNING)#ERROR试一试
    
    h1 = logging.StreamHandler()
    h1.setLevel(logging.INFO)
    fmt1 = logging.Formatter("log1 - h1 %(message)s")
    h1.setFormatter(fmt1)
    log1.addHandler(h1)
    
    log2 = logging.getLogger("s.s1")
    #log2.setLevel(logging.CRITICAL)
    print(log2.getEffectiveLevel())#继承父的level,WARNING
    
    h2 = logging.StreamHandler()
    h2.setLevel(logging.INFO)
    
    fmt2 = logging.Formatter("log2 - h2 %(message)2")
    h2.setFormatter(fmt2)
    
    f2 = logging.Filter("s")#过滤器 s,s.s1,s.s2
    h2.addFilter(f2)
    
    log2.addHandler(h2)
    
    log2.warning("log2 warning")

    消息log2的,它的名字是s.s1,因此过滤器名字设置为s或s.s1,消息就可以通过,但是如果是其他就不能通过,不设置过滤器名字,所以消息通过。

    过滤器核心就一句,在logging.Filter方法中。

     完

  • 相关阅读:
    模块和包——Python
    异常——Python
    单例——Python
    类属性和类方法——Python
    继承和多态——Python
    私有属性和私有方法——Python
    面向对象封装案例——Python
    面相对象基础语法——Python
    类、接口作为成员变量类型——Java
    内部类的概念和分类——Java
  • 原文地址:https://www.cnblogs.com/xpc51/p/11927830.html
Copyright © 2011-2022 走看看