グラフとチャートが表示されたデスクの2台のモニター
現場の実践

バッチ処理が「無言で失敗」する落とし穴とログ基盤の選び方

目次を見る

夜間バッチや定期インポート処理を運用していて、ジョブが「エラーも出さずに動かなかった」という経験はないでしょうか。
小規模なSaaSや社内システムの運用担当者、1人情シスやスタートアップのバックエンド担当者に向けて、この落とし穴と対処法を整理します。

ログを集めているのに障害に気づけない、という状態はよくある盲点です。
この記事では、その原因を段階的に分解し、自分のプロジェクトが該当するかの確認方法と、具体的な対策手順をまとめます。

何が起きるか: ログがあるのに障害に気づけない

典型的な事象はこうです。
顧客データの定期インポートジョブが夜間に動くはずなのに、ある日まったく起動していませんでした。

翌朝、サポートチームに「データが反映されていない」という問い合わせが来て初めて気づく、というケースです。

ログ基盤を導入していても、このパターンは防げません。
なぜなら、ログは「何かが起きたこと」を記録する仕組みであり、「何も起きなかったこと」は記録できないからです。

ジョブが起動さえしなければ、ログには1行も出力されません。
検索しても何も出てこないため、障害調査の起点すら作れない状態になります。

影響範囲は意外と広く、スケジューラの設定ミス、デプロイ時のcron設定漏れ、コンテナの再起動タイミングのずれなど、原因はさまざまです。
共通しているのは「ログ検索だけに頼った監視設計」という構造的な弱点です。

なぜ起きるか: ログ監視と死活監視の役割の違い

原因を分解すると、ログ検索(Elastic CloudやLoki、各種ホスト型ログAPIなど)と、ハートビート監視(heartbeat monitor、定期的に「生きている」信号を送る仕組み)の役割が混同されていることに行き着きます。

ログ検索は「起きた事象を後から掘り返す」ための道具です。
自己ホスト型のLoki(Grafana Labs製のログ集約システム)や、Elastic Cloud(Elasticsearchのマネージドサービス)は、複雑な検索やアラートパイプラインを組める反面、運用負荷が高めです。

一方、Better StackやホストされたログAPI(REST APIで直接ログを送受信するサービス)は、運用負荷を抑えつつ基本的な検索機能を提供します。
しかしこれらはすべて「ログが届いた後」の話であり、「ログが届かなかった」ことを検知する機能ではありません。

ここで整理が必要なのが、障害の2パターンです。
ジョブが「実行されて失敗した」場合と、「そもそも実行されなかった」場合は、根本的に別の問題として扱う必要があります。

前者はログに記録が残るため、ステータスやエラーメッセージから原因分析ができます。
後者は記録自体が存在しないため、ログ検索ツールをどれだけ高機能にしても検知できません。

この区別を怠ると、「ログ基盤を入れたから監視は万全」という誤った安心感につながります。
これが今回の落とし穴の核心です。

自分のプロジェクトが該当するか確認する方法

該当するかどうかは、以下の観点で確認できます。

  • 定期ジョブ(cron、Kubernetes CronJob、クラウドのスケジューラ)が「実行されたこと」自体を記録しているか
  • ジョブ実行結果の記録に、実行予定時刻・開始時刻・終了時刻・ステータス・処理件数が含まれているか
  • ジョブが起動しなかった場合にアラートが飛ぶ仕組みが別途あるか
  • ログ検索ツールの障害調査フローが「ログを検索する」の1手順だけで完結しているか

Kubernetes環境であれば、以下のコマンドでCronJobの直近の実行履歴を確認できます。

kubectl get cronjob <job-name> -o jsonpath='{.status.lastScheduleTime}'
kubectl get jobs --selector=job-name=<job-name>

lastScheduleTimeが更新されていない、あるいは期待する時刻から大きくずれている場合、スケジューラ側の問題を疑う必要があります。

Linux上でcronを使っている場合は、/var/log/syslogや/var/log/cronに実行ログが残っているか確認します。

grep CRON /var/log/syslog | tail -n 50

この出力に対象ジョブのエントリが見当たらない場合、cron自体が起動していないか、設定ファイル(/etc/crontabやユーザーのcrontab)の記述ミスが疑われます。

上記のいずれかに「ノー」が付く場合、今回の落とし穴に該当する可能性が高い状態です。

対策の手順

対策は、ログ検索とハートビート監視を分けて設計することに尽きます。
具体的な手順は以下のとおりです。

1. 各ジョブに一意の実行ID(run ID)を発行する。たとえばimp_20261009_0200_acmeのように、日時とアカウント名を含めた形式にすると検索が容易になります
2. 実行のたびに「予定時刻・開始時刻・終了時刻・ステータス・処理件数・トレースID」を1レコードとして記録する
3. 顧客のメールアドレスやアクセストークンなど個人情報・機密情報はログに含めない。GDPR第5条のデータ最小化原則は、EU圏外のプロジェクトでも設計指針として有効です
4. 別途ハートビート監視サービスを設定し、「猶予時間内に終端ステータス(成功・失敗いずれか)が届かない場合」にアラートを出す
5. 障害調査時は、実行IDとアカウントIDでログを検索し、ジョブが「未起動」「途中停止」「0件で完了」のどれだったかを判定する

特に手順5の「0件で完了」と「未起動」の区別は重要です。
0件は処理結果としてのデータであり、未起動はアラートが必要な異常です。
この2つを同じ「失敗」として扱うと、原因分析の初動が遅れます。

ツール選定については、運用負荷と調査の深さのトレードオフで考えます。

選択肢運用負荷調査の深さ向いているケース
自己ホスト型Loki高い深いストレージとインデックスを自前運用できるチーム
Elastic Cloud中程度深い複雑な検索やガバナンス要件がある場合
Better Stackなどマネージド型低いホスト型相応運用負荷を抑えつつ複数製品を比較したい場合
ホスト型ログAPI低い基本的小規模な障害調査と即時取り込みが目的の場合

小規模な顧客サポート系SaaSで、障害対応の目的が「スケジュールされたインポート結果の記録」と「インシデント発生時の検索」であれば、まずホスト型ログAPIで十分なケースが多いです。

REST APIで直接ログを送受信できるサービスであれば、SDKのインストールやクライアントライブラリのバージョン管理が不要になり、導入の初速が早くなります。

一方で、高度なアラートパイプライン、分散トレースのクエリ、エクスポート機能、ガバナンス統制が必要になった時点で、LokiやElastic Cloudのようなフルスタック構成への移行を検討すべきタイミングです。

ログ検索ツールの選定より先に、「ジョブが実行されなかったこと」を検知する仕組みを別立てで用意することが、障害の見落としを防ぐ最短の対策です。

まとめ

ログ基盤を導入しても、「実行されなかった」という障害は検知できません。
これはツールの機能不足ではなく、ログという仕組みそのものの構造的な限界です。

次に取れる一歩として、まず該当ジョブのkubectl get cronjobやcrontabログを確認し、直近の実行履歴が期待通りかをチェックしてみてください。

そのうえで、実行IDベースのログ記録とハートビート監視を分離して設計し直すことが、障害の早期発見につながります。
ツール選定はその後の話であり、運用負荷と調査の深さのバランスを見ながら、自己ホスト型かマネージド型かを判断すれば十分です。

参考

3 Hosted App Log API Alternatives to Self-Hosted Search for Small Business

この記事について: 本記事は AI を活用して作成し、forva AI 編集部が内容を確認・監修しています。

AI 駆動開発のご相談は forva AI へ。まずはお気軽にどうぞ。