CHAPTER 3 ・ エラーに強いコード / Lesson 12

ログを残す(logging)

print の代わりに、記録として残す。何件処理して、何件スキップしたかを、あとから確認できる形にします。

目安 18分 ・ インストール不要

このレッスンが終わったら

「logging は、処理の記録を重要度つきで残す仕組み。ログがあれば、あとから『何が起きたか』を確かめられる」と言える。

STEP 1

こんな場面、ありませんか?

Lesson 09 では、読めない行を飛ばしながら、messy.csv を最後まで処理しました。進み具合は、print で表示するのが、いちばん手軽です。

print で、進み具合を表示するサンプルファイル付き・書き換えOK
Ctrl / ⌘ + Enter でも実行

この欄では、サンプルファイルが使えます。実行のたびに、ファイルは最初の状態に戻ります。

結果
▶ 実行を押すと、ここに結果が出ます

画面で見るぶんには、これで十分です。ただ、毎晩、自動で動かすプログラムだと、困ることが出てきます。

  • 画面を閉じると、表示は消えます。翌朝、「昨夜は何があったか」を確かめられません
  • 「ただの進行報告」と「見逃せない問題」が、同じ見た目で並びます

これを解決するのが、処理の記録を残す仕組み、ログ(logging)です。

STEP 2

logging を動かしてみる

標準ライブラリの logging を使います。まず、重要度ごとに、4種類のログを出してみます。

重要度つきのログを出すコードは書き換えられます
Ctrl / ⌘ + Enter でも実行
結果
▶ 実行を押すと、ここに結果が出ます

行の先頭に、INFO WARNING ERROR と、重要度の名前が付きました。そして、debug のログは、表示されていません。理由は、次のStepで説明します。

では、Step 1 の処理を、ログで書き直します。最後に、何件成功して、何件スキップしたかの要約も残します。

件数の要約まで、ログに残すサンプルファイル付き・書き換えOK
Ctrl / ⌘ + Enter でも実行

この欄では、サンプルファイルが使えます。実行のたびに、ファイルは最初の状態に戻ります。

結果
▶ 実行を押すと、ここに結果が出ます

「問題の行」は WARNING、「始まりと終わりの件数」は INFO と、見た目で区別できます。

STEP 3

仕組みを読む

ログには、重要度の段階があります。これをログレベルと呼びます。

  1. DEBUG開発中の詳しい確認用
  2. INFO通常の進行(開始・終了・件数)
  3. WARNING注意(行を飛ばした、など)
  4. ERROR失敗(ファイルが読めない)
  5. CRITICAL続けられない重大な問題
右ほど重大です。level= で指定したレベル以上だけが、表示されます。

Step 2 の設定は level=logging.INFO でした。だから、INFO 以上が出て、いちばん下の DEBUG は出なかったのです。設定の部分を、1行ずつ日本語にします。

