python日志模块-logging

Posted FuZZ

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了python日志模块-logging相关的知识,希望对你有一定的参考价值。

日志模块

logging

 
logging模块主要可以根据自定义日志信息,在程序运行的时候将日志打印在终端及记录日志到文件中。在这先了解一下logging支持的日志五个级别
debug() 调试级别,一般用于记录程序运行的详细信息
info() 事件级别,一般用于记录程序的运行过程
warnning() 警告级别,,一般用于记录程序出现潜在错误的情形
error() 错误级别,一般用于记录程序出现错误,但不影响整体运行
critical 严重错误级别 , 出现该错误已经影响到整体运行

  • 简单用法,将日志打印到终端
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
#pyversion:python3.5
#owner:fuzj

import logging

logging.debug("User %s is loging" % 'jeck')
logging.info("User %s attempted wrong password" % 'fuzj')
logging.warning("user %s attempted wrong password more than 3 times" % 'mary')
logging.error("select db is timeout")
logging.critical("server is down")

输出结果:

WARNING:root:user mary attempted wrong password more than 3 times
ERROR:root:select db is timeout
CRITICAL:root:server is down

发现怎么没有debug和info的日志呢? 原来默认情况下,logging将日志打印到屏幕,日志级别为WARNING。而debug和info在warnning级别之下,所以不打印。我们可以调整一下终端输出级别,为美观一下,自定义日志格式

#!/usr/bin/env python
# -*- coding: UTF-8 -*-
#pyversion:python3.5
#owner:fuzj

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')


logging.debug("User %s is loging" % 'jeck')
logging.info("User %s attempted wrong password" % 'fuzj')
logging.warning("user %s attempted wrong password more than 3 times" % 'mary')
logging.error("select db is timeout")
logging.critical("server is down")

输出结果

Thu, 09 Jun 2016 11:42:08 日志.py[line:13] DEBUG User jeck is loging
Thu, 09 Jun 2016 11:42:08 日志.py[line:14] INFO User fuzj attempted wrong password
Thu, 09 Jun 2016 11:42:08 日志.py[line:15] WARNING user mary attempted wrong password more than 3 times
Thu, 09 Jun 2016 11:42:08 日志.py[line:16] ERROR select db is timeout
Thu, 09 Jun 2016 11:42:08 日志.py[line:17] CRITICAL server is down

这样的输出结果看着还过得去。现在脑补一下上面用到的logging.basicConfig函数

  • logging.basicConfig

    主要可以对日志的输出格式及方式做相关配置支持的参数由:

    • filename: 指定日志文件名
    • filemode: 和file函数意义相同,指定日志文件的打开模式,'w'或'a'
      • SDAD
    • 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: 指定时间格式,同time.strftime()
    • level: 设置日志级别,默认为logging.WARNING,可选 logging.DEBUG logging.INFO logging.WARNING logging.ERROR logging.CRITICAL
    • stream: 指定将日志的输出流,可以指定输出到sys.stderr,sys.stdout或者文件,默认输出到sys.stderr,当stream和filename同时指定时,stream被忽略
  • 记录日志到文件

现在有了新需求,我不能只打印日志到终端呀,我想记录日志咋办?
很简单,使用上面的logging.basicConfig即可搞定

#!/usr/bin/env python
# -*- coding: UTF-8 -*-
#pyversion:python3.5
#owner:fuzj


import logging

#在logging.basciConfig中指定文件和写入方式
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='a')


logging.debug("User %s is loging" % 'jeck')
logging.info("User %s attempted wrong password" % 'fuzj')
logging.warning("user %s attempted wrong password more than 3 times" % 'mary')
logging.error("select db is timeout")
logging.critical("server is down")

输出效果:
此没有终端输出,发现在同级目录生成了一个test.log文件,打开看看,原来日志都已经写进去了,和上面终端输出的一样

Thu, 09 Jun 2016 11:57:41 日志.py[line:15] DEBUG User jeck is loging
Thu, 09 Jun 2016 11:57:41 日志.py[line:16] INFO User fuzj attempted wrong password
Thu, 09 Jun 2016 11:57:41 日志.py[line:17] WARNING user mary attempted wrong password more than 3 times
Thu, 09 Jun 2016 11:57:41 日志.py[line:18] ERROR select db is timeout
Thu, 09 Jun 2016 11:57:41 日志.py[line:19] CRITICAL server is down

  • 同输出屏幕和记录日志

    现在又有了新需求,我不能只把日志记录到文件,有些日志信息我还是想直接在屏幕输出,而且还不影响日志记录文件,此过程比较复杂,先准备点基础知识

    • logging的四大组件
      • Loggers 提供应用程序可直接使用的接口
      • Handlers 发送日志到适当的目的地
      • Filters 提供了过滤日志信息的方法
      • Formatters 指定日志显示格式

下面看代码

#!/usr/bin/env python
# -*- coding: UTF-8 -*-
#pyversion:python3.5
#owner:fuzj




