Python 日志打印之logging.getLogger源码分析

使用Jenkins+Blue Ocean 持构建自动化部署之安卓源码打包、测试、邮件通知

日志打印之logging.getLogger源码分析

By:授客 QQ:1033553122

#实践环境

WIN 10

Python 3.6.5

 

#函数说明

logging.getLogger(name=None)

getLogger函数位于logging/__init__.py脚本

 

#源码分析

 

 

_loggerClass = Logger
# ...略

root = RootLogger(WARNING)
Logger.root = root
Logger.manager = Manager(Logger.root)

# ...略

def getLogger(name=None):
    """
    Return a logger with the specified name, creating it if necessary.

    If no name is specified, return the root logger.
    """
    if name:
        return Logger.manager.getLogger(name)
    else:
        return root 

 

 

 

 

结论:如函数注释所述,如果调用getLogger时,如果没有指定函数参数(即要获取的日志打印器名称)或者参数值不为真,则默认返回root打印器

 

 

##Logger.manager.getLogger(self, name)源码分析

 

立完flag,你可能需要对flag进行量化

该函数位于logging/__init__.py脚本

 

 

class Manager(object):
    """
    There is [under normal circumstances] just one Manager instance, which
    holds the hierarchy of loggers.
    """
    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):
        """
        Get a logger with the specified name (channel name), creating it
        if it doesn't yet exist. This name is a dot-separated hierarchical
        name, such as "a", "a.b", "a.b.c" or similar.

        If a PlaceHolder existed for the specified name [i.e. the logger
        didn't exist but a child of it did], replace it with the created
        logger and fix up the parent/child references which pointed to the
        placeholder to now point to the logger.
        """
        rv = None
        if not isinstance(name, str):
            raise TypeError('A logger name must be a string')
        _acquireLock()
        try:
            if name in self.loggerDict:
                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) # _loggerClass = Logger
                rv.manager = self
                self.loggerDict[name] = rv
                self._fixupParents(rv)
        finally:
            _releaseLock()
        return rv

  

 

###Logger源码分析

 

_nameToLevel = {
    'CRITICAL': CRITICAL,
    'FATAL': FATAL,
    'ERROR': ERROR,
    'WARN': WARNING,
    'WARNING': WARNING,
    'INFO': INFO,
    'DEBUG': DEBUG,
    'NOTSET': NOTSET,
}

# ...略

def _checkLevel(level):
    if isinstance(level, int):
        rv = level
    elif str(level) == level:
        if level not in _nameToLevel:
            raise ValueError("Unknown level: %r" % level)
        rv = _nameToLevel[level]
    else:
        raise TypeError("Level not an integer or a valid string: %r" % level)
    return rv

# ...略
class PlaceHolder(object):
    """
    PlaceHolder instances are used in the Manager logger hierarchy to take
    the place of nodes for which no loggers have been defined. This class is
    intended for internal use only and not as part of the public API.
    """
    def __init__(self, alogger):
        """
        Initialize with the specified logger being a child of this placeholder.
        """
        self.loggerMap = { alogger : None }

    def append(self, alogger):
        """
        Add the specified logger as a child of this placeholder.
        """
        if alogger not in self.loggerMap:
            self.loggerMap[alogger] = None



class Logger(Filterer):
    """
    Instances of the Logger class represent a single logging channel. A
    "logging channel" indicates an area of an application. Exactly how an
    "area" is defined is up to the application developer. Since an
    application can have any number of areas, logging channels are identified
    by a unique string. Application areas can be nested (e.g. an area
    of "input processing" might include sub-areas "read CSV files", "read
    XLS files" and "read Gnumeric files"). To cater for this natural nesting,
    channel names are organized into a namespace hierarchy where levels are
    separated by periods, much like the Java or Python package namespace. So
    in the instance given above, channel names might be "input" for the upper
    level, and "input.csv", "input.xls" and "input.gnu" for the sub-levels.
    There is no arbitrary limit to the depth of nesting.
    """
    def __init__(self, name, level=NOTSET):
        """
        Initialize the logger with a name and an optional level.
        """
        Filterer.__init__(self)
        self.name = name
        self.level = _checkLevel(level)
        self.parent = None
        self.propagate = True
        self.handlers = []
        self.disabled = False

    # ... 略

  

 

 

结论:如果调用logging.getLogger()时,有指定日志打印器名称,且名称为真(不为空字符串,0,False等False值),

1)如果名称为不存在的日志打印器名称,则,且参数值为真,但是即要获取的日志打印器名称)或者参数值不为真,则创建一个名为给定参数值的日志打印器,该日志打印器,默认级别默认为NOTSET,disable_existing_loggers配置为False,propagate配置为True。然后在日志打印器字典中记录该名称和日志打印器的映射关系,接着调用 _fixupParents(创建的日志打印器实例)类实例方法–为日志打印器设置上级日志打印器,最后返回该日志打印器。

