两个日志记录配置之间的主要区别是什么

时间:2017-05-06 13:03:48

标签: python logging

我正在进行日志记录配置,使其成为各种python项目的标准日志库。

但是发生了一些奇怪的事情:在之前的情况下使用" configure2",主文件" test1"总是打印两次。因此,如果我使用相同的记录器库导入了另一个库,它将打印相同的次数。

如果" configure1",一切似乎进展顺利。那么你们能告诉我为什么会发生这种情况以及记录功能发生了什么?

logger.py:

import logging
import logging.handlers
import logstash

def configure1():
    host = '10.211.55.12'
    logging.basicConfig(level=logging.DEBUG,)
    # fileConfig(log_file_path)

    logger = logging.getLogger(__name__)
    logger.addHandler(logstash.TCPLogstashHandler(host, 514, version=1))
    return logger


def configure2():
    host = '10.211.55.12'

    logger = logging.getLogger(__name__)
    logger.setLevel(logging.DEBUG)
    fh = logging.StreamHandler()
    logger.addHandler(fh)
    logger.addHandler(logstash.TCPLogstashHandler(host, 514, version=1))
    return logger

test1.py:

import logger
import test2
logger = logger.configure1()
#logger = logger.configure2()
logger.info('hello from test1')

test2.py:

import logger
logger = logger.configure1()
#logger = logger.configure2()
logger.info('hello from test2')

configure1结果:

INFO:logger:hello from test2
INFO:logger:hello from test1

configure2结果:

hello from test2
hello from test1
hello from test1

1 个答案:

答案 0 :(得分:0)

日志记录层次结构

Python记录器按层次结构排列,顶部的根记录器以空字符串表示,名称为后代。这类似于文件层次结构,其中'a.x''a.y''a'的后代,而'a''b'是根记录器的后代。

每个记录器处理传入的日志消息,然后将其传递给其父级。每个没有日志级别集的记录器都将检查其父级,直到找到具有该日志级别集的记录器。 (这是logger.levellogger.getEffectiveLevel()之间的区别)

要记住的重要一件事是basicConfig配置了 root 记录器,而logger.addHandlerlogger.setLevel在您请求的记录器上运行(通常是模块的记录器) )。另外,basicConfig只能被调用一次,随后对其的调用将被忽略。

在您的情况下,有2个记录器:

          '' (root)
         /  
        /    
'logger' (__name__ in logger.py, this is NOT 'test1' or 'test2'!)

logger.py

让我们拿出logstash,因为我们看不到结果,而我们在打印语句中加入以帮助了解发生了什么:

import logging

def configure1():
    logging.basicConfig(level=logging.DEBUG,)
    logger = logging.getLogger(__name__)
    print('root   =', logging.getLogger('').handlers)
    print('logger =', logging.getLogger('logger').handlers)
    return logger

def configure2():
    logger = logging.getLogger(__name__)
    logger.setLevel(logging.DEBUG)
    fh = logging.StreamHandler()
    logger.addHandler(fh)
    print('root   =', logging.getLogger('').handlers)
    print('logger =', logging.getLogger('logger').handlers)
    return logger

configure1()

您对basicConfig()的调用将初始化根记录器,该记录器将附加一个StreamHandler <stderr>,从而使您能够查看两条记录消息。

root   = [<StreamHandler <stderr> (NOTSET)>]
logger = []
root   = [<StreamHandler <stderr> (NOTSET)>]
logger = []
INFO:logger:hello from test2
INFO:logger:hello from test1

configure2()

basicConfig()不会被 调用,因此根记录器在 not 中没有任何处理程序。但是,每次对configure2()的调用向记录器添加

root   = []
logger = [<StreamHandler <stderr> (NOTSET)>]
root   = []
logger = [<StreamHandler <stderr> (NOTSET)>, <StreamHandler <stderr> (NOTSET)>]
hello from test2
hello from test1
hello from test1

那该怎么办?

您真正想要的是一个记录器,其名称与发出日志消息的模块相对应。因此,我建议将logger = logging.getLogger(__name__)放在每个生产模块的顶层,并使用它进行记录。

通过具有一个单独的模块来配置日志记录,该模块可以配置根记录器(对于应用程序)或程序包记录器(logging.getLogger('yourpackagename'),对于库和程序包),并仅在程序开始时运行一次。 / p>