我正在进行日志记录配置,使其成为各种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
答案 0 :(得分:0)
日志记录层次结构
Python记录器按层次结构排列,顶部的根记录器以空字符串表示,名称为后代。这类似于文件层次结构,其中'a.x'
和'a.y'
是'a'
的后代,而'a'
和'b'
是根记录器的后代。>
每个记录器处理传入的日志消息,然后将其传递给其父级。每个没有日志级别集的记录器都将检查其父级,直到找到具有该日志级别集的记录器。 (这是logger.level
和logger.getEffectiveLevel()
之间的区别)
要记住的重要一件事是basicConfig
配置了 root 记录器,而logger.addHandler
和logger.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>