赞
踩
Logging库是非常常用的记录日志库,通过logging
模块存储各种格式的日志,主要用于输出运行日志,可以设置输出日志的等级、日志保存路径、日志文件回滚等
Logging优点:
debug、info、warning、error 以及 critical
。通过赋予 logger
或者 handler
不同的级别,你就可以只输出错误消息到特定的记录文件中,或者在调试时只记录调试信息。先看下面一段代码,exam.py
- 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)
上面代码的用到了getLogger()、setLevel()、setFormatter()、StreamHandler()。后续进阶部分,我们会重点讲解这些函数。如果想系统了解python的日志写入,又不想趴英文网站,可以继续往下看,绝对详细、好理解。
根据它们用来跟踪的事件的级别或事件的严重程度命名的。以下描述了标准水平及其适用性(按严重程度的增加顺序)
可以看到,严重程度的级别依次是DEBUG<INFO<WARNING<ERROR<CRITICAL
- import logging
- logging.warning('Watch out!') # will print a message to the console
- logging.info('I told you so') # will not print anything
- import logging
- import os
- os.chdir("./") # 日志写入地址
- logging.basicConfig(filename='example.log', level=logging.DEBUG)
- # 注意:上面level设置的是显示的最低严重级别,小于level设置的最低严重级别将不会打印出来
- logging.debug('This message should go to the log file')
- logging.info('So should this')
- logging.warning('And this, too')
- logging.error('And non-ASCII stuff, too, like Øresund and Malmö')
basicConfig()函数要在debug()、info()等运行,且只要第一次运行才生效。后续的调用都是无效的。上述logging.basicConfig()写入的日志是增量的写入。如果想要覆盖之前的日志,可以设定为:
logging.basicConfig(filename='example.log',filemode='w',level=logging.DEBUG)
2. 多个模块日志调用
logger.py
- import logging
- import mylib
-
- def main():
- logging.basicConfig(filename='myapp.log', level=logging.INFO)
- logging.info('Started')
- mylib.do_something() # 这里打印的是另一个模块的日志
- logging.info('Finished')
-
- if __name__ == '__main__':
- main()
mylib.py
- import logging
-
- def do_something():
- logging.info("Doing things")
输出结果(下面打印结果中含有root,后续我们会说到怎样避免打印root)
INFO:root:Started
INFO:root:Doing something
INFO:root:Finished
:定义日志呈现中预想的日志呈现格式
- import logging
- logging.basicConfig(format='%(levelname)s:%(message)s', level=logging.DEBUG)
- logging.debug('This message should appear on the console')
- logging.info('So should this')
- logging.warning('And this, too')
输出结果:
DEBUG:This message should appear on the console
INFO:So should this
WARNING:And this, too
上面日志打印的结果格式中,少了root:。 有一系列可以用做格式化的属性,如下:
Attribute name | Format | Description |
args | You shouldn’t need to format this yourself. | The tuple of arguments merged into msg to produce message, or a dict whose values are used for the merge (when there is only one argument, and it is a dictionary). |
asctime | %(asctime)s | Human-readable time when the LogRecord was created. By default this is of the form ‘2003-07-08 16:49:45,896’ (the numbers after the comma are millisecond portion of the time). |
created | %(created)f | Time when the LogRecord was created (as returned by time.time()). |
exc_info | You shouldn’t need to format this yourself. | Exception tuple (à la sys.exc_info) or, if no exception has occurred, None. |
filename | %(filename)s | Filename portion of pathname. |
funcName | %(funcName)s | Name of function containing the logging call. |
levelname | %(levelname)s | Text logging level for the message ('DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL'). |
levelno | %(levelno)s | Numeric logging level for the message (DEBUG, INFO, WARNING, ERROR, CRITICAL). |
lineno | %(lineno)d | Source line number where the logging call was issued (if available). |
message | %(message)s | The logged message, computed as msg % args. This is set when Formatter.format() is invoked. |
module | %(module)s | Module (name portion of filename). |
msecs | %(msecs)d | Millisecond portion of the time when the LogRecord was created. |
msg | You shouldn’t need to format this yourself. | The format string passed in the original logging call. Merged with args to produce message, or an arbitrary object (see Using arbitrary objects as messages). |
name | %(name)s | Name of the logger used to log the call. |
pathname | %(pathname)s | Full pathname of the source file where the logging call was issued (if available). |
process | %(process)d | Process ID (if available). |
processName | %(processName)s | Process name (if available). |
relativeCreated | %(relativeCreated)d | Time in milliseconds when the LogRecord was created, relative to the time the logging module was loaded. |
stack_info | You shouldn’t need to format this yourself. | Stack frame information (where available) from the bottom of the stack in the current thread, up to and including the stack frame of the logging call which resulted in the creation of this record. |
thread | %(thread)d | Thread ID (if available). |
threadName | %(threadName)s | Thread name (if available). |
数据来源:https://docs.python.org/3/library/logging.html#logrecord-attributes
比如,我们将上面logging.basicConfig(format='%(levelname)s:%(message)s', level=logging.DEBUG) 修改为 logging.basicConfig(format='%(levelname)s:%(message)s:%(module)s', level=logging.DEBUG)。
输出的结果将会变为:
DEBUG:This message should appear on the console:logger
INFO:So should this:logger
WARNING:And this, too:logger
如果你想加入时间,可以试试加上%(asctime)s。
- import logging
- logging.basicConfig(format='%(asctime)s %(message)s')
- logging.warning('is when this event was logged.')
输出:
2010-12-12 11:41:42,612 is when this event was logged.
如果你希望控制输出时间的格式,可以使用datefmt.
- import logging
- logging.basicConfig(format='%(asctime)s %(message)s', datefmt='%m/%d/%Y %I:%M:%S %p')
- logging.warning('is when this event was logged.')
输出:
12/12/2010 11:46:36 AM is when this event was logged.
logging库提供了模块化的方法和几个组件,下列列出了模块定义的基础类和函数
Loggers :记录器公开应用程序代码直接使用的接口。
Handlers :处理程序将日志记录(由记录器创建)发送到相应的目标。
Filters :过滤器提供了更细粒度的工具,用于确定要输出哪些日志记录。
Formatters: 格式化程序指定最终输出中日志记录的布局。
logger = logging.getLogger(__name__)
这意味着logger的名称和包/模块层次结构一致,从logger 的名称记录事件是非常直观的方法。logger的根层级调用根logger. 根logger是函数debug()、info()、warning()、error()和critical()使用的记录器,这些函数只调用根记录器的同名方法。根logger名称会在打印时以'root'出现在输出结果中。
当然,可以将消息记录到不同的目的地。该软件包支持将日志消息写入文件、HTTP GET/POST位置、通过SMTP发送的电子邮件、通用套接字、队列或特定于操作系统的日志机制,如syslog或Windows NT事件日志。目的地由handler classes提供服务。如果有任何内置handler classes都无法满足的特殊要求,则可以创建自己的日志目标类。
默认情况下,不会为任何日志消息设置目标。可以使用basicConfig()指定目标(如控制台或文件),如上述所示。如果调用函数debug()、info()、warning()、error()和critical(),它们将检查是否未设置目标位置(如控制台或文件);如果没有设置,他们将设置控制台的目的地(sys.stderr)和显示消息的默认格式,然后再委托root logger进行实际的消息输出。
basicConfig() 设定的默认形式为:severity:logger name:message
Logger对象有三方面的工作。
在Logger对象中最常用的方方有两类:配置和消息发送。
最常用的配置方法:
举个栗子,比如我们在logging.basicConfig(format='%(levelname)s:%(message)s', level=logging.DEBUG)。这里的Level设置的是最低严重性级别DEBUG,那么,将打印所有的信息,包括DEBUG ,INFO,WARNING, ERROR和CRITICAL ;但是当上面的basicConfig(level=http://logging.INFO),则打印当中没有DEBUG,只有INFO,WARNING, ERROR和CRITICAL。这是因为level设置的最低级别是INFO,而DEBUG的严重性级别最低,所有在此不用打印。
配置logger对象后,以下方法将创建日志消息:
Handler对象负责将适当的日志消息(基于日志消息的严重性)分派到处理程序的指定目标。Handler对象可以使用addHandler()方法将零个或多个handler对象添加。应用程序可能希望将所有日志消息发送到日志文件,将所有错误或更高级别的日志消息发送到标准输出,并将所有关键消息发送到电子邮件地址。此场景需要三个单独的处理程序,每个处理程序负责将特定严重性的消息发送到特定位置。
Handler方法有很多(See here),下面主要介绍两种:StreamHandler 和 FileHandler
回到文章最开始的代码,我们看到
- fh=logging.FileHandler('test.log') # 将日志写入到test.log文件
- fh.setLevel(logging.DEBUG) # 并且需要指定写入的内容严重级别
同理,
- ch=logging.StreamHandler() # 将日志写入控制台
- ch.setLevel(loggong.DEBUG) # 并且需要指定写入的内容严重级别
除此之外,我们会发现,文中最开始的栗子exam.py中在最开始创建Logger时设置了setLevel
- logger=logging.getLogger('mylogger') # 创建logger
- logger.setLevel(logging.DEBUG) # 写入内容的严重级别
那么为什么后续在定义Handler时又做了一次set Level操作呢?原因是:Logger中设置的级别决定它将传递给Handler的消息严重性。每个Handler设置的setLevel()决定了该处理程序将发送哪些消息(记住:日志中消息是分严重程度的,当确定严重级别是某个层级时,该层级以下的消息不被发送或者记录,该层级以上的消息才被发送或者记录)。
到此,我们再次回顾一下exam.py中的代码,相信看到这里大家就明白了。
- 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)
Formatters对象配置了最终的顺序、结构和日志消息内容。
logging.Formatter.__init__(fmt=None, datefmt=None, style='%')
fmt:消息格式
datefmt:时间格式,默认为:%Y-%m-%d %H:%M:%S
程序配置日志有三种方式:
我们再举个简单的例子说明第一种方式,显示的创建Loggers、handlers和formatters
- import logging
- # 创建logger
- logger = logging.getLogger('simple_example') # logger名称
- logger.setLevel(logging.DEBUG) # 设定logger显示的严重级别
-
- # 创建一个handler,用于输出控制台,并且设定严重级别
- ch = logging.StreamHandler()
- ch.setLevel(logging.DEBUG)
-
- # 创建handler的输出格式(formatter)
- formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
-
- # 将formatter添加到handler中
- ch.setFormatter(formatter)
-
- # 将handler添加到logger中
- logger.addHandler(ch)
-
- # 输出以下内容
- # 'application' code
- logger.debug('debug message')
- logger.info('info message')
- logger.warning('warn message')
- logger.error('error message')
- logger.critical('critical message')
$ python simple_logging_module.py
#输出结果如下
2005-03-19 15:10:26,618 - simple_example - DEBUG - debug message
2005-03-19 15:10:26,620 - simple_example - INFO - info message
2005-03-19 15:10:26,695 - simple_example - WARNING - warn message
2005-03-19 15:10:26,697 - simple_example - ERROR - error message
2005-03-19 15:10:26,773 - simple_example - CRITICAL - critical message
第二种方式:引入配置文件,配置文件写在logging.conf中
- import logging
- import logging.config
- logging.config.fileConfig('logging.conf')
-
- # create logger
- logger = logging.getLogger('simpleExample')
-
- # 'application' code
- logger.debug('debug message')
- logger.info('info message')
- logger.warning('warn message')
- logger.error('error message')
- logger.critical('critical message')
配置文件的内容如下:
- [loggers]
- keys=root,simpleExample
-
- [handlers]
- keys=consoleHandler
-
- [formatters]
- keys=simpleFormatter
-
- [logger_root]
- level=DEBUG
- handlers=consoleHandler
-
- [logger_simpleExample]
- level=DEBUG
- handlers=consoleHandler
- qualname=simpleExample
- propagate=0
-
- [handler_consoleHandler]
- class=StreamHandler
- level=DEBUG
- formatter=simpleFormatter
- args=(sys.stdout,)
-
- [formatter_simpleFormatter]
- format=%(asctime)s - %(name)s - %(levelname)s - %(message)s
$ python simple_logging_module.py
#输出结果如下
2005-03-19 15:10:26,618 - simple_example - DEBUG - debug message
2005-03-19 15:10:26,620 - simple_example - INFO - info message
2005-03-19 15:10:26,695 - simple_example - WARNING - warn message
2005-03-19 15:10:26,697 - simple_example - ERROR - error message
2005-03-19 15:10:26,773 - simple_example - CRITICAL - critical message
配置文件的好处是:1.配置文件和代码的分离;2.非代码人员也能轻松定义配置文件的内容
第三种方式:dictConfig()
- version: 1
- formatters:
- simple:
- format: '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
- handlers:
- console:
- class: logging.StreamHandler
- level: DEBUG
- formatter: simple
- stream: ext://sys.stdout
- loggers:
- simpleExample:
- level: DEBUG
- handlers: [console]
- propagate: no
- root:
- level: DEBUG
- handlers: [console]
这种方式是我个人感觉非常非常赞的方式,即实现了代码和配置的分离,又简单好理解。
最后,总结一下整个操作流(官网上down下来的):
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。