Skip to content
On this page

日志处理

logging模块中封装了基础的日志功能,它提供了以下两种方式来记录日志:

  • 使用logging日志模块函数简单记录日志
  • 使用logging日志系统组件记录日志

前者封装了后者相关内容,提供了更简单的使用方式。

1. logging 简单日志

1.1 记录日志

py
import logging

logging.debug('debug log')
logging.info('info log')
logging.warning('warning log')
logging.error('error log')
logging.critical('critical log')

以上logging.debug()等方法的定义中,除了msgargs参数,还有一个**kwargs参数支持3个关键字参数exc_info, stack_info, extra

可选命名参数说明
exc_infobool。若为True会将异常信息添加到日志消息中
stack_infobool。若为True,堆栈信息将会被添加到日志信息中
extradict。用来定义消息格式中所含的字段

1.2 简单配置

logging.basicConfig()用于对日志进行简单配置。

basicConfig 参数描述
level设置日志最低输出级别,默认为logging.WARNING(枚举)
format格式化日志输出。可选字段格式见下表
filename设置日志保存文件
filemode设置日志文件写入方式。w表示覆盖写入,a表示追加写入
stream日志输出目标stream,如sys.stdout、sys.stderr以及网络stream
handlers日志处理程序列表

filename、stream和handlers这三个配置项只能同时存在一项,否则会引发ValueError异常。

format 字段格式描述
%(levelname)s级别名称(被filter修改后的)
%(levelno)s日志记录的数字形式的日志级别(10, 20, 30, 40, 50)
%(name)slogger名称,默认为root
%(pathname)s日志产生模块的完整路径名
%(filename)spathname的文件名部分
%(module)sfilename无后缀
%(lineno)d当前行号
%(funcName)s日志产生的函数名
%(message)s日志内容
%(asctime)s日志产生时间
%(process)s当前进程ID
%(processName)s当前进程名称
%(thread)s当前线程ID
%(threadName)s当前线程名称
py
import logging

logging.basicConfig(level=logging.DEBUG,
                    format='%(asctime)s - %(filename)s[line:%(lineno)d] - %(levelname)s: %(message)s',
                    filename="log.txt",
                    filemode="a")

logging.warning('this is a warning log')

程序中只需要进行一次日志配置,即可全局生效,一般在程序入口做统一配置

2. logging 组件

组件说明
loggers提供应用程序代码直接使用的接口
handlers用于将日志记录发送到指定的目的位置
filters提供更细粒度的日志过滤功能,用于决定哪些日志记录将会被输出
formatters用于控制日志信息的最终输出格式

loggers负责采集日志,handlers负责处理日志,如发送异常信息,每个handler可以定制filters过滤日志以及formatters格式化日志。

2.1 Logger

logging.getLogger([name])可以获取一个Logger对象,默认名为root

方法描述
setLevel()设置最低日志级别。低于此级别日志将被丢弃
addHandler() / removeHandler()新增/删除一个handler
addFilter() / removeFilter()新增/删除一个filter
debug() / info() / warning() / error() / critical()记录不同级别日志
exception()记录异常日志(含堆栈追踪信息),通常exception handler中使用
  • Logger对象通过其名称构成层级结构。其名称以'.'分割,'.'后面的logger是前面的children,如,名为 foo.bar, foo.bar.bazfoo.bamlogger都是 foochildren
  • Logger对象都有一个有效等级(effective level),若未显式设置则将从其祖先继承。root默认level为WARNING
  • Logger对象的handler处理完成后,会沿记成链继续向上传递。因此root绑定的handler可以捕捉到所有日志。可以设置logger.propagate=False关闭某个对象的传递机制。

2.2 Handler

Handler对象的作用是(基于日志消息的level)将消息分发到handler指定的位置(文件、网络、邮件等)。Logger对象添加任意个Handler对象。

方法描述
setLevel()设置最低日志级别。低于此级别日志将被丢弃
setFormatter()设置一个formatter
addFilter() / removeFilter()添加/删除一个filter

Handler是一个抽象类,不应被实例化,其常用示例子类如下表。

Handler描述
logging.StreamHandler将日志发送到输出到Stream,如std.out, std.err或任何file-like对象。
logging.FileHandler将日志发送到磁盘文件,默认情况下文件大小会无限增长
logging.NullHandler忽略error messages。可以避免No handlers could be found for logger XXX错误
logging.handlers.RotatingFileHandler将日志发送到磁盘文件,并支持日志文件按大小切割
logging.hanlders.TimedRotatingFileHandler将日志发送到磁盘文件,并支持日志文件按时间切割
logging.handlers.SocketHandler将日志发送到TCP Socket
logging.handlers.DatagramHandler将日志发送到UDP Socket
logging.handlers.HTTPHandler将日志发送到web服务器(支持GET和POST)
logging.handlers.SMTPHandler将日志发送到email
logging.handlers.QueueHandler将日志添加到指定多进程队列