basicConfig の設定を、1行ずつ日本語にする
logging.basicConfig(ログの出し方を、次のように決める
stream=sys.stdout,出す先は、画面(標準出力)にする
level=logging.INFO,INFO 以上のログだけを、出す
format="%(levelname)s %(message)s",「重要度の名前」と「本文」を、1行にして出す
force=True,前の設定が残っていても、今回の設定で上書きする
format="%(levelname)s %(message)s" の部品
%(levelname)s重要度の名前(INFO など)
%(message)slogging.info(...) に渡した本文

この書式には、時刻を表す %(asctime)s も使えます。自分のパソコンで使うときは、時刻を必ず入れます。「いつ起きたか」が分からないログは、役に立たないからです。このサイトでは、実行するたびに時刻が変わってしまうため、使いません。

STEP 4

変えて、試す

① 表示するレベルを変える。 同じログのまま、level だけを WARNING に変えてみます。

level を WARNING にするコードは書き換えられます
Ctrl / ⌘ + Enter でも実行
結果
▶ 実行を押すと、ここに結果が出ます

INFO が消え、WARNING と ERROR だけが残りました。確認中は DEBUG、普段は INFO か WARNINGと、コードを書き換えずに出す量を調整できます。これが、print との大きな違いです。

② ファイルに残す。 stream= の代わりに filename= を指定すると、ログはファイルに書かれます。書き終えたあと、読み返します。

ログをファイルに書いて、読み返すコードは書き換えられます
Ctrl / ⌘ + Enter でも実行
結果
▶ 実行を押すと、ここに結果が出ます

画面ではなく run.log に書かれ、open で読み返すと、中身が見えました。自分のパソコンでは、このファイルが残るので、翌朝に開いて確認できます。ファイルは、動かすたびに追記されます。毎回作り直したいときは、filemode="w" を足します。

実際の業務用のログは、こんな形をしています(サンプルの app.log です)。1行に、時刻・重要度・本文が入っています。

app.logサンプルファイル
2025-10-01 09:01:18 WARNING 金額が空欄の行があります(1件目)
2025-10-01 09:01:53 INFO 集計を終了しました(2件目)
2025-10-01 09:02:39 ERROR 日付を読み取れませんでした(3件目)
2025-10-01 09:03:19 INFO 集計を開始しました(4件目)
2025-10-01 09:03:48 INFO 集計を開始しました(5件目)
2025-10-01 09:04:08 WARNING 金額が空欄の行があります(6件目)
2025-10-01 09:05:31 ERROR 日付を読み取れませんでした(7件目)
2025-10-01 09:05:49 INFO 集計を終了しました(8件目)
2025-10-01 09:06:15 INFO 集計を終了しました(9件目)
2025-10-01 09:07:43 ERROR 日付を読み取れませんでした(10件目)
2025-10-01 09:08:06 INFO ファイルを読み込みました(11件目)
2025-10-01 09:08:16 ERROR 日付を読み取れませんでした(12件目)
2025-10-01 09:09:19 INFO ファイルを読み込みました(13件目)
2025-10-01 09:09:56 INFO 集計を開始しました(14件目)
2025-10-01 09:10:46 ERROR 日付を読み取れませんでした(15件目)
2025-10-01 09:12:11 INFO ファイルを読み込みました(16件目)
2025-10-01 09:12:48 ERROR 日付を読み取れませんでした(17件目)
2025-10-01 09:13:59 WARNING 金額が空欄の行があります(18件目)
2025-10-01 09:14:52 INFO ファイルを読み込みました(19件目)
2025-10-01 09:16:00 INFO 集計を開始しました(20件目)
2025-10-01 09:16:57 WARNING 金額が空欄の行があります(21件目)
2025-10-01 09:17:16 WARNING 金額が空欄の行があります(22件目)
2025-10-01 09:17:54 INFO 集計を終了しました(23件目)
2025-10-01 09:18:22 INFO 集計を開始しました(24件目)
2025-10-01 09:19:29 INFO 集計を開始しました(25件目)
2025-10-01 09:20:04 ERROR 日付を読み取れませんでした(26件目)
2025-10-01 09:20:17 INFO 集計を開始しました(27件目)
2025-10-01 09:21:45 INFO 集計を終了しました(28件目)
2025-10-01 09:22:09 INFO ファイルを読み込みました(29件目)
2025-10-01 09:22:55 INFO 集計を開始しました(30件目)
2025-10-01 09:23:01 INFO ファイルを読み込みました(31件目)
2025-10-01 09:23:47 ERROR 日付を読み取れませんでした(32件目)
2025-10-01 09:24:56 WARNING 金額が空欄の行があります(33件目)
2025-10-01 09:26:08 INFO ファイルを読み込みました(34件目)
2025-10-01 09:26:43 INFO 集計を開始しました(35件目)
2025-10-01 09:28:02 ERROR 日付を読み取れませんでした(36件目)
2025-10-01 09:28:51 ERROR 日付を読み取れませんでした(37件目)
2025-10-01 09:29:46 INFO 集計を開始しました(38件目)
2025-10-01 09:30:59 INFO 集計を開始しました(39件目)
2025-10-01 09:32:28 INFO ファイルを読み込みました(40件目)

③ ログに書いてはいけないもの。 ログは、ファイルに残り、あとから複数の人が開くかもしれません。

書いてはいけない

logging.info(f"口座番号 {account_no} への振込を確認")
logging.info(f"ログイン: {user} / パスワード {password}")

こう書く

logging.info(f"取引先コード {code} の振込を確認")
logging.info(f"ログイン: {user}")
パスワード・APIキー・口座番号・個人の住所や電話番号は、ログに書きません。コードや件数など、あとで追跡できる最小限の情報にします。
STEP 5

自分で書く

どのお題も、ログの設定は書いてあります(format に時刻は入れていません)。

お題① sales.csv を読み、読み込んだ件数と、売上の合計を、INFO のログに残してください。

お題①:件数をログに残すサンプルファイル付き・書き換えOK
Ctrl / ⌘ + Enter でも実行

この欄では、サンプルファイルが使えます。実行のたびに、ファイルは最初の状態に戻ります。

結果
▶ 実行を押すと、ここに結果が出ます
ヒントを見る

logging.info(f"{len(rows)}件を読み込みました") とします。合計は sum(int(row["売上"]) for row in rows) で求め、f文字列の {total:,} で桁区切りにします。

答えの例を見る
import csv
import logging
import sys
logging.basicConfig(stream=sys.stdout, level=logging.INFO, format="%(levelname)s %(message)s", force=True)

with open("sales.csv", encoding="utf-8") as f:
  rows = list(csv.DictReader(f))

logging.info(f"{len(rows)}件を読み込みました")
total = sum(int(row["売上"]) for row in rows)
logging.info(f"売上の合計は {total:,} 円です")

お題② messy.csv の「数量」が空欄の行に、WARNING のログを出してください。最後に、空欄の件数を INFO で残します。

お題②:警告ログを出すサンプルファイル付き・書き換えOK
Ctrl / ⌘ + Enter でも実行

この欄では、サンプルファイルが使えます。実行のたびに、ファイルは最初の状態に戻ります。

結果
▶ 実行を押すと、ここに結果が出ます
ヒントを見る

enumerate(rows, start=2) で行番号を付けて回し、row["数量"] == "" のときに logging.warning(...) を出して、件数を1つ増やします。ループのあとで logging.info(...) です。

答えの例を見る
import csv
import logging
import sys
logging.basicConfig(stream=sys.stdout, level=logging.INFO, format="%(levelname)s %(message)s", force=True)

with open("messy.csv", encoding="utf-8") as f:
  rows = list(csv.DictReader(f))

empty = 0
for line_no, row in enumerate(rows, start=2):
  if row["数量"] == "":
      empty += 1
      logging.warning(f"{line_no}行目: 数量が空欄です")
logging.info(f"空欄の行は {empty} 件でした")

お題③ ログを task.log に書く設定を完成させ、書き終えたあと、ERROR で始まる行が何行あるかを表示してください。

お題③:ファイルに残して、読み返すコードは書き換えられます
Ctrl / ⌘ + Enter でも実行
結果
▶ 実行を押すと、ここに結果が出ます
ヒントを見る

logging.basicConfig(filename="task.log", encoding="utf-8", level=logging.INFO, format="%(levelname)s %(message)s", force=True) と書きます。

答えの例を見る
import logging

logging.basicConfig(filename="task.log", encoding="utf-8", level=logging.INFO, format="%(levelname)s %(message)s", force=True)

logging.info("処理を開始します")
logging.error("ファイルを読めません")
logging.error("保存できません")

with open("task.log", encoding="utf-8") as f:
  lines = f.read().splitlines()
print(len([line for line in lines if line.startswith("ERROR")]))
STEP 6

仕事ならどう使う?

理解チェック「金額が空欄の行を飛ばして、処理は続けた」ことを残すとき、ふさわしいレベルはどれでしょう?

理解チェックログに書いてはいけないものは、どれでしょう?