zoukankan      html  css  js  c++  java
  • 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"
    " % 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 = '
    '
    
        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
    

    二、流程图

  • 相关阅读:
    机器学习中规则化和模型选择知识
    关于arcengine权限的设置
    arcengine 实现调用arctoolbox中的dissolove
    基于手机令牌的屏保软件设计与实现
    RelativeLayout中最底的View一个View.layout_marginBottom无效
    Citrus Engine简单Demo
    Java菜鸟学习笔记(23)--继承篇(二):继承与组合
    uva 579 ClockHands 几何初接触 求时针与分针的夹角
    OpenCV中GPU函数
    html监听,键盘事件
  • 原文地址:https://www.cnblogs.com/zhangliang91/p/11920643.html
Copyright © 2011-2022 走看看