【logging】Pythonのログ出力の基本的な使い方

2022.08.26

2023.08.16

Fabeee社員ブログ

【logging】Pythonのログ出力の基本的な使い方

こんにちは!Fabeeeのエンジニア、ぼんです。
今回はPythonでログ出力をする方法についてです。
システムを開発する上で、ログを使いこなせることは開発効率に大きく影響します。
Pythonのログを使いこなして、楽しく開発していきましょう!

loggingを使用したログ出力の基礎

pythonでログを出力するときには、基本的にloggingモジュールを使います。
print()ではなくloggingを使用するメリットとしては、「ログレベルを分けることで問題・原因の発見を効率的にする」、「必要なログだけをファイルに出力する」などが挙げられます。
まず、基本的なログ出力の例を試してみます。

loggingでログを出力する

loggingをimportし、logging.[ログレベル]で簡単にログを出力することができます。
ログレベルとは、ログの重要度のことを指し、DEBUG<INFO<WARNING<ERROR<CRITICALの順に重要性が増していきます。

import logging

logging.debug("debug")
logging.info("info")
logging.warning("warning")
logging.error("error")
logging.critical("critical")

# -> WARNING:root:warning
# ERROR:root:error
# CRITICAL:root:critical

ここで、loggingで記載してあるDEBUGとINFOが出力されていないことが分かります。
これは、loggingはデフォルトではWARNING以上の重要度を持つログを出力するようになっているためです。
なので次は、ログのデフォルトの設定を変更してみます。

ログレベルを設定する

ログに対して設定を追加するには、logging.basicConfigを使用します。
以下の例では、出力するログレベルをINFOに設定しているので、INFO以上の重要度を持つ、INFO,WARNING,ERROR,CRITICALが出力されることになります。

import logging

# 追加
logging.basicConfig(level=logging.INFO)

logging.debug("debug")
logging.info("info")
logging.warning("warning")
logging.error("error")
logging.critical("critical")

# -> INFO:root:info
# WARNING:root:warning
# ERROR:root:error
# CRITICAL:root:critical

ログをフォーマットして見やすくする

さらにログを見やすくするために、ログの設定にフォーマットを追加します。
ログレベルと同様、logging.basicConfigに設定を追加します。
今回は以下の4つを設定してみます。

  • 時間 : %(asctime)s
  • ログレベル : %(levelname)s
  • ロギングに使われたロガーの名前 : %(name)s
  • ロギングに渡したログメッセージ : %(message)s

設定できるほかの値を参照したい場合は、ドキュメントをご覧ください。
https://docs.python.org/ja/3/library/logging.html#logrecord-attributes

import logging

# format="%(asctime)s - %(levelname)s:%(name)s - %(message)s"を 追加
logging.basicConfig(level=logging.INFO, format="%(asctime)s - %(levelname)s:%(name)s - %(message)s")

logging.debug("debug")
logging.info("info")
logging.warning("warning")
logging.error("error")
logging.critical("critical")

# -> 2022-08-23 23:20:10,199 - INFO:root - info
# 2022-08-23 23:20:10,199 - WARNING:root - warning
# 2022-08-23 23:20:10,199 - ERROR:root - error
# 2022-08-23 23:20:10,199 - CRITICAL:root - critical

ログをファイルに出力する

最後に、ログをログファイルに出力してみたいと思います。
これも今までと同様、logging.basicConfigで設定します。
ログの出力先をコンソールではなくファイルにするためには、basicConfigにfilename=”test.log”を追加します。

下記のコードを実行すると、コンソールには何も表示されず、test.logというファイルが生成されます。

import logging

# filename="test.log"を 追加
logging.basicConfig(level=logging.INFO,
format="%(asctime)s - %(levelname)s:%(name)s - %(message)s",
filename="test.log")

logging.debug("debug")
logging.info("info")
logging.warning("warning")
logging.error("error")
logging.critical("critical")


2022-08-23 23:35:55,096 - INFO:root - info
2022-08-23 23:35:55,096 - WARNING:root - warning
2022-08-23 23:35:55,096 - ERROR:root - error
2022-08-23 23:35:55,096 - CRITICAL:root - critical

実践的なログ出力方法

ここまでの内容でもログを出力することはできます。
ただ、実際の開発で用いる場合、ログの出力先の切り分けや実行されたモジュールの判別などができると、より実践的なログになります。
ロガークラスのインスタンス(以下logger)を利用することで、これらの実装が可能になります。。

loggerを用いたログ出力の具体例

