Python logging模块源码解析:从基础类到高级用法

Dylan
Dylan
发布时间:2026-10-13 | 3人浏览过
原创

本文深入解析Python内置logging模块的源码结构,详细阐述LogRecord、Formatter、Filter、Handler及Logger等核心类的实现原理。通过剖析源码中的关键代码片段,解释日志级别过滤、格式化处理及多Handler分发机制,并介绍LoggerAdapter的扩展用法,帮助开发者掌握日志系统的底层逻辑与高级定制技巧。

简单使用

首先,通过一段极简的代码来演示logging模块的基本工作流程。

import logging
logger = logging.getLogger()
logging.basicConfig()
logger.setLevel('DEBUG')
logger.debug('logsomething')
#输出
out>>DEBG:root:logsomething

操作分为三个步骤:首先调用logging.getLogger函数获取一个Logger实例,此时该实例尚未配置;接着使用logging.basicConfig进行默认配置,设定格式和处理器等参数;最后,通过logger.setLevel将日志级别设置为DEBUG,并调用debug方法输出一条消息,该消息将直接显示在标准输出中。

logging模块在生成日志时引入了日志级别机制,默认包含以下预设级别:

CRITICAL = 50
ERROR = 40
WARNING = 30
INFO 20
DEBUG = 10
NOTEST = 0

每个Logger实例都关联一个日志级别,仅输出高于或等于该级别的日志。例如,若Logger的级别设为INFO,则调用debug方法不会输出任何内容,而调用warning方法则可以正常输出。

通常情况下,上述六个日志级别已足以覆盖日常开发需求。

logging模块的基础类结构

作为Python的标准库,logging模块的源码位于以下路径:

#主干代码
/usr/lib/python2.7/logging/__init__.py
#扩展的handler和config
/usr/lib/pyhon2.7/logging/config.py
/usr/lib/python2.7/loging/handlers.py

构成logging核心功能的几个基础类均定义在__init__.py文件中:

第一个基础类:LogRecord

LogRecord对象代表日志中的一条具体记录,通常包含时间戳、日志级别、消息内容、当前模块名、行号及函数名等信息。可以将LogRecord对象视为一个包含上述字段的大字典:

class LogRecord(object):
 #代表一条日志的类
 def getMessage(self):
  #获取self.msg
 def markLogRecord(dict):
 #这个方法很重要,生成一个空的LogRecord,然后通过一个字典,直接更新LogReocrd中的成员变量
 rv = LogRecord(None, None, "", 0, "", (), None, None)
 rv.__dict__.update(dict)
 return rv

第二个基础类:Formatter

Formatter对象用于定义日志的输出格式。虽然LogRecord存储了大量信息,但实际打印时通常只需部分字段,Formatter即负责根据指定格式提取并格式化这些字段,其实现依赖于Python的字符串格式化功能:

#通过字典的方式,输出格式化字符串
print('%(name)s:%(num)d'%{'name':'my_name', 'num' : 100})
out >>>my_name:100
如果说LogRecord是后方的数据字典,那么Formatter就是前方的格式字符串抽象。

关键代码如下:

class Formatter(object):
 def __init__(self, fmt=None, datefmt = None):
  if fmt:
   self._fmt = fmt
  else:
   #默认的format
   self._fmt = "%(message)s"
 def format(self, record)
  #使用self._fmt进行格式化
  s = self._fmt %record.__dict__
  return s

第三个基础类:Filter和Filterer

Filter类的功能较为单一。其filter()方法接收一个LogRecord对象,若记录通过筛选则返回1,否则返回0。从代码逻辑可见,筛选主要基于LogRecord的name属性进行判断。

Filterer类维护了一个Filter对象列表,用于管理一组Filter规则。

关键代码如下:

class Filter(object):
 def __init__(self, name=''):
  self.name = name
  self.nlen = len(name)
 def filter(self, record):
  #返回1表示record通过,0表示record不通过
  if self.nlen == 0:
   return 1
  elif self.name == record.name:
   return 1
  #record.name不是以filter开头
  elif record.name.find(self.name, 0, self.nlen) != 0:
   return 0
  #最后一位是否为
  return (record.name[self.nlen] == '.')
class Filterer(object):
 #这个类其实是定义了一个self.filters = []的列表管理多个filter
 def addFilter(self, filter):
 def removefilter(self, filter):
 def filter(self, record):
 #使用列表中所有的filter进行筛选,任何一个失败都会返回0
 #例如:
  #filter.name = 'A', filter2.name='A.B', filter2.name = 'A, B, C'
  #此时record.name = 'A,B,C,D'这样的record才能通过所有filter的筛选

logging模块的高级类

