🐍

競プロJupyter Notebook勢のためのlogging設定考察

2024/08/04に公開

TL;DR

from logging import basicConfig, root, DEBUG, WARNING

def some_algorithm():
    basicConfig(level=DEBUG if 'get_ipython' in globals() else WARNING)
    root.debug('hello')

some_algorithm()

動機

テストの書きやすさや、単純に好みから、Jupyter Notebookで競技プログラミングに出ています。

しかし、ロギングは競プロではただでさえ気を使うのに、Jupyter Notebook固有の問題が加わってきます。

  • AtCoderでは標準出力を判定するので、print文を消し忘れると誤答になる
  • 代わりに標準エラー出力を使おうとするも、Jupyter Notebookの標準エラー出力は真っ赤なのでデバッグには向かない
  • loggingライブラリを使う場合、何度も実行するセル内でStreamHandlerを定義していると、実行の度にハンドラが増える

上記の問題の解決に加えて、コピペする行数を最小限(2行)に抑えたのが冒頭のコードです。

解説

コードは冒頭と同じです。コメントで解説します。

from logging import basicConfig, root, DEBUG, WARNING

def some_algorithm():

    # basicConfig は、ルートロガーに StreamHandler がない場合のみ設定する。
    # ルートロガーの利用を認めることになるが、子ロガーで冪等にハンドラを設定する方法がデフォルトで用意されていない。
    basicConfig(
        # 一般的には get_ipython() を呼び出すことが多いようだが、Jupyter Notebook以外の環境でエラーハンドリングが必要になる。
        # Jupyter Notebook 固有の変数として __IPYTHON__ があるが、これは globals() には定義されていないようだ。
        # その他、環境変数やモジュールを見る案もあったが、os, sysのimportが必要なので見送った。
        level=DEBUG if 'get_ipython' in globals() else WARNING
    )

    # logging.debug() でも構わない。
    # しかし、root.debug() の方が明示的にルートロガーを使っていることが伝わる。
    root.debug('hello')

some_algorithm()

まとめ

  • basicConfigを用いることで、セルの再実行に対しても冪等にStreamHandlerを設定できました。
  • get_ipythonがグローバル変数にあるかを見ることで、ライブラリのimportやエラーハンドリング無しに実行環境を判定することができました。[1]
  • rootを使うことで、ルートロガーの利用を明示しました。
脚注
  1. get_ipythonはJupyter Notebookのセル上で実行しない場合(import先に書いてあるなど)Falseになる。コード自体をコピペする今回のようなケースでのみ有用ということに注意。 ↩︎

GitHubで編集を提案

Discussion