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.

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

import logging

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

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

    logger.warning('warning.')

コードを実行すると以下のようなファイルが生成され、ログがexample.logに出力されたのがわかります。

ファイルにログを出力するハンドラを生成したい場合は、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の順で高くなります。

レベル 使うタイミング
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 属性

まとめ

この記事は、簡単にログの使い方をまとめました。

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

Linkログ設計指針 – Qiita

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

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

最短3か月でエンジニア転職『DMM WEBCAMP COMMIT』