import logging

logger = logging.getLogger("test.conf")   #创建一个logger,默认为root logger
logger.setLevel(logging.DEBUG)   #设置全局log级别为debug。注意全局的优先级最高

hterm =  logging.StreamHandler()    #创建一个终端输出的handler,设置级别为error
hterm.setLevel(logging.ERROR)

hfile = logging.FileHandler("access.log")    #创建一个文件记录日志的handler,设置级别为info
hfile.setLevel(logging.INFO)

formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')   #创建一个全局的日志格式

hterm.setFormatter(formatter)   #将日志格式应用到终端handler
hfile.setFormatter(formatter)   #将日志格式应用到文件handler


logger.addHandler(hterm)    #将终端handler添加到logger
logger.addHandler(hfile)    #将文件handler添加到logger


#定义日志msg,注意此时是logger,不是logging了
logger.debug("User %s is loging" % 'jeck')
logger.info("User %s attempted wrong password" % 'fuzj')
logger.warning("user %s attempted wrong password more than 3 times" % 'mary')
logger.error("select db is timeout")
logger.critical("server is down")


结果:
在终端输出:

2016-06-09 12:22:25,283 - test.conf - ERROR - select db is timeout
2016-06-09 12:22:25,283 - test.conf - CRITICAL - server is down

在日志文件里写入:

2016-06-09 12:22:25,283 - test.conf - INFO - User fuzj attempted wrong password
2016-06-09 12:22:25,283 - test.conf - WARNING - user mary attempted wrong password more than 3 times
2016-06-09 12:22:25,283 - test.conf - ERROR - select db is timeout
2016-06-09 12:22:25,283 - test.conf - CRITICAL - server is down

是不是很高大上?不要激动,这只是简单实现了而已。logging模块的功能远不止这些。下面将深入剖析logging的四大组件

先来干货,请看下面脚本:

#!/usr/bin/env python
# -*- coding: UTF-8 -*-
#pyversion:python3.5
#owner:fuzj


import logging


#创建logger
logger = logging.getLogger()      #创建默认logger
logger1 = logging.getLogger("testlog")     #创建一个名为testlog1的logger实例logger1
logger2 = logging.getLogger("testlog")     #创建一个名为testlog1的logger实例logger2
logger3 = logging.getLogger("testlog.child")   #创建一个testlog子实例logger3


#设置logger的日志级别
logger1.setLevel(logging.DEBUG)     #将logger1日志级别设置为debug
logger2.setLevel(logging.INFO)     #将logger1日志级别设置为info
logger3.setLevel(logging.ERROR)     #将logger1日志级别设置为warning


#创建handler
hterm = logging.StreamHandler()     #输出到终端
hfile = logging.FileHandler("test.log")  #输出到文件

#定义日志格式
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')

#将日志格式应用到handler
hterm.setFormatter(formatter)
hfile.setFormatter(formatter)

#给logger添加handler
logger.addHandler(hterm)
logger.addHandler(hfile)

logger1.addHandler(hterm)
logger1.addHandler(hfile)

logger2.addHandler(hterm)
logger2.addHandler(hfile)

logger3.addHandler(hterm)
logger3.addHandler(hfile)


#记录日志信息

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')

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

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

结果:文件和终端输出:

2016-06-09 14:55:46,136 - root - WARNING - logger warning message
2016-06-09 14:55:46,136 - root - ERROR - logger error message
2016-06-09 14:55:46,136 - root - CRITICAL - logger critical message
2016-06-09 14:55:46,136 - testlog - INFO - logger1 info message
2016-06-09 14:55:46,136 - testlog - INFO - logger1 info message
2016-06-09 14:55:46,137 - testlog - WARNING - logger1 warning message
2016-06-09 14:55:46,137 - testlog - WARNING - logger1 warning message
2016-06-09 14:55:46,137 - testlog - ERROR - logger1 error message
2016-06-09 14:55:46,137 - testlog - ERROR - logger1 error message
2016-06-09 14:55:46,137 - testlog - CRITICAL - logger1 critical message
2016-06-09 14:55:46,137 - testlog - CRITICAL - logger1 critical message
2016-06-09 14:55:46,137 - testlog - INFO - logger2 info message
2016-06-09 14:55:46,137 - testlog - INFO - logger2 info message
2016-06-09 14:55:46,137 - testlog - WARNING - logger2 warning message
2016-06-09 14:55:46,137 - testlog - WARNING - logger2 warning message
2016-06-09 14:55:46,137 - testlog - ERROR - logger2 error message
2016-06-09 14:55:46,137 - testlog - ERROR - logger2 error message
2016-06-09 14:55:46,137 - testlog - CRITICAL - logger2 critical message
2016-06-09 14:55:46,137 - testlog - CRITICAL - logger2 critical message
2016-06-09 14:55:46,137 - testlog.child - ERROR - logger3 error message
2016-06-09 14:55:46,137 - testlog.child - ERROR - logger3 error message
2016-06-09 14:55:46,137 - testlog.child - ERROR - logger3 error message
2016-06-09 14:55:46,137 - testlog.child - CRITICAL - logger3 critical message
2016-06-09 14:55:46,137 - testlog.child - CRITICAL - logger3 critical message
2016-06-09 14:55:46,137 - testlog.child - CRITICAL - logger3 critical message
  • logger