基于上述基础类,可以构建出实现logging核心功能的高级类。

Handler——日志输出过程的抽象。Handler类继承自Filterer,封装了日志输出的具体逻辑。Handler类包含一个成员变量self.level,日志级别的过滤机制正是在Handler层面实现的。此外,Handler类定义了一个emit(record)方法,负责实际输出日志,该方法必须在Handler的子类中具体实现。

关键代码如下:

class Handler(Filterer):
 def __init__(self, level = NOTEST)
  #handler必须有level属性
  self.level = _checkLevel(level)
 def format(self, record):
  #使用self.formatter, formattercord
 def handler(self, record):
  #如果通过filter的筛选,则emit这条log
  rv = self.filter(record)
  self.emit(record)
 def emit(self, record):
  #等待子类去实现

接下来介绍两个简单的Handler子类。在logging源码中,handler.py文件专门定义了许多复杂的Handler实现,例如支持内存缓存的Handler或支持日志轮转(rotation)的Handler。

StreamHandler 这是最简单的Handler实现,将日志写入一个流对象,默认输出目标为sys.stderr。

关键代码如下:

class StreamHandler(Handler):
 def __init__(self, stream = None):
  if stream is None:
   stream = sys.stderr
  self.stream = stream
 def emit(self, record):
  #将record的信息写入流
  #处理一些编码的异常
  fs = '%s\n' #每条日志都有换行
  stream = self.stream
  stream.write(fs%msg)

FileHandler

用于将日志输出到文件的Handler,它继承自StreamHandler。

关键代码如下:

class FileHandler(StreamHandler):
 def __init__(self, filename, mode='a')
  #append方式打开一个文件
  StreamHandler.__init__(self, self._open())
 def emit(self, record):
  #和streamhandler保持一致
  StreamHandler.emit(self, record)

Logger——独立的日志管道

什么是Logger?

+ Logger类继承自Filterer,

+ Logger对象拥有logger.level日志级别属性,

+ Logger对象管理多个Handler:logger.handlers = [],

+ Logger对象之间存在父子层级关系。

简而言之,Logger类整合了前述的LogRecord、Filter、Formatter和Handler类。首先,Logger根据输入生成LogRecord对象,经过Filter过滤和Formatter格式化后,再通过self.handlers列表中的所有Handler将日志发送出去。

一个Logger可以关联多个Handler,从而实现将同一条日志分发到任意多个目标位置。

class Logger(Filterer):
 def __init__(self, name, level=NOTEST)
  #handler列表
  self.handlers = []
  self.level = _checklevel(level)
 def addHandler(self, hdlr):
 def removeHandler(self, hdlr):
 def _log(self, level, msg, args, exc_info=None, extra=None):
  #在_log函数中创建了一个LogRecord对象
  record = self.makeRecord(self.name, level, fn, lno, msg, args, exc_info, func, extra)
  #交给handle函数
  self.handle(record)
 def handle(self, reord):
  #进行filter,然后调用callHandlers
  if(not self.disabled) and self.filter(record):
   self.callHandlers(record)
 def callHandlers(self, record):
  #从当前logger到所有的父logger,递归的handl传入的record
  c = self
  while c:
   for hdlr in c.handlers:
    hdlr.handle(record) #进入handler的emit函数发送log
   ....
   c = c.parent

LoggerAdapter——对标准Logger的扩展

虽然LogRecord字典已提供丰富的成员变量,但若希望在日志输出时附加自定义信息(例如调用特定函数获取额外上下文),则可以使用LoggerAdapter。该类的作用即在于此。

LoggerAdapter的设计颇具巧思。若不进行任何修改,LoggerAdapter与Logger在行为上并无二致,它本质上是对Logger类的包装。

LoggerAdapter的实际用法已在成员函数process()的注释中说明:

def process(self, msg, kwargs):
 '''
 Normally,you'll only need to overwrite this one method in a LoggerAdapter subclass for your specific needs.
 '''

即通过重写process函数实现自定义逻辑,以下是一个示例:

import logging
import random
L=logging.getLogger('name')
#定义一个函数,生成0~1000的随机数
def func():
 return random.randint(1,1000)
class myLogger(logging.LoggerAdapter):
 #继承LoggerAdapter,重写process,生成随机数添加到msg前面
 def process(self,msg,kwargs):
  return '(%d),%s' % (self.extra['name'](),msg) ,kwargs
#函数对象放入字典中传入 
LA=myLogger(L,{'name':func})
#now,do some logging
LA.debug('some_loging_messsage')
out>>DEBUG:name:(167),some_loging_messsage

相关标签:

本站声明

本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。

Python 相关产品和应用

全部下载

专题列表