以下のような実装だと、すべてルートロガーになってしまい、モジュールごとのログの切り分けなどができない状態でした。

import logging

logging.warning("warning")
logging.error("error")

そこで、モジュールごとにロガーを定義して、ログの切り分けを可能にしたものがloggerになります。

基本的には次のように定義して使用します。

import logging

# __name__を入れることで、「パッケージ/モジュール階層」が取得できる。(任意の文字列でもOK)
logger = logging.getLogger(__name__)

# loggingではなく、loggerであることに注意する。
logger.warning("warning")
logger.error("error")

次に、loggerをつかった簡単なログ出力の具体例を2つ挙げます。

ログが発生したモジュール名を出力する

まず、メインのファイルを作成します。
ファイル名はmain.pyとします。

import logging
from test import Test

# loggerを定義
logger = logging.getLogger(__name__)

# loggerのログレベルを設定
logger.setLevel(logging.WARNING)

# loggerのフォーマット、出力先ファイルを定義
formatter = logging.Formatter('%(asctime)s - %(levelname)s:%(name)s - %(message)s')
file_handler = logging.FileHandler('test.log')
file_handler.setFormatter(formatter)

# loggerのフォーマット、出力先ファイルを設定
logger.addHandler(file_handler)

logger.warning("warning")
logger.error("error")

# test.py内のクラスを呼び出す
test = Test()

次に、main.pyで呼び出すTestクラスを作成します。

ファイル名はtest.pyとします。

import logging

logger = logging.getLogger(__name__)
logger.setLevel(logging.WARNING)

formatter = logging.Formatter('%(asctime)s - %(levelname)s:%(name)s - %(message)s')
file_handler = logging.FileHandler('test.log')
file_handler.setFormatter(formatter)
logger.addHandler(file_handler)

# 呼び出されたらloggerを実行するだけのクラスを作成
class Test:
  def __init__(self):
    logger.warning("this is test class")

main.pyを実行してみます。すると、以下のようなtest.logファイルが生成されます。

3つ目のログに注目すると、WARNING:testという出力がされています。
これによって、3つ目のログはtestモジュールで出力されたものだということが分かりやすくなりました。

2022-08-24 00:21:06,392 - WARNING:__main__ - warning
2022-08-24 00:21:06,392 - ERROR:__main__ - error
2022-08-24 00:21:06,392 - WARNING:test - this is test class

エラーのログだけファイルに残す

次に、「INFO以上のログはコンソールに出力し、さらにERROR以上のログだけファイルに出力する」というログを作成してみます。
これは、ハンドラーを用いてファイル出力とコンソール用出力の2つを設定することで実現できます。

import logging

logger = logging.getLogger(__name__)
# loggerのログレベルをINFOに設定
logger.setLevel(logging.INFO)

formatter = logging.Formatter('%(asctime)s - %(levelname)s:%(name)s - %(message)s')
# ファイルハンドラでtest.logにログを出力するように設定
file_handler = logging.FileHandler('test.log')
# test.logに出力するログレベルを個別でERRORに設定
file_handler.setLevel(logging.ERROR)
file_handler.setFormatter(formatter)

# コンソールに出力するためのStreamHandlerを設定
stream_handler = logging.StreamHandler()
stream_handler.setFormatter(formatter)

# loggerにそれぞれのハンドラーを追加
logger.addHandler(file_handler)
logger.addHandler(stream_handler)

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

# コンソールにはINFO以上のログが出力される。
# -> 2022-08-24 00:29:14,708 - INFO:__main__ - info
# 2022-08-24 00:29:14,708 - WARNING:__main__ - warning
# 2022-08-24 00:29:14,708 - ERROR:__main__ - error
# 2022-08-24 00:29:14,708 - CRITICAL:__main__ - critical

生成されたtest.logを見てみると、ERROR以上のログしか出力されていないことが分かります。
このように、ファイルハンドラーを使用し、ファイルに出力するログレベルをERROR以上に設定することができました。

2022-08-24 00:35:09,421 - ERROR:__main__ - error
2022-08-24 00:35:09,421 - CRITICAL:__main__ - critical

最後に

Pythonのログ出力について解説してみましたが、いかがでしたでしょうか。
良いログを生成し、開発の手助けになれば幸いです。
今回書ききれなかったさらに詳しいログ出力の方法については、公式のドキュメントを参照していただくことをお勧めします。
https://docs.python.org/ja/3/howto/logging.html#advanced-logging-tutorial

Fabeee編集部

Fabeee編集部

こちらの記事はFabeee編集部が執筆しております。