在 Python 中,怎样才能算作一个比较标准的日志记录过程呢?或许很多人会使用 print 语句输出一些运行信息,然后再在控制台观察,运行的时候再将输出重定向到文件输出流保存到文件中,这样其实是非常不规范的,在 Python 中有一个标准的 logging 模块,我们可以使用它来进行标注的日志记录,利用它我们可以更方便地进行日志记录,同时还可以做更方便的级别区分以及一些额外日志信息的记录,如时间、运行模块信息等。
logging模块是Python的一个标准库模块,开发过程中,可以通过该模块,灵活的完成日志的记录。
logging模块提供了两种记录日志的方式:
1)使用logging提供的模块级别的函数(logging.basicConfig,logging.debug,logging.info…)
2)使用logging模块的组件(loggers,handlers,filters,formatters)这里建议使用第二种方式
DEBUG < INFO < WARNING < ERROR < CRITICAL
日志级别(level) | 描述 |
---|---|
DEBUG | 调试级别,一般用于问题的排查,日志的信息最为详细 |
INFO | 仅记录普通的信息,日志信息的详细程度仅次于DEBUG |
WARNING | 警告信息,一般这类信息不会影响程序的正常运行 |
ERROR | 错误信息, 出现错误信息时,程序一般已不能正常运行 |
CRITICAL | 更严重的错误信息,程序不能继续运行 |
import logging logging.info('This is a log info') logging.debug('Debugging') logging.warning('Warning exists') logging.info('Finish')
我们发现 DEBUG 的信息是没有输出的,这是因为我们在全局配置的时候设置了输出为 INFO 级别,所以 DEBUG 级别的信息就被过滤掉了。 这时如果我们将输出的日志级别设置为 DEBUG,就可以看到 DEBUG 级别的日志输出了:
logging.basicConfig(level=logging.DEBUG, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
basicConfig是logging模块的全局配置方法
关键字 | 描述 |
---|---|
filename | 创建一个FileHandler,使用指定的文件名,而不是使用StreamHandler。 |
filemode | 如果指明了文件名,指明打开文件的模式(如果没有指明filemode,默认为'a')。 |
format | handler使用指明的格式化字符串。 |
datefmt | 使用指明的日期/时间格式。 |
level | 指明根logger的级别。 |
stream | 使用指明的流来初始化StreamHandler。该参数与'filename'不兼容,如果两个都有,'stream'被忽略。 |
格式 | 描述 |
---|---|
%(levelno)s | 打印日志级别的数值 |
%(levelname)s | 打印日志级别名称 |
%(pathname)s | 打印当前执行程序的路径 |
%(filename)s | 打印当前执行程序名称 |
%(funcName)s | 打印日志的当前函数 |
%(lineno)d | 打印日志的当前行号 |
%(asctime)s | 打印日志的时间 |
%(thread)d | 打印线程id |
%(threadName)s | 打印线程名称 |
%(process)d | 打印进程ID |
%(message)s | 打印日志信息 |
import logging logging.basicConfig(level=logging.DEBUG, filename='output.log', datefmt='%Y/%m/%d %H:%M:%S', format='%(asctime)s - %(name)s - %(levelname)s - %(lineno)d - %(module)s - %(message)s') logging.info('This is a log info') logging.debug('Debugging') logging.warning('Warning exists') logging.info('Finish')
组件名称 | 功能描述 |
---|---|
Logger | 日志器,提供了应用程序可一直使用的接口 |
Handler | 负责日志记录的传输目的地,比如有FileHandler(写入目标为文件)和StreamHandler(写入目标为流,默认为标准输出流) |
Filter | 负责过滤哪些日志是要输出的,若有多个 Logger,可根据名称过滤出指定的 Logger 来记录日志 |
Formatter | 负责对日志输出格式的格式化 |
1. Logger
1)logging.getLogger(name) 指定name,返回一个名称为name的Logger实例。如果再次使用相同的名字,是实例化一个对象。未指定name,返回Logger实例,名称是root,即根Logger。
2)创建一个或多个 handler,用于指定日志信息的输出流向
3)创建一个或多个 formatter,指定日志的格式,并分别将 formatter 绑定到 上
4)将 handler 绑定到 logger对象 上
5)logger.setLevel(logging.DEBUG) 设置日志级别
import logging logger = logging.getLogger(__name__) logger.setLevel(level=logging.WARN) # Log logger.debug('Debugging') logger.critical('Critical Something') logger.error('Error Occurred') logger.warning('Warning exists') logger.info('Finished')
2.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 服务器。
import logging logger = logging.getLogger(__name__) logger.setLevel(level=logging.DEBUG) # StreamHandler stream_handler = logging.StreamHandler(sys.stdout) stream_handler.setLevel(level=logging.DEBUG) logger.addHandler(stream_handler) # FileHandler file_handler = logging.FileHandler('output.log') file_handler.setLevel(level=logging.INFO) formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') file_handler.setFormatter(formatter) logger.addHandler(file_handler) # Log logger.info('This is a log info') logger.debug('Debugging') logger.warning('Warning exists') logger.info('Finish')
3.Formatter
在进行日志格式化输出的时候,我们可以不借助于 basicConfig 来全局配置格式化输出内容,可以借助于 Formatter 来完成,下面我们再来单独看下 Formatter 的用法:
import logging logger = logging.getLogger(__name__) logger.setLevel(level=logging.WARN) formatter = logging.Formatter(fmt='%(asctime)s - %(name)s - %(levelname)s - %(message)s', datefmt='%Y/%m/%d %H:%M:%S') handler = logging.StreamHandler() handler.setFormatter(formatter) logger.addHandler(handler) # Log logger.debug('Debugging') logger.critical('Critical Something') logger.error('Error Occurred') logger.warning('Warning exists') logger.info('Finished')
在这里我们指定了一个 Formatter,并传入了 fmt 和 datefmt 参数,这样就指定了日志结果的输出格式和时间格式,然后 handler 通过 setFormatter () 方法设置此 Formatter 对象即可
设置format中特殊字符asctime(日期时间)的输出格式
- 特殊字符:
- %y 两位数的年份表示(00-99)
- %Y 四位数的年份表示(000-9999)
- %m 月份(01-12)
- %d 月内中的一天(0-31)
- %H 24小时制小时数(0-23)
- %I 12小时制小时数(01-12)
- %M 分钟数(00=59)
- %S 秒(00-59)
使用自己想要的分隔符和顺序来定义日期时间的格式,例如:
datefmt='%m/%d/%Y %I:%M:%S %p'
4.Filter
决定一个日志记录是否发送到handler。
import logging logger1 = logging.getLogger('aaa') logger2 = logging.getLogger('bbb') # 定义一个 filter filter = logging.Filter(name='bbb') # 定义一个 handler ch = logging.StreamHandler() formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') ch.setFormatter(formatter) # 若两个 logger对象 的日志级别相同,且都是用通过一个 handler,可以在这个 handler 上设置日志级别 ch.setLevel(logging.ERROR) # 在 handler 上放置过滤器 ch.addFilter(filter) logger1.addHandler(ch) logger2.addHandler(ch) logger1.error('logger1 error message') logger2.error('logger2 error message')
因为对handler增加一个过滤器,过滤器就像一个筛子,只筛选出我们想要的日志,上图代码中的过滤器只接收名为bbb的loger产生的日志