Python:在其他输出之前将注释打印到控制台

8

我一直在尝试理解Python中的日志记录。我有一个__init__模块、两个其他模块和一个主模块。不知何故,当我运行我的模块时,日志详细信息会跳过代码流并首先打印出来。

请问有人能告诉我这是为什么吗?

这段代码位于__init__.py文件中。

from dir1.mod1 import FirstClass
from dir1.mod2 import SecondClass

logger = logging.getLogger(__name__)

logger.setLevel(logging.DEBUG)
f_handler=logging.FileHandler('python_logs.log')
f_handler.setLevel(logging.DEBUG)
c_handler = logging.StreamHandler()
c_handler.setLevel(logging.ERROR)

f_formatter = logging.Formatter('%(name)s - %(levelname)s - %(message)s')
c_formatter = logging.Formatter('%(name)s - %(levelname)s - %(message)s')

f_handler.setFormatter(f_formatter)
c_handler.setFormatter(c_formatter)

logger.addHandler(f_handler)
logger.addHandler(c_handler)

这是在另外两个模块中的(resp类的__init__()内部编写的。
self.logger = logging.getLogger(__name__)

在一个模块中定义的addn()函数的片段

def addn(self):
    z=self.x +self.y
    print('sum is '+z)
    self.logger.error('incrementing number!')
    self.logger.info('Still incrementing number!!')
    return z

我的主要模块(我运行的)有这个:

from dir1.mod1 import FirstClass
from dir1.mod2 import SecondClass

number = FirstClass(2,2)
print('addition results')
number.addn()

我期望得到以下的输出结果:

加法结果

和为3

dir1.mod1 - 错误 - 数字增量!

但我实际得到的是:

dir1.mod1 - 错误 - 数字增量!

dir1.mod1 - 错误 - 数字增量!

加法结果:

和为3

为什么日志信息会跳出代码流?还有,为什么日志信息会打印两次?


在你的 addn 函数中,你混合使用了 print 和 logging。如果你用 self.logger.info 替换 print,会发生什么?我怀疑打印和记录器的刷新顺序并不是你想象的那样。 - ma3oun
在这种情况下,“加法结果”应该优先打印,因为它在调用addn函数之前被执行了,对吗?……但是我仍然在log消息之后获得加法结果。 - Sanju
3个回答

10

Python StreamHandler默认会将日志记录到stderr,而您的打印语句则会输出到stdout。这是两个不同的管道,它们之间的顺序不能保证。

为确保正确的排序,请首先将所有输出发送到同一个管道。例如,您可以在打印语句中添加file=sys.stderr参数。


谢谢,这个方法起作用了!但我仍然不确定为什么日志会打印两次。 - Sanju
这确实解决了跳转行为。但是print可能会被调用得比全局初始化的logger频繁。也许配置logger更容易,而不是每次调用print时都要添加file=sys.stderr? - data-monkey
是的,我并不以任何方式对这段代码进行制裁。实际上,你并不需要一个全局记录器,你只需要在你的模块中导入并实例化记录器;如果我没记错的话,日志类将会在整个程序中应用日志配置。 - undefined

4
您可以尝试禁用记录器的传播属性。
传播:如果该属性为true,则将记录到此记录器的事件传递给更高级别(祖先)记录器的处理程序,以及附加到此记录器的任何处理程序。消息直接传递给祖先记录器的处理程序-不考虑所涉及的祖先记录器的级别或过滤器。
这是一个使用文件和标准输出具有不同调试级别的初始记录器示例:
def init_logger_singleton():

    global logger

    logger = logging.getLogger(name='loggerName')
    logger.propagate = False
    logger.setLevel(10)
    formatter = logging.Formatter(
        '\t%(message)s'
    )

    filehandler = logging.StreamHandler()
    filehandler.setLevel(40)
    filehandler.setFormatter(formatter)
    logger.addHandler(filehandler)

我使用数字来定义日志级别,其中10 = DEBUG,40 = ERROR。更多信息请查看此链接


2

您也可以将参数sys.stdout添加到流处理程序中,这样print和logger事件都将传递到一个通道。

stream_handler = logging.StreamHandler(sys.stdout)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接