【Python logging入門】printデバッグからの卒業!プロが教えるログ設計と実践テクニック
プログラムの開発中、変数の値を確認したり、処理がどこまで進んだかを確認したりするためにprint()関数を多用していませんか?小規模なスクリプトであればそれでも問題ありませんが、プロジェクトが複雑になるにつれて、あちこちに仕込んだprint()文の管理は煩雑になりがちです。 今回は、そんな「printデバッグ」から卒業し、より高度で体系的なログ管理を実現するためのPython標準ライブラリloggingモジュールについて、初心者にも分かりやすく解説します。
さようならprintデバッグ!loggingモジュールを使うべき理由
多くのPython初学者がデバッグで最初にお世話になるのがprint()関数です。 手軽で直感的な反面、本格的なアプリケーション開発ではいくつかの課題が浮上します。
- 情報過多とノイズ:デバッグ用の表示と、本来ユーザーに見せるべき出力がコンソールに混在し、非常に見づらくなります。
- 手作業でのON/OFF:デバッグが終わるたびに、大量の
print()文を一つひとつコメントアウトしたり削除したりする必要があり、手間がかかる上に消し忘れのリスクもあります。 - 情報量の不足:単純なメッセージしか表示できないため、「いつ」「どのファイルのどの部分で」そのメッセージが出力されたのかといった付加情報がありません。
こうしたprint()の弱点を解決するのがloggingモジュールです。 loggingは、単にメッセージを表示するだけでなく、「誰が、いつ、どこで、どのくらい重要か」という文脈情報を含んだ「ログ」を体系的に記録するための強力な仕組みを提供します。
- ログレベルの管理:「デバッグ」「情報」「警告」「エラー」など、メッセージの重要度に応じてレベル分けし、表示するレベルを簡単に切り替えられます。
- 柔軟なフォーマット:日時、ファイル名、行番号、モジュール名などをログメッセージに自動で追加できます。
- 出力先の自由な制御:コンソールへの表示だけでなく、ファイルへの保存や、ネットワーク経由でのログサーバーへの送信などを簡単に行えます。
一言で言えば、printが場当たりのメモ書きだとすれば、loggingは整理された公式の議事録のようなものです。この機会に、プロフェッショナルなログ管理術を身につけましょう。
ログの重要度を使い分ける「ログレベル」

loggingモジュールの最も基本的な機能が「ログレベル」です。これはログメッセージの重要度を示すもので、デフォルトで5つのレベルが定義されています。 プログラムの実行時に「このレベル以上のログだけ表示する」といった設定が可能です。
| レベル名 | 数値 | 主な用途 |
|---|---|---|
| DEBUG | 10 | 開発中の詳細な情報。変数の値や処理の通過点など、問題解決の手がかりとなる情報。 |
| INFO | 20 | プログラムが正常に動作していることを示す情報。処理の開始・終了、ユーザーの操作記録など。 |
| WARNING | 30 | すぐにはエラーにならないが、注意が必要な状況。非推奨の機能が使われた、設定値が古くてデフォルト値を使った、など。 |
| ERROR | 40 | 処理が失敗したことを示すエラー。ファイルの読み込み失敗、データベース接続エラーなど。 |
| CRITICAL | 50 | アプリケーションの実行継続が困難な、致命的なエラー。メモリ不足やシステムのコア部分の障害など。 |
デフォルトではWARNING以上のレベルのログのみが表示されます。 開発中はDEBUGレベルまで表示して詳細な情報を追い、本番運用ではINFOやWARNING以上に絞って重要な情報だけを監視する、といった使い分けが簡単に行えます。
import logging
# ログの基本設定。レベルをDEBUGにすることで、全てのレベルのログが表示されるようになる
logging.basicConfig(level=logging.DEBUG)
logging.debug("これはデバッグメッセージです。変数の値は X です。")
logging.info("処理を開始しました。")
logging.warning("設定ファイルが見つかりません。デフォルト値を使用します。")
logging.error("データベースへの接続に失敗しました。")
logging.critical("致命的なエラーが発生しました。アプリケーションを終了します。")
ログの見栄えを整える「フォーマッタ」
ログには、メッセージ本体だけでなく「いつ」「どこで」といった付加情報が欠かせません。loggingモジュールでは、Formatterを使ってログの出力形式を自由にカスタマイズできます。
basicConfigのformat引数に、特定のキーワードを含む文字列を渡すことで、ログの書式を設定できます。
import logging
log_format = '%(asctime)s - %(levelname)s - %(message)s'
logging.basicConfig(level=logging.INFO, format=log_format)
logging.info("フォーマットされたログメッセージです。")
# 出力例: 2025-11-05 22:30:00,123 - INFO - フォーマットされたログメッセージです。
よく使われるフォーマット変数をいくつか紹介します。
%(asctime)s: ログが記録された日時%(levelname)s: ログのレベル名 (INFO, WARNINGなど)%(message)s: ログのメッセージ本体%(name)s: ロガーの名前(後述)%(filename)s: ファイル名%(lineno)d: 行番号
例えば、より詳細な情報をログに含めたい場合は、以下のように設定します。
import logging
log_format = '%(asctime)s - %(name)s - %(levelname)s - [%(filename)s:%(lineno)d] - %(message)s'
logging.basicConfig(level=logging.DEBUG, format=log_format)
logger = logging.getLogger('my_app')
logger.debug("詳細なデバッグ情報")
# 出力例: 2025-11-05 22:35:10,456 - my_app - DEBUG - [main.py:8] - 詳細なデバッグ情報
このようにフォーマットを統一することで、ログの可読性が格段に向上し、問題発生時の原因究明が迅速になります。
ログの行き先を管理する「ハンドラ」とファイル出力

