EC-CUBEのログでERRORの件数を数えたら0件でした。ところが同じ日のHTTPステータスには500が出ています。おかしいと思ってパターンを変えて数え直したら、ERRORは310件ありました。grepのパターンの末尾にコロンを1つ付けていたせいで、1件もマッチしていなかったのです。
この記事では、なぜ0件に化けたのか、そして「0件」という結果を信じてよいかをどう判断するかを書きます。件数を数えるパターンの安全な書き方も、実際に動かして確かめたものを載せます。
0件と310件の差はコロン1文字だった
使っていたのはこのパターンです。
grep -c '\.ERROR:' front-YYYY-MM-DD.log
# → 0
コロンを外すと結果が変わります。
grep -cE '\.ERROR' front-YYYY-MM-DD.log
# → 310
原因はログの書式です。EC-CUBEのログは、環境やチャンネルの設定によってレベル表記のあとにコロンが付く形と付かない形があります。付かない形はこうなっています。
[2026-08-05 00:01:03] front.ERROR [1d8594b] [4711] [Eccube\Log\Logger:log:66] - Something failed
front.ERROR の直後は半角スペースで、コロンではありません。一方、コロンが付く形式ならこうです。
[2026-08-05 10:00:00] front.ERROR: Something failed
'\.ERROR:' は後者にしかマッチしません。前者のログに対しては、ERRORが何件あっても常に0を返します。
0件が「異常なし」ではなく「数え損ね」のサイン
件数が0だったとき、それを信じてよいかを判断する材料は2つあります。どちらも、ログの中身を読み込む前に気づけるものです。
1つ目は、他の指標と矛盾していないか。 HTTPステータスに500が出ているのに、アプリケーションログのERRORが0件というのは噛み合いません。500を返したなら、ふつうは何かがログに残ります。実際にこのときも、500が出ているのにERROR=0という組み合わせが引っかかりでした。
2つ目は、普段の件数と比べて落差が大きすぎないか。 いつも数百件出ているログが突然0件になったとき、まず疑うべきは「エラーが消えた」ではなく「数え方が変わった・合っていない」です。エラーが本当にゼロになるより、パターンが外れるほうがずっとよく起きます。
この2つのどちらかに当てはまったら、次の節の確認をします。
レベル表記の実物を見てからパターンを決める
推測でパターンを直すより、ログに実際どう書かれているかを見るのが早いです。レベル表記だけを抜き出して集計します。
grep -oE '\] [a-z]+\.[A-Z]+' front-YYYY-MM-DD.log | sort | uniq -c | sort -rn
コロンの付かない書式のログで実行すると、こう出ました。
16367 ] front.INFO
310 ] front.ERROR
ERRORが310件あることと、レベル表記のあとにコロンが無いことが同時に分かります。この出力を見てからパターンを組めば、書式を取り違えることはありません。どのレベルがどれだけ出ているかの全体像もつかめるので、ERRORだけを数えるより先にこれを実行するほうが結局早いです。
コロンを外すだけでは数え過ぎになる
ここで、単に '\.ERROR' にすれば解決というわけではありません。このパターンは、本文中に .ERROR という文字列が出てくる行も拾ってしまいます。次のログで試すと分かります。
[2026-08-05 10:00:00] front.ERROR [a1] [4711] - real error
[2026-08-05 10:00:01] front.INFO [a2] [4711] - message mentions .ERROR in text
[2026-08-05 10:00:02] front.WARNING [a3] [4711] - ok
grep -cE '\.ERROR' tricky.log
# → 2 (INFOの行まで数えている)
実際のERRORは1件なので、1件多く数えています。0件になる誤りとは逆方向ですが、これも間違いです。
レベル表記の区切りまで含めて指定すると、両方の書式で正しく数えられます。
grep -cE '\.ERROR[: ]' front-YYYY-MM-DD.log
[: ] は「コロンまたは半角スペース」です。コロン付きの書式でもコロン無しの書式でも、レベル表記の直後にはどちらかが来るので、両方に対応できます。実際に2つの書式のログで試して、どちらも正しい件数が出ることを確認しました。
本文中の .ERROR まで確実に外したいなら、行頭のレベル表記の位置に限定します。
grep -cE '^\[[^]]+\] [a-z]+\.ERROR[: ]' tricky.log
# → 1 (本文中の .ERROR を除外できる)
先頭の ^\[[^]]+\] が日時部分、続く [a-z]+\. がチャンネル名にあたります。ログの行構造そのものを指定しているので、本文に何が書かれていても影響を受けません。
レベルごとの件数はまとめて出す
ERRORとCRITICALを別々のパターンで数えると、片方だけ書式を間違えて取りこぼす余地が残ります。レベルを抜き出して一度に集計すれば、そもそも取りこぼしが起きません。
grep -oE '^\[[^]]+\] [a-z]+\.[A-Z]+' front-YYYY-MM-DD.log | awk '{print $NF}' | sort | uniq -c
先ほどの3行のログに対する実行結果です。
1 front.ERROR
1 front.INFO
1 front.WARNING
数えたいレベルを事前に決めなくてよいのが利点です。想定していなかったレベルが出ていても、この出力には現れます。
数え方の妥当性は別の指標で裏を取る
今回の誤りは、grepの書き方の問題として片付けることもできます。ただ、実際に危なかったのは0件という結果をそのまま報告してしまったことでした。パターンが合っているかどうかは、そのパターンの結果だけを見ていても分かりません。
だから、アプリケーションログのERROR件数のような数字は、単独で見ずに別の指標と突き合わせます。HTTPステータスの5xxが出ているか、普段の件数と比べてどうか。この2つと矛盾しない範囲に収まっていれば、数え方はおおむね合っています。矛盾していたら、エラーの中身を調べる前に数え方を疑います。
ログの調べ方については本番環境でエラーが発生した時のログ確認方法とログの種類(チャンネル)を追加する方法でも書いています。