2.3 Formater

Formater用于配置日志信息的最终顺序、结构和内容。可以直接实例化Formatter类。也可以实现一个Formatter的子类来完成自定义处理行为。

py
Formatter(fmt=None, datefmt=None, style='%')
  • fmt。消息格式化字符串,默认为message原始值
  • datefmt。日期格式字符串,默认为%Y-%m-%d %H:%M:%S
  • style。可取值为%,{,$,默认为%

2.4 Filter

Filter可以被HandlerLogger用来做比level更细粒度的、更复杂的过滤功能。Filter是一个过滤器基类,它只允许某个logger层级下的日志事件通过过滤。

py
class logging.Filter(name='')
    filter(record)

如,一个filter实例化时传递的name参数值为'A.B',那么该filter实例将只允许名称为类似如下规则的loggers产生的日志记录通过过滤:'A.B','A.B,C','A.B.C.D','A.B.D',而名称为'A.BB', 'B.A.B'的loggers产生的日志则会被过滤掉。如果name的值为空字符串,则允许所有的日志事件通过过滤。

filter方法用于具体控制传递的record记录是否能通过过滤,如果该方法返回值为0表示不能通过过滤,返回值为非0表示可以通过过滤。如果有需要,也可以在filter(record)方法内部改变该record,比如添加、删除或修改一些属性。我们还可以通过filter做一些统计工作,比如可以计算下被一个特殊的loggerhandler所处理的record数量等。

3. 日志处理

3.1 日志处理流程

使用logging四大组件处理日志的简要流程如下:

  1. 创建一个logger
  2. 设置logger的日志的等级
  3. 创建合适的Handler
  4. 设置每个Handler的日志等级
  5. 创建日志的Formatter
  6. Handler中添加Formatter
  7. 将上面创建的Handler添加到logger
  8. 输出日志

一条日志信息要想被最终输出需要依次经过以下几次过滤:

  • logger等级过滤
  • logger.filter过滤
  • logger.handler等级过滤
  • logger.handler.filter过滤

3.2 记录日志(组件方式)

py
import logging
from logging import handlers

# 创建logger
logger = logging.getLogger("colin_logger")  # 如果参数为空则返回root logger
logger.setLevel(logging.WARNING)  # 设置logger日志等级

# 创建handler
stream_handler = logging.StreamHandler()  # 打印到控制台
socket_handler = handlers.SocketHandler("192.168.0.200", 5000)  # 发送到指定TCP服务器

# 设置输出日志格式
formatter = logging.Formatter(fmt="%(asctime)s - %(filename)s[line:%(lineno)d] - %(levelname)s: %(message)s")

# 为handler指定输出格式,注意大小写
stream_handler.setFormatter(formatter)
socket_handler.setFormatter(formatter)

# 为logger添加的日志处理器
logger.addHandler(socket_handler)
logger.addHandler(stream_handler)

# 输出不同级别的log
logger.warning("this is a warning log")
try:
    print(colin)  # 测试异常
except Exception as ex:
    logger.error(ex, exc_info=1, stack_info=1)  # 记录错误日志,同时记录异常和堆栈信息

4. 日志配置

配置日志输出有以下三种方式。

  • 代码显式的创建loggers, handlersformatters并分别调用它们的配置函数
  • 创建一个日志配置文件,然后使用fileConfig()函数来读取该文件的内容
  • 创建一个包含配置信息的dict,然后把它传递个dictConfig()函数

三种方式更详尽内容参阅 https://www.cnblogs.com/yyds/p/6885182.html

5. 记录上下文信息

除了传递给日志记录函数的参数外,有时候我们还想在日志输出中包含一些额外的上下文信息。比如,在一个网络应用中,可能希望在日志中记录客户端的特定信息,如:远程客户端的IP地址和用户名。这里我们来介绍以下几种实现方式:

  • 通过向日志记录函数传递一个extra参数引入上下文信息
  • 使用LoggerAdapters引入上下文信息
  • 使用Filters引入上下文信息

三种方式更详尽内容参阅 https://www.cnblogs.com/yyds/p/6897964.html

6. 分布式日志

如果需要使用分布式日志,可以借助loggingHTTPHandler/SocketHandler/DatagramHandler等组件发送日志到分布式日志服务器监听地址。

ELK作为流行的分布式日志框架,可以借助Docker快速搭建

分布式日志服务器搭建和使用参见 https://architecture.a-nomad.com/log/elk.html

Released under the MIT License.