loggingモジュールの強力な機能の一つが、ログの出力先を柔軟に制御できるハンドラです。 ハンドラは、ロガーによって生成されたログを受け取り、指定された場所に出力する役割を担います。
- StreamHandler: コンソール(標準出力や標準エラー出力)にログを出力します。
basicConfigのデフォルトの出力先です。 - FileHandler: ファイルにログを出力します。
例えば、「コンソールにはINFOレベル以上の情報を簡潔に表示し、ファイルにはDEBUGレベル以上の詳細な情報をすべて記録する」といった設定が可能です。
import logging
# ロガーを取得
logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG) # ロガー自体のレベルは最も低く設定
# 1. コンソール用ハンドラ (INFO以上)
stream_handler = logging.StreamHandler()
stream_handler.setLevel(logging.INFO)
stream_formatter = logging.Formatter('%(levelname)s: %(message)s')
stream_handler.setFormatter(stream_formatter)
# 2. ファイル用ハンドラ (DEBUG以上)
file_handler = logging.FileHandler('app.log', encoding='utf-8')
file_handler.setLevel(logging.DEBUG)
file_formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
file_handler.setFormatter(file_formatter)
# ロガーにハンドラを追加
logger.addHandler(stream_handler)
logger.addHandler(file_handler)
logger.debug("これはファイルにのみ記録されるメッセージです。")
logger.info("このメッセージはコンソールとファイルの両方に出力されます。")
logger.error("このエラーも両方に出力されます。")
ログがたまりすぎるのを防ぐ「ログローテーション」
長期間稼働するアプリケーションでは、ログファイルがどんどん大きくなり、ディスク容量を圧迫する可能性があります。 この問題を解決するのがログローテーションです。ログローテーションは、ログファイルが一定のサイズに達したり、一定期間が経過したりしたときに、自動で新しいファイルに切り替える仕組みです。
logging.handlersモジュールには、便利なローテーション用ハンドラが用意されています。
- RotatingFileHandler: ファイルサイズに基づいてローテーションを行います。
- TimedRotatingFileHandler: 時間(深夜0時、1時間ごとなど)に基づいてローテーションを行います。
以下は、ログファイルが1MBに達するごとにローテーションし、最大5世代分のバックアップファイルを保持する例です。
import logging
from logging.handlers import RotatingFileHandler
logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)
# 1MBでローテーション、バックアップは5つまで
handler = RotatingFileHandler(
'app_rotate.log',
maxBytes=1000000,
backupCount=5,
encoding='utf-8'
)
formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
handler.setFormatter(formatter)
logger.addHandler(handler)
# 大量のログを書き込むテスト
for i in range(100000):
logger.info(f"ログメッセージ番号 {i}")
この設定により、app_rotate.logが1MBを超えると、app_rotate.log.1、app_rotate.log.2… のように古いログがリネームされて保存され、ログファイルの肥大化を防ぎます。 本番環境ではログローテーションの設定は必須と言えるでしょう。
大規模開発でも役立つベストプラクティス
最後に、より実践的で堅牢なログ設計のためのベストプラクティスをいくつか紹介します。
- 各モジュールでは
getLogger(__name__)を使う: 各Pythonファイル(モジュール)でロガーを取得する際は、logging.getLogger(__name__)とするのが定石です。__name__にはモジュール名(例: `my_project.utils`)が自動的に入るため、ログに出力した際にどのモジュールからのメッセージかが一目瞭然になります。 - ログ設定は一元管理する: ログレベルやハンドラ、フォーマッタの設定は、アプリケーションの起動時に一箇所で行うのが理想です。 設定をまとめたモジュールを作成したり、設定ファイル(YAMLやJSON)から読み込むようにすると、管理が容易になります。
- ライブラリとアプリケーションのロガーを分離する: 自分で作成したライブラリ内でログを出力する場合、ライブラリ側で
basicConfigを呼び出したり、ハンドラを追加したりすべきではありません。 ライブラリ利用者がアプリケーション全体でログ設定を制御できるように、ライブラリはgetLogger(__name__)でロガーを取得してメッセージを記録するだけに留めるべきです。 - 機密情報をログに出力しない: パスワード、APIキー、個人情報などの機密データは、決してログに含めないでください。 誤って出力しないよう、フィルタ機能を使ったり、コードレビューで注意したりする仕組みが重要です。
- 構造化ロギングを検討する: 大規模なシステムでは、ログを自動的に収集・分析することが一般的です。その際、ログがJSONのような構造化された形式だと、機械的な処理が非常に容易になります。
まとめ
Pythonのloggingモジュールは、単なるメッセージ出力以上の強力な機能を提供する、ソフトウェア開発に不可欠なツールです。 ログレベル、フォーマッタ、ハンドラといったコンポーネントを理解し、適切に設定することで、デバッグの効率を飛躍的に向上させ、安定したアプリケーション運用を支えることができます。
最初は少し設定が複雑に感じるかもしれませんが、一度基本的な使い方をマスターすれば、printデバッグには戻れなくなるはずです。 この記事を参考に、あなたの次のプロジェクトから、ぜひloggingモジュールを活用してみてください。
この記事は、Python公式ドキュメント (Logging HOWTO) や、各種技術ブログの情報を参考に作成されました。
“`
—
### 納品用HTML(citationタグ除去済み)
“`html
【Python logging入門】printデバッグからの卒業!プロが教えるログ設計と実践テクニック
プログラムの開発中、変数の値を確認したり、処理がどこまで進んだかを確認したりするためにprint()関数を多用していませんか?小規模なスクリプトであればそれでも問題ありませんが、プロジェクトが複雑になるにつれて、あちこちに仕込んだprint()文の管理は煩雑になりがちです。 今回は、そんな「printデバッグ」から卒業し、より高度で体系的なログ管理を実現するためのPython標準ライブラリloggingモジュールについて、初心者にも分かりやすく解説します。
さようならprintデバッグ!loggingモジュールを使うべき理由
多くのPython初学者がデバッグで最初にお世話になるのがprint()関数です。 手軽で直感的な反面、本格的なアプリケーション開発ではいくつかの課題が浮上します。
- 情報過多とノイズ:デバッグ用の表示と、本来ユーザーに見せるべき出力がコンソールに混在し、非常に見づらくなります。
- 手作業でのON/OFF:デバッグが終わるたびに、大量の
print()文を一つひとつコメントアウトしたり削除したりする必要があり、手間がかかる上に消し忘れのリスクもあります。 - 情報量の不足:単純なメッセージしか表示できないため、「いつ」「どのファイルのどの部分で」そのメッセージが出力されたのかといった付加情報がありません。
こうしたprint()の弱点を解決するのがloggingモジュールです。 loggingは、単にメッセージを表示するだけでなく、「誰が、いつ、どこで、どのくらい重要か」という文脈情報を含んだ「ログ」を体系的に記録するための強力な仕組みを提供します。
- ログレベルの管理:「デバッグ」「情報」「警告」「エラー」など、メッセージの重要度に応じてレベル分けし、表示するレベルを簡単に切り替えられます。
- 柔軟なフォーマット:日時、ファイル名、行番号、モジュール名などをログメッセージに自動で追加できます。
- 出力先の自由な制御:コンソールへの表示だけでなく、ファイルへの保存や、ネットワーク経由でのログサーバーへの送信などを簡単に行えます。
一言で言えば、printが場当たりのメモ書きだとすれば、loggingは整理された公式の議事録のようなものです。この機会に、プロフェッショナルなログ管理術を身につけましょう。
ログの重要度を使い分ける「ログレベル」

