python logging模块源码分析


阅读目录

一、源码分析

二、流程图

一、源码分析

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

二、流程图

posted @ 2019-11-23 22:49  初遇ぃ  阅读(1092)  评论(0编辑  收藏  举报
//一下两个链接最好自己保存下来,再上传到自己的博客园的“文件”选项中 //一下两个链接最好自己保存下来,再上传到自己的博客园的“文件”选项中