Python

【Python】loggingでログを記録する方法を解説

この記事では、Pythonでログを記録する方法を解説します。

ログとは、ソフトウェア(アプリとか)を実行している際に起こったイベント(出来事)を記録したデータのことを言います。

ログを記録しておくことで不測の事態が発生した場合に原因を解明するための材料として使うことができてとても便利です。

それでは、ログを記録する方法を見てきましょう!

ログ(log)とは?

ログとは、コンピュータが付ける記録のことを言います。何かしらのアクションがあるたびに記録を付けることで、プログラム中で何が起こっているかを把握することができます。

いつどこから接続したのか確認するための「アクセスログ」や、コンピュータ上でエラーが発生したときに記録する「エラーログ」など様々なログがあります。

ログは、人間が確認するものなので「いつ・誰が・何をしたか」などが記述されており、そのとき起こった出来事の詳細がわかるように記録するのが一般的です。

何かしら問題が起きた場合でもログを確認することでエラーが発生した箇所や原因を特定しやすくなります。

ログの基本

Pythonでログを記録するには、loggingモジュールを使います。loggingモジュールは、標準ライブラリなのでインポートするだけで使用可能です。

まずは、簡単にログを出力してみましょう!

# loggingモジュールのインポート
import logging

# loggingクラスのインスタンス化
logger = logging.getLogger(__name__)
logger.warning('warning: ログです')

実行結果

warning: ログです

ログを扱うにはlogger(ロガー)を使います。

loggerは、loggerクラスから直接インスタンス化するのは禁止されており、必ずgetLogger()メソッドでインスタンス化する必要があります。

getLogger()メソッドの第一引数に文字列を指定することでloggerごとの名前を設定することができます。 __name__を指定することでモジュール名をロガー名に指定することができ、モジュールごとにログを記録する際に便利です。

また、同じ名前でロガーを生成した場合、常に同一のロガーオブジェクトを参照します。

そして最後に warningメソッド でログを出力しています。

  • logger(ロガー) を getLogger()メソッド で生成する
  • getLogger()メソッド の引数でロガーの名前を指定
  • 同じ名前のロガーは同一のオブジェクトを参照

ログの出力先を設定する

ロガーにハンドラを設定することでログの出力先を変更することができます。

ハンドラが一つも設定されていなかった場合、sys.stderrに ログレベルWARNING以上のログを出力するように設定されています。ログレベルについては後述します。

例えばsys.stdoutに出力するようにするには、StreamHandler()クラスを使い、ハンドラを生成してaddHandler()メソッドでロガーにハンドラを追加します。

import logging
import sys  # sysモジュールのインポート

# ロガーのインスタンス化
logger = logging.getLogger(__name__)

# sys.stdoutに出力するハンドラを生成
sh = logging.StreamHandler(sys.stdout)
# ロガーにハンドラを追加
logger.addHandler(sh)

logger.warning('warning.')

実行結果

warning.

出力される場所が同じなのでイマイチ変わったかわかりませんね...。わかりやすいように今度はファイルにログを出力してみます。

ファイル操作がわからない方は以下の記事を参考にして下さい。

【Python】open関数を使ってファイルを読み書きする方法を解説この記事では、Pythonのopen関数を使ってソースコード内からファイルを読み書きする方法を解説します。Pythonでファイルを読み書きするには、open関数を使って生成したファイルオブジェクトを通してファイルにデータを書き込んだり、ファイルのデータを読み込んだりします。それでは、ファイルを読み書きする方法を見ていきましょう!...

では、「example.log」というファイルを作成してログを書き込んでみます。その際、open()関数のモードは'a'にし、ファイルが存在している場合は追記するようにしておきます。

import logging

# ロガーのインスタンス化
logger = logging.getLogger(__name__)

with open('example.log', 'a') as f:
    # ファイルに出力するハンドラを生成
    sh = logging.StreamHandler(f)
    # ロガーにハンドラを追加
    logger.addHandler(sh)

    logger.warning('warning.')

このコードを実行すると以下のようなファイルが生成されました。

ファイルにログを出力するハンドラを生成したい場合は、FileHandler()クラスを使うとより簡単です。第一引数にファイル名、またはファイルのパスを指定するだけでOKです。

import logging

# ロガーのインスタンス化
logger = logging.getLogger(__name__)

