Python - QueueHandler & QueueListener 範例
Posted on Nov 30, 2018 in Python 程式設計 - 高階 by Amo Chen ‐ 2 min read
Python logging 模組提供很多內建的 handlers ,可以依照各種不一樣的使用情況選擇不同的 handler 處理日誌(log)。
然而,關於 QueueHandlers 的說明最為吸引我的目光:
Along with the QueueListener class, QueueHandler can be used to let handlers do their work on a separate thread from the one which does the logging.
This is important in Web applications and also other service applications where threads servicing clients need to respond as quickly as possible, while any potentially slow operations (such as sending an email via SMTPHandler) are done on a separate thread.
QueueHandler 與 QueueListener 讓我們能夠把 logging 的功能用另外的執行緒(thread)執行,如此一來像是 SMTPHandler 這種耗時間會拖慢 Web 應用或是其他服務的 handler 就可以在另外的執行緒執行,讓重要的服務能夠盡快地回應用戶,避免被 logging 拖慢回應速度。
根據官方文件 Dealing with handlers that block 改用 QueueHandlers & QueueListener 很簡單(以下有稍微改過):
import queue
from logging.handlers import QueueHandler
from logging.handlers import QueueListener
formatter = logging.Formatter('%(threadName)s: %(message)s')
que = queue.Queue(-1) # no limit on size
queue_handler = QueueHandler(que)
logger = logging.getLogger()
logger.addHandler(queue_handler)
logger.setLevel(logging.DEBUG)
handler = logging.StreamHandler()
handler.setLevel(logging.DEBUG)
handler.setFormatter(formatter)
listener = QueueListener(que, handler)
listener.start()
logger.debug('Hello World')
listener.stop()
簡而言之,上述範例的資料流大致上長這個樣子:
Logger --> QueueHandler --> QueueListener --> StreamHandler
Logger 將資料傳遞至 QueueHandler
之後, QueueListener
會接收到 QueueHandler
傳遞過來的日誌,這些日誌最後再交給真正的 Handler 處理,也就是 StreamHandler
。
理解上述流程之後,就比較能夠理解上述範例的步驟:
que = queue.Queue(-1)
新增 1 個沒有限制 size 的 Queuequeue_handler = QueueHandler(que)
取得 1 個 QueueHandler- 透過
logger = logging.getLogger()
取得 logger logger.addHandler(queue_handler)
則將第 2 步取得的 QueueHandler 加入 logger 的 handlers 之中,以從中取得 log 資訊- 然而 QueueHandler 只是扮演傳遞的角色,所以我們需要一個真正處理 log 的 handler ,因此透過
handler = logging.StreamHandler()
取得 1 個 StreamHandler 處理 QueueHandler 傳遞過來的日誌 - 接著
listener = QueueListener(que, handler)
負責將 QueueHandler 透過que
傳遞過來的日誌交給 步驟 5 的 StreamHandler 處理 - 最後
listener.start()
讓 QueueListener 執行 thread 開始工作 logger.debug('Hello World')
試著使用 logger 記錄日誌
執行結果如下:
MainThread: Hello World
如此一來就完成將日誌資料丟到 thread 執行的工作囉!
結語
本文用 StreamHandler 作為示範,如果只是利用一般的 stderr, stdout 做為 logger 輸出目標的話,倒不必如此大費周章,然而像是 SMTPHandler 這種比較耗時的 Log Handler 則可以考慮用 QueueHandler 搭配 QueueListener 將耗時的 Log Handler 在另外的 thread 中執行,避免拖累應用(application)的回應速度。
References
https://docs.python.org/3/library/logging.handlers.html