2)如果名称已存在日志打印器名称,则获取该日志打印器,然后判断日志打印器是否为PlaceHolder类实例,如果是,则创建一个名为所给参数值的日志打印器,同第1)点,该日志打印器,默认级别默认为NOTSET,disable_existing_loggers配置为False,propagate配置为True。然后在日志打印器字典中记录该名称和日志打印器的映射关系,接着调用 _fixupParents(创建的打印器实例)类实例方法,_fixupChildren(PlaceHolder类实例–根据名称获取的日志打印器,新建的日志打印器实例)–为新建日志打印器设置上级日志打印器,PlaceHolder类实例现有下级PlaceHolder日志打印器实例重新设置上级日志打印器,最后返回该日志打印器。

 

 

###_fixupParents及_fixupChildren函数源码分析

# _fixupParents

# ...略
class Manager(object):
    # ...略
    def _fixupParents(self, alogger):
        """
        Ensure that there are either loggers or placeholders all the way
        from the specified logger to the root of the logger hierarchy.
        """
        name = alogger.name # 获取日志打印器名称
        i = name.rfind(".") 
        rv = None # 存放alogger日志打印器的上级日志打印器
        while (i > 0) and not rv: # 如果名称中存在英文的点,并且找到上级日志打印器
            substr = name[:i] # 获取名称中位于最后一个英文的点的左侧字符串(暂且称至为 点分上级)
            if substr not in self.loggerDict: # 如果 点分上级 不存在日志打印器字典中
                self.loggerDict[substr] = PlaceHolder(alogger) # 创建PlaceHolder实例作为 点分上级 对应的日志打印器 # 继续查找点分上级日志打印器 # 注意,这里的PlaceHolder仅是占位用,不是真的打印器,这里为了方便描述,暂且称之为PlaceHolder日志打印器
            else: # 否则
                obj = self.loggerDict[substr] # 获取 点分上级 对应的日志打印器
                if isinstance(obj, Logger): # 如果为Logger实例,如果是,则跳出循环,执行 # 为日志打印器设置上级
                    rv = obj
                else: # 否则
                    assert isinstance(obj, PlaceHolder) # 断言它为PlaceHolder的实例
                    obj.append(alogger) # 把日志打印器添加为点分上级对应的PlaceHolder日志打印器实例的下级日志打印器 执行 # 继续查找点分上级日志打印器
            i = name.rfind(".", 0, i - 1) # 继续查找点分上级日志打印器
        if not rv: # 找不到点分上级、或者遍历完所有点分上级,都没找到上级日志打印器
            rv = self.root # 则 把root日志打印器设置为alogger日志打印器的上级日志打印器
        alogger.parent = rv # 为日志打印器设置上级



    def _fixupChildren(self, ph, alogger):
        """
        Ensure that children of the placeholder ph are connected to the
        specified logger.
        """
        name = alogger.name  # 获取日志打印器名称
        namelen = len(name)  # 获取日志打印器名称长度 
        for c in ph.loggerMap.keys(): # 遍历获取的PlaceHolder日志打印器实例的子级日志打印器
            #The if means ... if not c.parent.name.startswith(nm)
            if c.parent.name[:namelen] != name: # 如果PlaceHolder日志打印器实例名称不以alogger日志打印器名称为前缀,
                alogger.parent = c.parent # 那么,设置alogger日志打印器的上级日志打印器为PlaceHolder日志打印器
                c.parent = alogger # 设置alogger日志打印器为PlaceHolder日志打印器原有下级PlaceHolder日志打印器的上级

  

 

结论:日志打印器都是分父子级的,这个父子层级是怎么形成的,参见上述函数代码注解

 

 

 

Sentry(v20.12.1) K8S 云原生架构探索,SENTRY FOR JAVASCRIPT 故障排除

给TA买糖
共{{data.count}}人
人已赞赏
经验教程

DHCP最佳实践(一)

2021-1-17 10:45:00

经验教程

使用Jenkins+Blue Ocean 持构建自动化部署之安卓源码打包、测试、邮件通知

2021-1-17 11:53:00

⚠️
免责声明:根据《计算机软件保护条例》第十七条规定“为了学习和研究软件内含的设计思想和原理,通过安装、显示、传输或者存储软件等方式使用软件的,可以不经软件著作权人许可,不向其支付报酬。”您需知晓本站所有内容资源均来源于网络,仅供用户交流学习与研究使用,版权归属原版权方所有,版权争议与本站无关,用户本人下载后不能用作商业或非法用途,需在24个小时之内从您的电脑中彻底删除上述内容,否则后果均由用户承担责任;如果您访问和下载此文件,表示您同意只将此文件用于参考、学习而非其他用途,否则一切后果请您自行承担,如果您喜欢该程序,请支持正版软件,购买注册,得到更好的正版服务。 本站为个人博客非盈利性站点,所有软件信息均来自网络,所有资源仅供学习参考研究目的,并不贩卖软件,不存在任何商业目的及用途,网站会员捐赠是您喜欢本站而产生的赞助支持行为,仅为维持服务器的开支与维护,全凭自愿无任何强求。本站部份代码及教程来源于互联网,仅供网友学习交流,若您喜欢本文可附上原文链接随意转载。
无意侵害您的权益,请发送邮件至 momeis6@qq.com 或点击右侧 私信:momeis 反馈,我们将尽快处理。
0 条回复 A文章作者 M管理员
    暂无讨论,说说你的看法吧
个人中心
今日签到
有新私信 私信列表
搜索