python / python/cpython

Handle TCP socket failure in `logging.handlers.SysLogHandler.emit`

未关闭
#122,959 8 条评论 0 个 reaction 已指派 0 人 在 GitHub 查看

还没有人认领这个 Issue。

3.12 3.13 3.14 stdlib type-bug
主要语言
Python
星标
77.2k
派生
35.9k
PR 合并指标
PR 指标待抓取

描述

Bug report

Bug description:

Client loggers on TCP using a socket stream can timeout. A TCP logger server could issue disconnect to TCP client due to timeout caused usually by no activity after some elapsed time. TCP servers typically have to control how many TCP listeners are active in memory to prevent TCP flooding. They do it by monitoring TCP activity timeouts and by removing open TCP sockets from memory and by sending a FIN to client to disconnect socket in remote TCP table as well.

Going back to the Python apps that use "import logging" to send events to specifically to TCP handlers, an app relying on TCP logger would not recover successfully if socket was closed by server's TCP FIN packet; thus app's syslog message/event would no longer be forwarded to TCP log collector. The bug exists because there is no verification of TCP socket stream existence in logging.handlers.SysLogHandler.emit.

If socket was torned down by Kernel it doesn't exist and socket Exception is generated and a new socket stream of type socket.SOCK_STREAM should be created and message resent to remote TCP collector on 2nd attempt. The reason why the bug exists is because the current logger handler does not support explicit try:except auto-remediation handling for socket types socket.SOCK_STREAM (TCP sockets) in logging.handlers.SysLogHandler.emit. The proposed update below addresses that gap by providing recovery support for TCP sockets that were closed by Kernel ensuring data logging continuity for Python apps without restarting the application..

Notice that there isn't a recovery block in the original code for socket type socket.SOCK_STREAM in logging.handlers.

I added the code in bold to show how the socket would be created for a Python app using TCP-based client logging as a handler. I have confirmed this bug exist with our TCP syslog infrastructure and confirmed corrected by the following code in bold. You could duplicate yourself by adding a TCP handler to any IP using import logging and then wait have the TCP server disconnect the socket sending FIN to client and the TCP client will fail to create a new socket on new event through logger. An error should appear when trying to self.socket.send(msg) when a TCP socket doesn't exist and there is no try/except to recover from that in original code.

Please approve this bug and I'll issue a pull request with the bolded code below to fix it.

From logging.handlers.SysLogHandler.emit:

if not self.socket:
    self.createSocket()

if self.unixsocket:
    try:
        self.socket.send(msg)
    except OSError:
        self.socket.close()
        self._connect_unixsocket(self.address)
        self.socket.send(msg)
+# UDP socket type
elif self.socktype == socket.SOCK_DGRAM:
    self.socket.sendto(msg, self.address)
+# TCP socket type
+# This block doesn't exist in original handlers.py code for TCP
+elif self.socktype == socket.SOCK_STREAM:
+    try:
+        self.socket.sendall(msg)
+    # create new TCP socket stream, if TCP socket broken or disconnected
+    except OSError:
+        self.socket = socket.socket(socket.AF_INET, self.socktype)
+        self.socket.connect(self.address)
+        # retry sending msg after making new TCP AF_INET socket
+        self.socket.sendall(msg)
else:
    self.socket.sendall(msg)

CPython versions tested on:

CPython main branch

Operating systems tested on:

Linux

Linked PRs
  • gh-123238

贡献指南

打开贡献指南

从这里开始

  1. 先读完整个 Issue,再读项目的贡献指南。
  2. 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
  3. Fork 仓库,在一个分支上完成修改。
  4. 提交 Pull Request,并在描述里引用这个 Issue 编号。

调研方向

先从 handlers.py 中的 logging.handlers.SysLogHandler.emit 开始,并在进行更改前检查关联的 PR gh-123238。验证 issue 中描述的 TCP socket 失败路径,包括在发生 OSError 后重新连接并重试发送消息;当该行为已得到覆盖且现有的 logging handler 行为保持不变时,即表示完成。

由索引模型根据 Issue 内容生成。

评估

技术栈
python
领域
backend
Issue 类型
缺陷
难度
3/5
预计耗时
1-2 天
活跃度
停滞
描述清晰度
描述清楚
新手友好度
35/100

把新 issue 发到你的邮箱

精选适合新手参与的 GitHub issue 摘要。