💡 Tips

Cloudflare Workersのログ監視とエラー通知を個人開発で実装する手順【2025年版】

結論:Tail WorkersとWebhook通知の組み合わせが個人開発には最適

Cloudflare Workersの本番運用で最初につまずくのは「エラーが起きても気づけない」ことだ。個人開発でDatadogやSentryのような有料APMを常時契約するのはコスト的に厳しい。

結論として、個人開発規模なら次の構成で十分実用に耐える。

  • Tail WorkersでメインWorkerのログ・例外を捕捉する
  • 捕捉したログをSlack/Discord Webhookに整形して送信する
  • 重大度でフィルタし、通知の洪水を防ぐ

追加の月額コストはほぼゼロで組める。以下、実装手順を順番に説明する。

全体構成の考え方

Cloudflare WorkersにはリアルタイムでログをストリームするAPIとしてwrangler tailがあるが、これは手元で見る用のコマンドであり、常時監視には向かない。

常時監視にはTail Workersを使う。Tail Workersは、他のWorkerの実行ログ・例外・console出力をイベントとして受け取れる専用Workerだ。メインのWorker(API本体など)に紐づけておくと、そのWorkerの実行結果がすべてTail Worker側に転送されてくる。

処理の流れは次のとおり。

  1. メインWorkerでエラーが発生する
  2. Tail Workerがそのイベントを受信する
  3. Tail Worker内でエラー内容を判定し、Slack/Discordへfetchでpostする

手順1:Tail Workerを作成する

まず監視専用のWorkerプロジェクトを新規に作る。

npm create cloudflare@latest log-monitor -- --type=hello-world
cd log-monitor

src/index.tsをTail Worker用のハンドラに書き換える。

export default {
  async tail(events: TraceItem[], env: Env) {
    for (const event of events) {
      const hasError = event.exceptions.length > 0 ||
        event.logs.some((log) => log.level === "error");

      if (!hasError) continue;

      const message = event.exceptions[0]?.message ??
        event.logs.find((l) => l.level === "error")?.message.join(" ");

      await notify(env, {
        scriptName: event.scriptName,
        message: String(message),
        outcome: event.outcome,
      });
    }
  },
};

async function notify(env: Env, info: { scriptName?: string; message: string; outcome: string }) {
  await fetch(env.SLACK_WEBHOOK_URL, {
    method: "POST",
    headers: { "content-type": "application/json" },
    body: JSON.stringify({
      text: `:rotating_light: [${info.scriptName}] ${info.outcome}\n${info.message}`,
    }),
  });
}

ポイントは、exceptionsとlogsの両方を見ること。console.errorで出したログと、キャッチされなかった例外は別々に流れてくるため、片方だけ見ると通知漏れが起きる。

手順2:メインWorkerにTail Workerを紐づける

メインWorker側のwrangler.toml(またはwrangler.jsonc)に以下を追記する。

[[tail_consumers]]
service = "log-monitor"

Tail Worker側には環境変数としてWebhook URLをsecretで登録する。

npx wrangler secret put SLACK_WEBHOOK_URL

両方をデプロイすれば、メインWorkerで例外が起きるたびにSlackへ通知が飛ぶようになる。

手順3:通知の洪水を防ぐフィルタリング

実運用で必ず問題になるのが「同じエラーが数百件連続発生してSlackが埋まる」現象だ。個人開発ではレート制限をかけるのが現実的。

  • 同一エラーメッセージをKVやDurable Objectsに直近発生時刻を保存し、一定時間(例:5分)以内の重複通知を抑制する
  • outcomeが"ok"以外(exception, exceededCpu, canceledなど)のみ通知対象にする
  • 4xx系の想定内エラー(バリデーションエラーなど)はconsole.warnに留め、console.errorは本当に対応が必要なものだけに使う運用ルールを決める

実装コストと通知の質はトレードオフになるため、最初はシンプルなフィルタから始めて、ノイズが多ければ条件を足していく方が現実的だ。

通知先の比較

通知先実装の手間向いている用途
Slack Webhook低い(fetch一発)チーム利用・スレッドで議論したい場合
Discord Webhook低い(fetch一発)個人・小規模コミュニティ運用
メール(Resend等)中(APIキー管理)重大障害のみ確実に見たい場合
Sentry等のAPM高いが機能豊富スタックトレース解析・チーム規模拡大後

個人開発の初期段階ではSlackかDiscordのWebhookで十分であり、必要になった時点でAPMへ移行する順序がコスト効率がよい。

注意点

  • Tail WorkersはWorkers有料プラン相当の機能として提供されており、無料枠の制約や仕様は変わりやすいため、公式の最新情報を確認してほしい
  • Tail Worker自体もWorkerであるため、無限ループやfetch先の障害でTail Worker側がエラーになるケースがある。通知処理自体はtry-catchで囲み、失敗しても本体の処理に影響しないようにする
  • ログに個人情報や認証トークンを含めてSlackに流すと情報漏洩につながる。通知本文に含める情報は事前に取捨選択する

まとめ

Cloudflare WorkersのエラーはTail Workersで捕捉し、Slack/Discord Webhookに投げるだけで、追加コストほぼゼロの監視体制が作れる。まずは例外検知と簡易フィルタから始め、ノイズや見落としが出た箇所だけ条件を足していくのが個人開発における現実的な進め方だ。

Cloudflare Pages・Workersの周辺構成についてはCloudflare PagesにAstroサイトをデプロイする方法|手順と詰まりどころやCloudflare Queues × Workers TypeScript で作る最小ジョブキュー【個人開発向け】も参考にしてほしい。