loggingモジュールの最も基本的な機能が「ログレベル」です。これはログメッセージの重要度を示すもので、デフォルトで5つのレベルが定義されています。 プログラムの実行時に「このレベル以上のログだけ表示する」といった設定が可能です。
| レベル名 | 数値 | 主な用途 |
|---|---|---|
| DEBUG | 10 | 開発中の詳細な情報。変数の値や処理の通過点など、問題解決の手がかりとなる情報。 |
| INFO | 20 | プログラムが正常に動作していることを示す情報。処理の開始・終了、ユーザーの操作記録など。 |
| WARNING | 30 | すぐにはエラーにならないが、注意が必要な状況。非推奨の機能が使われた、設定値が古くてデフォルト値を使った、など。 |
| ERROR | 40 | 処理が失敗したことを示すエラー。ファイルの読み込み失敗、データベース接続エラーなど。 |
| CRITICAL | 50 | アプリケーションの実行継続が困難な、致命的なエラー。メモリ不足やシステムのコア部分の障害など。 |
デフォルトではWARNING以上のレベルのログのみが表示されます。 開発中はDEBUGレベルまで表示して詳細な情報を追い、本番運用ではINFOやWARNING以上に絞って重要な情報だけを監視する、といった使い分けが簡単に行えます。
import logging
# ログの基本設定。レベルをDEBUGにすることで、全てのレベルのログが表示されるようになる
logging.basicConfig(level=logging.DEBUG)
logging.debug("これはデバッグメッセージです。変数の値は X です。")
logging.info("処理を開始しました。")
logging.warning("設定ファイルが見つかりません。デフォルト値を使用します。")
logging.error("データベースへの接続に失敗しました。")
logging.critical("致命的なエラーが発生しました。アプリケーションを終了します。")
ログの見栄えを整える「フォーマッタ」
ログには、メッセージ本体だけでなく「いつ」「どこで」といった付加情報が欠かせません。loggingモジュールでは、Formatterを使ってログの出力形式を自由にカスタマイズできます。
basicConfigのformat引数に、特定のキーワードを含む文字列を渡すことで、ログの書式を設定できます。
import logging
log_format = '%(asctime)s - %(levelname)s - %(message)s'
logging.basicConfig(level=logging.INFO, format=log_format)
logging.info("フォーマットされたログメッセージです。")
# 出力例: 2025-11-05 22:30:00,123 - INFO - フォーマットされたログメッセージです。
よく使われるフォーマット変数をいくつか紹介します。
%(asctime)s: ログが記録された日時%(levelname)s: ログのレベル名 (INFO, WARNINGなど)%(message)s: ログのメッセージ本体%(name)s: ロガーの名前(後述)%(filename)s: ファイル名%(lineno)d: 行番号
例えば、より詳細な情報をログに含めたい場合は、以下のように設定します。
import logging
log_format = '%(asctime)s - %(name)s - %(levelname)s - [%(filename)s:%(lineno)d] - %(message)s'
logging.basicConfig(level=logging.DEBUG, format=log_format)
logger = logging.getLogger('my_app')
logger.debug("詳細なデバッグ情報")
# 出力例: 2025-11-05 22:35:10,456 - my_app - DEBUG - [main.py:8] - 詳細なデバッグ情報
このようにフォーマットを統一することで、ログの可読性が格段に向上し、問題発生時の原因究明が迅速になります。
ログの行き先を管理する「ハンドラ」とファイル出力

