Python - 如何刷新日志?(Django)

49

我正在使用Django-nonrel在Google App Engine上工作,这迫使我使用logging.debug()而不是print()

"logging"模块由Django提供,但我在使用它而不是print()时遇到了一些困难。

例如,如果我需要验证变量x中保存的内容,我会写
logging.debug('x is: %s' % x)。但是,如果程序很快崩溃(没有刷新流),那么它就永远不会被打印出来。

因此,为了调试,我需要在程序发生错误并退出之前刷新debug(),但这并没有发生。


不是回答您的问题,但您为什么要使用“print”呢?! - aschmid00
3
“Flushing”日志是什么意思? - Daniel Roseman
希望所列举的例子有助于理解。 - Rucent88
我会尝试像AdminEmailHandler这样的日志处理程序来处理特定情况,比如你的例子。 数据库日志处理程序可能是更好的选择,不确定是否有免费的Sentry替代品。 - Gonzalo
可能不是最佳方法,但对我有效。希望能帮到别人。我尝试使用flush()方法,但没有起作用。当我使用sys.exit(..)退出应用程序时,我创建了一个包装器方法(exit())...在退出应用程序之前等待了1秒钟(time.sleep(1))。我还捕获任何意外错误并调用exit()方法。这个超时似乎可以清除任何未处理的日志消息。 - Raj
8个回答

34

我认为这可能对你有用,假设你只使用了一个(或默认)处理程序:

>>> import logging
>>> logger = logging.getLogger()
>>> logging.debug('wat wat')
>>> logger.handlers[0].flush()

虽然这在文档中被视为不太妥当。

应用程序代码不应直接实例化和使用Handler的实例。相反,Handler类是定义所有处理程序应具有的接口并建立一些子类可以使用(或覆盖)的默认行为的基类。 http://docs.python.org/2/howto/logging.html#handler-basic

而且这可能会影响性能,但如果你真的卡住了,这可能有助于调试。


5
如果唯一的方式是通过一个不应访问的处理程序来刷新日志,那么人们该怎么做呢?这听起来很糟糕。 - Joseph Garvin
4
不,这完全不是文档所说的。上面的引用意味着你应该只实例化和使用Handler的子类,而不是直接使用Handler。你的代码只使用其他人已经实例化的任何处理程序类,因此没有问题。 - Hjulle
12
确实非常有帮助;但我仍然觉得非常奇怪,我无法告诉配置文件中愚蠢的记录器始终进行刷新;而是我的客户端代码必须强制执行。 - GhostCat
它返回“超出范围”。我使用一个.ini文件初始化了记录器,其中包含两个处理程序。logging.hasHandlers()返回true。logger.handlers[0].flush() IndexError: 列表索引超出范围 - Mario Stefanutti

26

如果您的使用情况是,当退出时,您的Python程序应该刷新其日志,请使用logging.shutdown()

来自Python文档:

logging.shutdown()

通知日志系统通过刷新和关闭所有处理程序来执行有序关闭。这应该在应用程序退出时调用,并且在此调用后不应再使用日志记录系统。[...]


7
Django日志依赖于标准的Python日志模块。该模块具有一个模块级方法:logging.shutdown(),可刷新所有处理程序并关闭日志记录系统(即在调用后不能再使用日志记录)。检查此函数的代码显示,目前(Python 2.7)日志模块在名为_handlerList的模块级变量中保存了对所有处理程序的弱引用列表,因此可以通过执行类似以下操作来刷新所有处理程序:
[h_weak_ref().flush() for h_weak_ref in logging._handlerList]

因为这个解决方案使用了模块的内部机制,@Mikes上面的解决方案更好,但是它依赖于对日志记录器的访问,可以进行如下的泛化:

 [h.flush() for h in my_logger.handlerList]

1

0

可能是因为线程中出现了异常,导致记录器没有记录(似乎没有刷新)。线程需要捕获自己的异常并记录它们。

您不能依赖主线程来捕获和记录线程异常。

例如,定期运行函数的计时器将具有此行为,该函数将需要捕获和记录其异常。


0

刷新 stdout/stderr

sys.stdout.flush()
sys.stderr.flush()

0
我遇到了同样的问题,我希望我的log.info能够刷新到std:out。结果我只需要向stdout添加一个流处理程序,问题就解决了。
logger.addHandler(logging.StreamHandler(sys.stdout)).

另外,还要添加适当的日志级别,但这是显而易见的。

-3
一个简单的函数,在编程时始终用于注册调试消息。不要在生产中使用它,因为它不会轮换:
def make_log(message):
    import datetime
    with open('mylogfile.log','a') as f:
        f.write(f"{datetime.datetime.now()} {message}\n")

然后作为使用

make_log('my message to register')

当准备投入生产时,只需注释掉最后两行即可。

def make_log(message):
    import datetime
    #with open('mylogfile.log','a') as f:
    #    f.write(f"{datetime.datetime.now()} {message}\n")

实现一些琐碎的日志记录器并不能解决问题。有很好的理由,为什么会提供复杂的日志记录器,并且通过实现,您迟早会遇到大部分困难。一些原因包括:性能、日志级别等。 - Markus Dutschke

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