阅读目录
一、源码分析
二、流程图
一、源码分析
1 创建logger对象
logger = logging.getLogger(__name__)
# 1.加载文件,创建以下单例对象
root = RootLogger(WARNING)
Logger.root = root
Logger.manager = Manager(Logger.root)
_loggerClass = Logger
# 2.getLogger()
if name:
return Logger.manager.getLogger(name)
else:
return root
# 3.看一下getLogger()干了些什么
class Manager(object):
def __init__(self, rootnode):
"""
Initialize the manager with the root node of the logger hierarchy.
"""
self.root = rootnode
self.disable = 0
self.emittedNoHandlerWarning = False
self.loggerDict = {}
self.loggerClass = None
self.logRecordFactory = None
def getLogger(self, name):
rv = None
if not isinstance(name, str):
raise TypeError(\'A logger name must be a string\')
_acquireLock() # 加了一个可重入锁,因为下面会操作字典,保证线程安全
try:
if name in self.loggerDict: # self.loggerDict第一次初始化为空,所有会走else语句
rv = self.loggerDict[name]
if isinstance(rv, PlaceHolder):
ph = rv
rv = (self.loggerClass or _loggerClass)(name)
rv.manager = self
self.loggerDict[name] = rv
self._fixupChildren(ph, rv)
self._fixupParents(rv)
else:
rv = (self.loggerClass or _loggerClass)(name) # self.loggerClass第一次初始化为空,所以调用的是Logger单例初始化
rv.manager = self
self.loggerDict[name] = rv # 在初始化字典内添加了 {__name__:logger_obj}
self._fixupParents(rv) # 很重要的一步,看一下吧
finally:
_releaseLock()
return rv
# 3. _fixupParents() 绑定上一级logger对象
def _fixupParents(self, alogger):
name = alogger.name
i = name.rfind(".") #第一次进来,__name__对应__main__也是个字符串,所有i=0
rv = None
while (i > 0) and not rv:
substr = name[:i]
if substr not in self.loggerDict:
self.loggerDict[substr] = PlaceHolder(alogger)
else:
obj = self.loggerDict[substr]
if isinstance(obj, Logger):
rv = obj
else:
assert isinstance(obj, PlaceHolder)
obj.append(alogger)
i = name.rfind(".", 0, i - 1)
if not rv:
rv = self.root
# 给logger对象绑定一个parent属性指向另一个logger对象,也就形成了一个单向链表
# 第一次进来rv是RootLogger,所以RootLogger应该为整个链表的头节点
alogger.parent = rv
2 logger.debug()
class Logger(Filterer):
def __init__(self, name, level=NOTSET):
"""
Initialize the logger with a name and an optional level.
"""
Filterer.__init__(self)
self.name = name # 此时name = __main__
self.level = _checkLevel(level)
self.parent = None
self.propagate = True
self.handlers = []
self.disabled = False
self._cache = {}
# 1.看一下调用的debug做了什么
def debug(self, msg, *args, **kwargs):
# 循环判断多级(parent)level是否有大于当前设置的level,没有则退出结束流程
if self.isEnabledFor(DEBUG):
self._log(DEBUG, msg, args, **kwargs)
# 2. 看一下_log干了些啥
def _log(self, level, msg, args, exc_info=None, extra=None, stack_info=False):
sinfo = None
if _srcfile: # 获取到当前文件的路径
try:
# 3.查看是被谁调用的,fn-文件路径,lno-代码行,func-module,sinfo-缓存获取的数据
fn, lno, func, sinfo = self.findCaller(stack_info)
except ValueError: # pragma: no cover
fn, lno, func = "(unknown file)", 0, "(unknown function)"
else: # pragma: no cover
fn, lno, func = "(unknown file)", 0, "(unknown function)"
# 4.处理异常信息,为空不走
if exc_info:
if isinstance(exc_info, BaseException):
exc_info = (type(exc_info), exc_info, exc_info.__traceback__)
elif not isinstance(exc_info, tuple):
exc_info = sys.exc_info()
# 5.开始了新大陆,创建一条记录
record = self.makeRecord(self.name, level, fn, lno, msg, args,
exc_info, func, extra, sinfo)
# 6. 创建的record对象交给handle处理
self.handle(record)
# 5.看看record干了些什么
def makeRecord(self, name, level, fn, lno, msg, args, exc_info,
func=None, extra=None, sinfo=None):
# 5.1这里去初始化了一个新的对象class LogRecord(object):我们在下面在展开谈
rv = _logRecordFactory(name, level, fn, lno, msg, args, exc_info, func,
sinfo)
if extra is not None:
for key in extra:
if (key in ["message", "asctime"]) or (key in rv.__dict__):
raise KeyError("Attempt to overwrite %r in LogRecord" % key)
rv.__dict__[key] = extra[key]
return rv
# 6.看看handle干了些什么
def handle(self, record):
# disabled走到这还是False, 紧接着就去过筛选器了
if (not self.disabled) and self.filter(record):
# 6.2 将记录器传递给所有相关处理程序处理
self.callHandlers(record)
# 6.1 这里可以继续去探索logging模块的主要组成对象Filter功能
def filter(self, record):
rv = True # 默认返回True
for f in self.filters:
if hasattr(f, \'filter\'):
result = f.filter(record)
else:
result = f(record) # assume callable - will raise if not
if not result:
rv = False
break
return rv
# 6.2 handler处理器处理
def callHandlers(self, record):
c = self
found = 0
# 循环遍历此记录器及其父级的所有处理程序
while c:
# 这里如果有自定制的handler(FileHandler,StreamHandler),肯定会有值
for hdlr in c.handlers:
found = found + 1
if record.levelno >= hdlr.level:
hdlr.handle(record) # 根据不同的定制handler走到对应的handle方法
# propagate=False 则记录器为最高级别
if not c.propagate:
c = None #break out
else:
c = c.parent
# 没有设置handler处理
if (found == 0):
if lastResort: # 其实就是去初始化了一个logging的另一个对象Handler
if record.levelno >= lastResort.level:
lastResort.handle(record) # 最后在来刨一下Handler源码,见小节4
elif raiseExceptions and not self.manager.emittedNoHandlerWarning:
sys.stderr.write("No handlers could be found for logger"
" \\"%s\\"\\n" % self.name)
self.manager.emittedNoHandlerWarning = True
3 LogRecord(object)
class LogRecord(object):
"""初始化了很多变量用来在之后记录log,包括去获取线程,进程的名称,pid等"""
def __init__(self, name, level, pathname, lineno,
msg, args, exc_info, func=None, sinfo=None, **kwargs):
ct = time.time()
self.name = name
self.msg = msg
if (args and len(args) == 1 and isinstance(args[0], collections.abc.Mapping)
and args[0]):
args = args[0]
self.args = args
self.levelname = getLevelName(level)
self.levelno = level
self.pathname = pathname
try:
self.filename = os.path.basename(pathname)
self.module = os.path.splitext(self.filename)[0]
except (TypeError, ValueError, AttributeError):
self.filename = pathname
self.module = "Unknown module"
self.exc_info = exc_info
self.exc_text = None # used to cache the traceback text
self.stack_info = sinfo
self.lineno = lineno
self.funcName = func
self.created = ct
self.msecs = (ct - int(ct)) * 1000
self.relativeCreated = (self.created - _startTime) * 1000
if logThreads:
self.thread = threading.get_ident()
self.threadName = threading.current_thread().name
else: # pragma: no cover
self.thread = None
self.threadName = None
if not logMultiprocessing: # pragma: no cover
self.processName = None
else:
self.processName = \'MainProcess\'
mp = sys.modules.get(\'multiprocessing\')
if mp is not None:
# Errors may occur if multiprocessing has not finished loading
# yet - e.g. if a custom import hook causes third-party code
# to run when multiprocessing calls import. See issue 8200
# for an example
try:
self.processName = mp.current_process().name
except Exception: #pragma: no cover
pass
if logProcesses and hasattr(os, \'getpid\'):
self.process = os.getpid()
else:
self.process = None
4 Handler(Filterer)
class Handler(Filterer):
def __init__(self, level=NOTSET):
Filterer.__init__(self)
self._name = None
self.level = _checkLevel(level)
self.formatter = None
# Add the handler to the global _handlerList (for cleanup on shutdown)
_addHandlerRef(self)
self.createLock()
def handle(self, record):
rv = self.filter(record) # Filter过滤,默认True
if rv:
self.acquire()
try:
# 需要注意的是此时self是自定制的handler对象,如果没有则是lastResort继承的StreamHandler
self.emit(record) # 最重要的方法,emit处理我们记录器里面放置的那些信息
finally:
self.release()
return rv
5 StreamHandler(Handler) 流处理
class StreamHandler(Handler):
terminator = \'\\n\'
def __init__(self, stream=None):
"""
Initialize the handler.
If stream is not specified, sys.stderr is used.
"""
Handler.__init__(self)
if stream is None:
stream = sys.stderr # 默认终端输出
self.stream = stream
def emit(self, record):
try:
msg = self.format(record) # 格式化输出format处理就不展开阐述了
stream = self.stream
stream.write(msg) # 发出信息
stream.write(self.terminator) # 打了个换行符
self.flush() # 默认是sys.stderr,就是告诉终端立即输出
except Exception:
self.handleError(record)
6 FileHandler(Handler) 文件处理
class FileHandler(StreamHandler):
"""
A handler class which writes formatted logging records to disk files.
"""
def __init__(self, filename, mode=\'a\', encoding=None, delay=False):
filename = os.fspath(filename)
self.baseFilename = os.path.abspath(filename)
self.mode = mode
self.encoding = encoding
self.delay = delay
if delay:
Handler.__init__(self)
self.stream = None
else:
StreamHandler.__init__(self, self._open())
def emit(self, record):
if self.stream is None:
self.stream = self._open() # 打开了一个文件流,但是并没有去写到文件
# 判断是否还需要打印到终端
StreamHandler.emit(self, record)
def _open(self):
"""
Open the current base file with the (original) mode and encoding.
Return the resulting stream.
"""
return open(self.baseFilename, self.mode, encoding=self.encoding)
"""关闭hanlder处理时候回调钩子"""
def shutdown(handlerList=_handlerList):
for wr in reversed(handlerList[:]):
#errors might occur, for example, if files are locked
#we just ignore them if raiseExceptions is not set
try:
h = wr()
if h:
try:
h.acquire()
h.flush() # 如果是文件就会触发文件对象的flush方法,一次性把日志内容都写入了
h.close() # 清除流,关闭文件,清楚缓存区等操作
except (OSError, ValueError):
pass
finally:
h.release()
except: # ignore everything, as we\'re shutting down
if raiseExceptions:
raise
二、流程图