# ファイルに出力するハンドラを生成
sh = logging.FileHandler('example.log')
# ロガーにハンドラを追加
logger.addHandler(sh)

logger.warning('warning.')

この他にも便利なハンドラが用意されているので気になる方はチェックしてください。

LinkLogging HOWTO --- 便利なハンドラ - Python ドキュメント

ちなみに、ハンドラは1つのロガーに複数設定可能です。

ログレベル

今まではwarning()メソッドを使ってログを出力していましたが、異なるログレベルを出力するメソッドを使うことでログの重要度を変えることができます。

ログレベルには、CRITICALERRORWARNINGINFODEBUGNOTSETがあり、 CRITICALが一番高く、NOTSETが一番低くなっています。

レベル 使うタイミング
DEBUG 問題を診断するときに使う
INFO 想定通りにいっているか確認
WARNING 想定外のことが起こりそうなときに使う
ERROR 重大な問題により、ある機能を実行できない
CRITICAL プログラム自体が実行を続けられない

それぞれのレベルのログを出力してみます。

# loggingモジュールのインポート
import logging

# ロガーのインスタンス化
logger = logging.getLogger(__name__)

logger.debug('debug.')
logger.info('info.')
logger.warning('warning.')
logger.error('error.')
logger.critical('critical.')

実行結果

warning.
error.
critical.

ロガーのデフォルトのログレベルはWARNINGに設定されているため、重要度がWARNING以上のログメッセージだけが出力されます。

ログレベルを設定するにはsetLevel()メソッドを使用します。試しにロガーのログレベルをinfoに設定して同様にログを出力してみます。

import logging
import sys

# ロガーのインスタンス化
logger = logging.getLogger(__name__)
# ハンドラの生成
sh = logging.StreamHandler(sys.stdout)
# ロガーにハンドラを追加
logger.addHandler(sh)
# ロガーのログレベルをinfoに設定
logger.setLevel(logging.INFO)

logger.debug('debug.')
logger.info('info.')
logger.warning('warning.')
logger.error('error.')
logger.critical('critical.')

実行結果

info.
warning.
error.
critical.

また、ハンドラにもログレベルを設定することができます。どちらにもログレベルが設定されている場合は、重要度が高い方が優先されます。

import logging
import sys

# ロガーのインスタンス化
logger = logging.getLogger(__name__)
# ハンドラの生成
sh = logging.StreamHandler(sys.stdout)
# ハンドラのログレベルを設定
sh.setLevel(logging.ERROR)
# ロガーにハンドラを追加
logger.addHandler(sh)
# ロガーのログレベルをinfoに設定
logger.setLevel(logging.INFO)

logger.debug('debug.')
logger.info('info.')
logger.warning('warning.')
logger.error('error.')
logger.critical('critical.')

実行結果

error.
critical.

ログによって重要度を変更することで確認するべきログを明確化できます。

フォーマッタを設定する

今までのコードでは、あまり意味のないログを出力していました(warning. など)。ログとして記録しておくべきなのは、いつ、どこで、何が起きたかなどの詳しい情報です。

詳しい情報を出力するには、フォーマッタを生成してハンドラに追加します。すると、ログの情報をフォーマッタ通りに整形して出力させることができます。

import logging
import sys

# ロガーのインスタンス化
logger = logging.getLogger(__name__)
# ハンドラの生成
sh = logging.StreamHandler(sys.stdout)
# フォーマッタを生成
fmt = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
# ハンドラにフォーマッタをセット
sh.setFormatter(fmt)
# ロガーにハンドラを追加
logger.addHandler(sh)

logger.warning('warning.')

実行結果

2021-08-18 23:50:13,977 - __main__ - WARNING - warning.

出力できる属性とフォーマット方法については以下のリンクを参考にしてください。

LinkLogRecord 属性

まとめ

この記事では、Pythonの標準モジュールloggingを使ってログを記録する方法を解説しました。

何かしらのプログラムを作成した場合は、必ずログをとるようにしましょう!
どういうタイミングでログを記録すればいいの?って方は以下の記事が参考になります。

Linkログ設計指針 - Qiita

なんかゴチャゴチャしていて一見使いずらそうな感じのするログですが、絶対に必要な機能なのでちょこちょこ使って仕組みを忘れる前に慣れてしまいましょう!

それでは今回の内容はここまでです。ではまたどこかで〜( ・∀・)ノ