UPSIDER Tech Blog

GCP Error Reportingで「最低限の異常検知」を低コストに実現する

はじめに

こんにちは。法人審査基盤の開発をしているMitomiです。

以前の記事で紹介があったように、UPSIDERでは法人カード基盤のマイクロサービス化を行いました。

tech.up-sider.com

私たちのサービスは「基盤」という立ち位置にある以上、ここで障害が発生すると基盤を利用している他のマイクロサービスにも影響が及んでしまいます。そのため異常検知には力を入れる必要がありました。エラーになりそうな箇所へアラートを仕込んだり、主要なメトリクスを用意したりといった備えはもちろんですが、それに加えて、「想定していなかった異常」にもできるだけ早く気づける状態にしておきたいという思いがありました。

一方で、最初から Datadog や Sentry のようなフル機能のAPMを導入するのはコスト面でもオンボーディング面でも負担が大きくなかなか踏み切れずにいました。

そこでまずは「最低限の異常検知」を低コストに実現する手段として、GCP標準機能である Error Reporting を採用しました。

docs.cloud.google.com

本記事では、Error Reportingで何ができるのか、どう実装したのか、運用して感じたメリットと限界について紹介します。

Error Reportingとは

Error ReportingはCloud Logging上のログエントリを分析し、エラーを集計してくれるGoogle Cloudのサービスです。

主に以下の機能があります。

  • エラーの集計とグループ化:エラー以上の重要度で出力されたログのうち、スタックトレースを含むエントリを自動的に拾い上げ、例外タイプ・上位フレーム・エラーメッセージなどから根本原因が同じと考えられるエラーを自動的にグループ化してくれます。
  • 通知:新規に発生したエラーグループや「解決済み」としたエラーグループが再発したタイミングで、メール・モバイルアプリ・Slackへの通知、またはWebhook にイベントを飛ばすことができます。

ログ経由でエラーを拾わせるだけでなく、Error Reporting API を使って明示的にエラーイベントを送信することも可能です。

docs.cloud.google.com

Error Reporting は Datadog や Sentry のようなフル機能のAPMではなく、一般的なAPMが提供している機能のうちエラー検知・グルーピング・通知の部分を切り出したものというイメージが近いです。

  • メトリクス(レイテンシ、QPSなど)の収集機能はない
  • 分散トレーシングやプロファイリングは別途用意する必要がある
  • カスタムダッシュボードは作れない

そのため、APMをすでに導入しているチームにとっては機能不足に感じられるかもしれません。一方で、Cloud Loggingの料金以外、Error Reporting自体の追加コストがほぼかからないため、これから監視基盤を整えていくチームにとっては、「まずError Reportingで異常検知を始める → 必要になったらAPMを足す」という段階的な導入の選択肢として扱える軽量な仕組みだと言えます。

通知の設定も簡単で、GCPコンソールからMonitoringの通知チャンネルを作成し、Error Reportingの通知先として設定するのみで完了します。

docs.cloud.google.com

具体的にどう実装したか

Error Reportingがエラーログを認識するために必要なのは、「スタックトレースを含むエラーログを、Cloud Loggingが解釈できる形式でstdoutに出力する」ことです。

私たちのバックエンドでは、ロギングライブラリに pino を採用しています。GCP向けの整形には @google-cloud/pino-logging-gcp-config を利用しており、ライブラリがError Reportingに対応したログ形式へ自動変換してくれます。

想定外のエラーが発生した際に、スタックトレース付きでログを出力するようにしておきます。コードのイメージは以下のような形です。

try {
  const response = await this.httpClient.request(/* ... */)
  return ok(response)
} catch (error) {
  this.logger.error({ err: error instanceof Error ? error : new Error(String(error)) })
  return err(/* ... */)
}

pinoのlogger.error の引数に err フィールドとして Errorオブジェクトを渡すことで、pino はこれを検出して stack を自動的にシリアライズしてくれます。これがError Reportingのグルーピング対象となります。

防御的プログラミングの一環として「本来この分岐は通らないはずだが、念のためハンドリングしている」という箇所にもスタックトレースを仕込んでおくのも効果的です。

例えば、審査の状態遷移の中で「ここには来ないはず」だけど念のため防御を入れておきたい分岐などです。

// 正常な遷移であれば、この時点で前段の確認は完了しているはず
if (!screening.isPreviousCheckCompleted()) {
  this.logger.error({
    err: new Error(`審査ステータスに異常が発生しています`),
  })
  return err(/* ... */)
}

このような箇所は普段の運用ではほぼ発火しませんが、もし通知が飛んできた場合は、開発時に想定できていなかったエッジケースを踏んでいる可能性が高いです。Error Reportingに繋いでおくことで、そうしたエッジケースを早期に検知し潜在的なバグの芽を本番で大ごとになる前に潰せるようになります。

