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

理解上述流程之後,就比較能夠理解上述範例的步驟:

  1. que = queue.Queue(-1) 新增 1 個沒有限制 size 的 Queue
  2. queue_handler = QueueHandler(que) 取得 1 個 QueueHandler
  3. 透過 logger = logging.getLogger() 取得 logger
  4. logger.addHandler(queue_handler) 則將第 2 步取得的 QueueHandler 加入 logger 的 handlers 之中,以從中取得 log 資訊
  5. 然而 QueueHandler 只是扮演傳遞的角色,所以我們需要一個真正處理 log 的 handler ,因此透過 handler = logging.StreamHandler() 取得 1 個 StreamHandler 處理 QueueHandler 傳遞過來的日誌
  6. 接著 listener = QueueListener(que, handler) 負責將 QueueHandler 透過 que 傳遞過來的日誌交給 步驟 5 的 StreamHandler 處理
  7. 最後 listener.start() 讓 QueueListener 執行 thread 開始工作
  8. 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

對抗久坐職業傷害

研究指出每天增加 2 小時坐著的時間,會增加大腸癌、心臟疾病、肺癌的風險,也造成肩頸、腰背疼痛等常見問題。

然而對抗這些問題,卻只需要工作時定期休息跟伸展身體即可!

你想輕鬆改變現狀嗎?試試看我們的 PomodoRoll 番茄鐘吧! PomodoRoll 番茄鐘會根據你所設定的專注時間,定期建議你 1 項辦公族適用的伸展運動,幫助你打敗久坐所帶來的傷害!

贊助我們的創作

看完這篇文章了嗎? 休息一下,喝杯咖啡吧!

如果你覺得 MyApollo 有讓你獲得實用的資訊,希望能看到更多的技術分享,邀請你贊助我們一杯咖啡,讓我們有更多的動力與精力繼續提供高品質的文章,感謝你的支持!