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

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

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

『DMM WEBCAMP COMMIT』