2022.08.26
2023.08.16
Fabeee社員ブログ
こんにちは!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