Handle TCP socket failure in `logging.handlers.SysLogHandler.emit`
まだ誰も着手していません。
- 主要言語
- 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
コントリビューションガイド
はじめの一歩
- issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
- 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
- リポジトリをフォークし、ブランチを切って変更します。
- issue 番号を参照したプルリクエストを送ります。
調査の方向性
handlers.py の logging.handlers.SysLogHandler.emit から始め、変更を加える前にリンクされている PR gh-123238 を確認してください。issue に記載されている TCP ソケットの失敗経路を、OSError の後に再接続してメッセージを再試行する処理を含めて検証してください。動作がカバーされ、既存の logging handler の動作が維持されていれば完了です。
索引モデルが issue の本文から書いたものです。
評価
- 技術スタック
- python
- 領域
- backend
- issue の種類
- バグ
- 難易度
- 3/5
- 見積もり時間
- 1〜2日
- 活発さ
- 停滞
- 明瞭さ
- 明確に書かれている
- 初心者へのやさしさ
- 35/100