LoginSignup
2
2

More than 3 years have passed since last update.

loggerに独自のハンドラを設定する

Posted at

Pythonには標準でLoggerというログ出力を行うためのクラスがあります。

このLoggerに独自のハンドラを追加することで、たとえばログの出力をGUIに表示するなどといったことができるようになります。

まずはHandlerを継承したクラスを作る

まずはHandlerを継承したクラスを作りましょう。

handler.py
class GUILogHandler(logging.Handler):
    def __init__(self, window, level=logging.NOTSET):
      logging.Handler.__init__(self, level=level)
      self._window = window

    def emit(self, record):
      if len(record.msg) > 0:
        msg = self.format(record)
      else:
        msg = ""
      try:
        js = "let l = document.getElementById('log_area');"
        js += "let item = document.createElement('div');"
        js += "item.classList.add('log_item');"
        js += "item.classList.add('{0}');".format(record.levelname)
        js += "item.textContent = '{0}';".format(escape_string(msg))
        js += "l.appendChild(item);"
        js += "item.scrollIntoView({behavior: 'smooth', block: 'end', inline: 'nearest'});"
        self._window.evaluate_js(js)
      except Exception:
        self.handleError(record)

ここでは、emit()というメソッドだけが、オーバーライド必要なメソッドとなります。

ログの出力処理などが行われた際、このメソッドが呼び出されますので、self.format()メソッドを使用してフォーマッタを通したログを、出力するという処理を行います(このコードでは、PyWebViewを使って作成したUIの、log_areaというIDのdivに、ログを出力しようとしています)。

このフォーマッタを格納する領域は、Handlerの__init__()で作成されています。このため、もし独自の初期化処理を行う場合は、必ずlogging.Handler.__init__(self, level=level)として、スーパークラスの__init__()を呼ぶ必要があります。

emit()で引き渡されるrecordパラメータの各値の意味

emit()メソッドで引き渡されるrecordには、ログメッセージだけでなく、様々なログ情報が含まれます。

すべてのリストは、公式のマニュアルに載っていますので、そちらを参照してみてください。

ざっくりと、良く使うであろうパラメータは以下の通り。

  • asctime: 事象が発生したときの時刻。ミリ秒単位
  • filename: ログ出力を行ったファイルの名前
  • funcName: ログ出力を行った関数の名前
  • levelname: ログレベル(DEBUG, INFOなどの値)
  • message: ログのメッセージ
  • processName: プロセス名
  • threadName: スレッド名

ハンドラの追加と、余計なハンドラの削除について

このようにして作成したハンドラは、logger.addHandler()メソッドで追加可能です。

main.py
import logging
# ...
logger = logging.getLogger(self.__class__.__name__)
logger.addHandler(GUILogHandler(window))

ただし、このようにしてLoggerに追加したハンドラは、logger内に登録されるため、上記のコードを二回以上実行するときなど、重複してハンドラが登録されてしまう場合があります。

そのような場合は、ロガーに登録されているハンドラを一度すべて消去する必要があります。

main.py
# ハンドラーリストを動的に書き換えてしまうことになるので、forで回すと上手くループできません。
while len(logger.handlers) > 0:
  logger.removeHandler(logger.handlers[0])
2
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2