【问题标题】:Logging Signals in Python在 Python 中记录信号
【发布时间】:2020-09-30 23:18:18
【问题描述】:

我有一个单线程 Python 应用程序。关闭此应用程序的标准方法是向其发送 SIGINT 并让各种 with:try: finally: 块处理安全和优雅的关闭。但是,这会导致日志文件不可读,因为您只是开始查看来自处理程序的消息,而没有明确指示发生了什么或关闭的原因。

我尝试通过添加一个简单的信号处理程序来解决这个问题,该处理程序会在引发KeyboardInterrupt 之前记录接收到的信号,如下所示:

def log_and_exit_handler(signum, stack):
    logger.info(f"Terminating due to signal {_signal.Signals(signum)}", stack_info=True)
    raise KeyboardInterrupt()
_signal.signal(_signal.SIGINT, log_and_exit_handler)

但是,在测试它时,我收到了一个日志错误:

--- Logging error ---
Traceback (most recent call last):
  File "/usr/local/lib/python3.7/logging/__init__.py", line 1029, in emit
    self.flush()
  File "/usr/local/lib/python3.7/logging/__init__.py", line 1009, in flush
    self.stream.flush()
RuntimeError: reentrant call inside <_io.BufferedWriter name='<stderr>'>
Call stack:
  [REDACTED]
  File "[REDACTED]", line 485, in _save_checkpoint
    _logger.info(f"Checkpoint saved")
  File "/usr/local/lib/python3.7/logging/__init__.py", line 1378, in info
    self._log(INFO, msg, args, **kwargs)
  File "/usr/local/lib/python3.7/logging/__init__.py", line 1514, in _log
    self.handle(record)
  File "/usr/local/lib/python3.7/logging/__init__.py", line 1524, in handle
    self.callHandlers(record)
  File "/usr/local/lib/python3.7/logging/__init__.py", line 1586, in callHandlers
    hdlr.handle(record)
  File "/usr/local/lib/python3.7/logging/__init__.py", line 894, in handle
    self.emit(record)
  File "/usr/local/lib/python3.7/logging/__init__.py", line 1029, in emit
    self.flush()
  File "/usr/local/lib/python3.7/logging/__init__.py", line 1009, in flush
    self.stream.flush()
  File "[REDACTED]", line 203, in log_and_exit_handler
    logger.info("Terminating due to signal {signal_}".format(signal_=signal_), stack_info=True)
Message: 'Terminating due to signal 2'
Arguments: ()

显然,收到信号时,应用程序已经在输出日志消息,logging 模块不可重入。

我是否可以使用任何变通方法或替代方法来安全地完成我在收到信号时记录信号的目标?

【问题讨论】:

  • 我遇到了同样的问题,最终使用 asyncio 模块处理每个信号
  • @AlejandroKaspar 你能详细说明一下吗?

标签: python python-3.x logging signals


【解决方案1】:

我无法重现,因此无法验证这些解决方案。 (您是否正在做一些会导致对 stderr 的写入过长的时髦操作?)

避免在 log_and_exit_handler 中使用 stderr

默认记录器已配置为写入标准错误。似乎程序在使用 stderr 时被中断,因此无法再次访问。确实,您需要避免程序中可能使用的任何 IO。从根记录器中删除 stderr 处理程序(以避免对 stderr 的访问尝试)并添加一个文件处理程序。

使用 sys.excepthook

类似的功能,但它的默认操作是写入标准错误,所以应该是安全的。似乎io limition of signal handlers 是众所周知的。希望通过异常钩子处理可以绕过它。

【讨论】:

  • AFAICT,问题不在于访问 STDERR 本身;问题出在logging 模块的内部。我很确定如果我让我的中断处理程序直接使用print() 写入 STDERR,它会正常工作,但这意味着放弃logging.info() 提供的便利和元数据。此外,将输出切换到文件也行不通;这是在 Docker 容器中运行的,因此日志需要转到 STDERR 或 STDOUT 以由 Docker 获取。
  • 听起来您有一些解决问题的好主意。我将我的钱用于在 log_and_exit_handler 中使用 print() 到 STDERR 仍然触发它,因为运行时错误说它在那个缓冲区中。我还认为将 STDOUT 用于您的主程序,而 STDERR 仅用于 log_and_exit_handler 就可以了。
猜你喜欢
  • 1970-01-01
  • 2016-07-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-05-02
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多