実際に運用してみて感じたこと

導入してから一定期間運用してみて印象に残っていることをいくつか紹介します。

設定が圧倒的に簡単

導入のために必要なのは「stdoutにスタックトレースを含むエラーログをJSONで出力すること」だけです。

私たちのチームでは、ある程度開発が進んだ後でアラート設定を行ったのですが、アプリ側の対応はほとんど必要ありませんでした GCP側で通知先を設定するだけで、実運用に耐える異常検知の仕組みが構築できました。

検証環境にも気軽に入れられる

コスト面の負担がほぼないため、本番環境だけでなく検証環境にも躊躇なくError Reportingを有効化できました。検証環境はちょっとした実装ミスや異常系のテストでエラーが出やすい場所ですが、「とりあえず入れておく」ができるのは想像以上にありがたいです。

「念のため」のコードも安心して仕込める

前述した防御的プログラミングのように「発生するか分からないけれど、万一発生したらすぐに気づきたいと思う箇所にエラーログを仕込んでおけば大丈夫」という安心感は大きいと感じています。

AIエージェントによるノイズには注意

これは運用してみて気づいた地味な落とし穴でした。最近はClaude CodeなどのAIエージェントに実装を任せる機会が増えています。AIは周囲のコードを真似て書く傾向があるため、スタックトレースの出力箇所が既存コードに多いと、本来必要のない箇所にまでスタックトレースを仕込まれてしまうことがありました。Error Reportingは「スタックトレース付きで吐かれたエラー」を区別なく拾うため、こうしたノイズもそのまま通知に乗ってきてしまいます。コードレビューの時などに「このログ・スタックトレースは本当に必要か?」を人間が意識する良いきっかけになりました。

まとめ

本記事では、私たちが法人審査基盤の運用で取り入れている、GCP Error Reportingを使った異常検知の仕組みについて紹介しました。

Error ReportingはフルセットのAPMと比べると機能は限られていますが、設定の手軽さやコストの低さから、「まず最低限の異常検知の仕組みを入れたい」というフェーズには非常にフィットする選択肢だと感じています。スタックトレース付きでエラーを吐くだけで動作するため、防御的プログラミングや「念のため」のハンドリングと組み合わせることで、本番で問題になる前にエッジケースに気づける機会を着実に増やしてくれます。

本記事で紹介した方法は私たちのチームでの一例ですが、「異常検知の仕組みをまだ整えられていない」「いきなりAPMを入れるのはオーバーかもしれない」というチームにとっては、最初の一歩として現実的な選択肢になるのではないかと思います。

最後まで読んでいただきありがとうございました。

法人カード基盤の外部提供の挑戦 技術ブログ バトンリレー

各エンジニア組織が法人カード基盤の外部提供の挑戦の中で得た知見を順次公開していきます!技術、開発設計や思想、Tipsなど、幅広くお伝えします。

▼公開予定表 (各記事公開後、リンク集になります)

タイトル チーム 執筆者
UPSIDERのカード基盤を「外部提供できる形」にするまで——マイクロサービス化とマルチブランドアーキテクチャ 法人カード基盤の外部提供 全体構想 Mitsui
Spanner Change Streams を自前で読む ― 決済データを欠損なく取り込む カード管理基盤チーム(Solaris) Ohsako
1年動き続けるワークフローを壊さずにデプロイする ― Temporal Worker Versioning 導入記 請求基盤チーム(Hermes) nishiwaki
GCP Error Reportingで「最低限の異常検知」を低コストに実現する 法人審査基盤チーム(Hestia) Mitomi
複数の銀行データソースを1つに束ねる ― マルチブランド法人カードを支える銀行連携基盤 銀行連携基盤 terry
仮説駆動で取り組む負荷試験計画 processor Kinsho
AIが実装しレビューする時代に、プロダクトエンジニアは何をするのか お客様向け管理画面チーム(Iris) Akari
法人カード基盤の外部提供にも耐えうる不正利用対策基盤の構築 Anti-fraudチーム Ryutaro
法人カード基盤の外部提供という挑戦 決済基盤を「他社にまるごと使ってもらう」QAの話 QAチーム Naoki

なぜUPSIDERが法人カード基盤の外部提供に取り組むのか、その背景を綴っている記事も合わせてぜひご覧ください。

note.com

We Are Hiring!!

UPSIDERでは現在積極採用をしています。 ぜひお気軽にご応募ください。 herp.careers

herp.careers

UPSIDER Engineering Deckはこちら📣

speakerdeck.com