logger一个树形层级结构,输出信息之前都要获得一个Logger,logger = logging.getLogger()返回一个默认的Logger也即root Logger,并应用默认的日志级别、Handler和Formatter设置.
从输出中发现,
1)root logger由于没有定义loglevel,所以默认是warning,所以输出三天记录
2)logger1 testlog.logger 定义的loglevel是debug,怎么输出的信息是虫info开始呢? 原来logger1和logger2对应的同一个logger实例,logger2重新定义了loglevel,所以logger1和logger2对应的实例loglevel级别就成了info,小心在这里出错
3)从日志条数上看,怎么logger1 和logger2重复输出了2次,logger2重复输出了三次呢,原来和logger的结构有关,他是树形结构,logger = logging.getLogger()显示的创建了root Logger,而我们继续创建的logger1 logger2 则是root logger实例的子实例,而logger3是logger1和logger2的子实例

logger2 = logging.getLogger("testlog")     
logger3 = logging.getLogger("testlog.child")

看出来了吧,logger2对应的是testlog,而logger3对应的是testlog.child。这样可得知,一个logger不仅需要自己的handler处理消息,还会把消息发送给自己的父logger的handler。,而子logger如果没有定义日志级别、Handler、Filter等设置,会逐层继承父logger的设置,所以父logger会优先按子logger的属性进行处理,否则就按自己的处理,所以就出现了多次重复日志现象

  • handler

    负责发送相关的信息到指定目的地

    • 常用的Handler方法:

      • Handler.setLevel(lel):指定日志级别,低于lel级别的日志将被忽略
      • Handler.setFormatter():给这个handler选择一个Formatter
      • Handler.addFilter(filt)、Handler.removeFilter(filt):新增或删除一个filter对象
    • 可用的handler类型有:

      • logging.StreamHandler 可以向类似与sys.stdout或者sys.stderr的任何文件对象(file object)输出信息
      • logging.FileHandler 用于向一个文件输出日志信息
      • logging.handlers.RotatingFileHandler 类似于上面的FileHandler,但是它可以管理文件大小。当文件达到一定大小之后,它会自动将当前日志文件改名,然后创建一个新的同名日志文件继续输出
      • logging.handlers.TimedRotatingFileHandler 和RotatingFileHandler类似,不过,它没有通过判断文件大小来决定何时重新创建日志文件,而是间隔一定时间就自动创建新的日志文件
      • logging.handlers.SocketHandler 使用TCP协议,将日志信息发送到网络。
      • logging.handlers.DatagramHandler 使用UDP协议,将日志信息发送到网络。
      • logging.handlers.SysLogHandler 日志输出到syslog
      • logging.handlers.NTEventLogHandler 远程输出日志到Windows NT/2000/XP的事件日志
      • ogging.handlers.SMTPHandler 远程输出日志到邮件地址
      • logging.handlers.MemoryHandler 日志输出到内存中的制定buffer
      • logging.handlers.HTTPHandler 通过GETPOST远程输出到HTTP服务器

    具体参考:
    https://docs.python.org/3/library/logging.handlers.html#module-logging.handlers

  • filter

定义handler处理的规则
如定义一个规则

filter=logging.Filter('testlog.child')    #符合testlog.child的才能记录日志到文件
hterm.addFilter(filter)            #将filer应用到hterm的handle

执行结果:
终端输出:

2016-06-09 15:42:30,501 - testlog.child - ERROR - logger3 error message
2016-06-09 15:42:30,501 - testlog.child - ERROR - logger3 error message
2016-06-09 15:42:30,501 - testlog.child - ERROR - logger3 error message
2016-06-09 15:42:30,501 - testlog.child - CRITICAL - logger3 critical message
2016-06-09 15:42:30,501 - testlog.child - CRITICAL - logger3 critical message
2016-06-09 15:42:30,501 - testlog.child - CRITICAL - logger3 critical message

日志输出不变
此处是给logger加了filer,也可以给handler加,但是影响的酒不止一个logger了。他会影响所有使用这个handler的logger

  • formatters

用于定义输出的日志格式。用到的格式化内容可以参考logging.basicConfig()函数

#定义日志格式
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')

#将日志格式应用到handler
hterm.setFormatter(formatter)
hfile.setFormatter(formatter)

以上是关于python日志模块-logging的主要内容,如果未能解决你的问题,请参考以下文章

Python logging 模块

Python之日志处理 logging模块

Python之日志处理(logging模块)

Python——logging模块(日志模块)

Python——logging模块(日志模块)

python常用代码片段总结