loggingモジュールの強力な機能の一つが、ログの出力先を柔軟に制御できるハンドラです。 ハンドラは、ロガーによって生成されたログを受け取り、指定された場所に出力する役割を担います。
- StreamHandler: コンソール(標準出力や標準エラー出力)にログを出力します。
basicConfigのデフォルトの出力先です。 - FileHandler: ファイルにログを出力します。
例えば、「コンソールにはINFOレベル以上の情報を簡潔に表示し、ファイルにはDEBUGレベル以上の詳細な情報をすべて記録する」といった設定が可能です。
import logging
# ロガーを取得
logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG) # ロガー自体のレベルは最も低く設定
# 1. コンソール用ハンドラ (INFO以上)
stream_handler = logging.StreamHandler()
stream_handler.setLevel(logging.INFO)
stream_formatter = logging.Formatter('%(levelname)s: %(message)s')
stream_handler.setFormatter(stream_formatter)
# 2. ファイル用ハンドラ (DEBUG以上)
file_handler = logging.FileHandler('app.log', encoding='utf-8')
file_handler.setLevel(logging.DEBUG)
file_formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
file_handler.setFormatter(file_formatter)
# ロガーにハンドラを追加
logger.addHandler(stream_handler)
logger.addHandler(file_handler)
logger.debug("これはファイルにのみ記録されるメッセージです。")
logger.info("このメッセージはコンソールとファイルの両方に出力されます。")
logger.error("このエラーも両方に出力されます。")
ログがたまりすぎるのを防ぐ「ログローテーション」
長期間稼働するアプリケーションでは、ログファイルがどんどん大きくなり、ディスク容量を圧迫する可能性があります。 この問題を解決するのがログローテーションです。ログローテーションは、ログファイルが一定のサイズに達したり、一定期間が経過したりしたときに、自動で新しいファイルに切り替える仕組みです。
logging.handlersモジュールには、便利なローテーション用ハンドラが用意されています。
- RotatingFileHandler: ファイルサイズに基づいてローテーションを行います。
- TimedRotatingFileHandler: 時間(深夜0時、1時間ごとなど)に基づいてローテーションを行います。
以下は、ログファイルが1MBに達するごとにローテーションし、最大5世代分のバックアップファイルを保持する例です。
import logging
from logging.handlers import RotatingFileHandler
logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)
# 1MBでローテーション、バックアップは5つまで
handler = RotatingFileHandler(
'app_rotate.log',
maxBytes=1000000,
backupCount=5,
encoding='utf-8'
)
formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
handler.setFormatter(formatter)
logger.addHandler(handler)
# 大量のログを書き込むテスト
for i in range(100000):
logger.info(f"ログメッセージ番号 {i}")
この設定により、app_rotate.logが1MBを超えると、app_rotate.log.1、app_rotate.log.2… のように古いログがリネームされて保存され、ログファイルの肥大化を防ぎます。 本番環境ではログローテーションの設定は必須と言えるでしょう。
大規模開発でも役立つベストプラクティス
最後に、より実践的で堅牢なログ設計のためのベストプラクティスをいくつか紹介します。
- 各モジュールでは
getLogger(__name__)を使う: 各Pythonファイル(モジュール)でロガーを取得する際は、logging.getLogger(__name__)とするのが定石です。__name__にはモジュール名(例: `my_project.utils`)が自動的に入るため、ログに出力した際にどのモジュールからのメッセージかが一目瞭然になります。 - ログ設定は一元管理する: ログレベルやハンドラ、フォーマッタの設定は、アプリケーションの起動時に一箇所で行うのが理想です。 設定をまとめたモジュールを作成したり、設定ファイル(YAMLやJSON)から読み込むようにすると、管理が容易になります。
- ライブラリとアプリケーションのロガーを分離する: 自分で作成したライブラリ内でログを出力する場合、ライブラリ側で
basicConfigを呼び出したり、ハンドラを追加したりすべきではありません。 ライブラリ利用者がアプリケーション全体でログ設定を制御できるように、ライブラリはgetLogger(__name__)でロガーを取得してメッセージを記録するだけに留めるべきです。 - 機密情報をログに出力しない: パスワード、APIキー、個人情報などの機密データは、決してログに含めないでください。 誤って出力しないよう、フィルタ機能を使ったり、コードレビューで注意したりする仕組みが重要です。
- 構造化ロギングを検討する: 大規模なシステムでは、ログを自動的に収集・分析することが一般的です。その際、ログがJSONのような構造化された形式だと、機械的な処理が非常に容易になります。
まとめ
Pythonのloggingモジュールは、単なるメッセージ出力以上の強力な機能を提供する、ソフトウェア開発に不可欠なツールです。 ログレベル、フォーマッタ、ハンドラといったコンポーネントを理解し、適切に設定することで、デバッグの効率を飛躍的に向上させ、安定したアプリケーション運用を支えることができます。
最初は少し設定が複雑に感じるかもしれませんが、一度基本的な使い方をマスターすれば、printデバッグには戻れなくなるはずです。 この記事を参考に、あなたの次のプロジェクトから、ぜひloggingモジュールを活用してみてください。
この記事は、Python公式ドキュメント (Logging HOWTO) や、各種技術ブログの情報を参考に作成されました。
“`
Sources
help
qiita.com
ambitious-engineer.com
n-works.link
sejuku.net
zenn.dev
zenn.dev
zenn.dev
superbusinessman.biz
zenn.dev
python.org
musclecoding.com
tohoho-web.com
okpy.net
hiros-dot.net
lifewithpython.com
zenn.dev
python.org
python.org
nkhn37.net
qiita.com
jimaru.blog
kamedassou.com
carmatec.com
blogspot.com
qiita.com
fullfront.co.jp
qiita.com