1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

AIはコード生成だけじゃない ― Cloud Run APIの可視化ダッシュボードを対話だけで構築

Posted at

tacomsエンジニアの高橋です。 tacoms Advent Calendar 2025の12日目になります。

AIコーディングツールの進化によって、開発作業の多くをAIに任せる機会が増えてきました。

ただ、「AIコーディングツール」という名前のせいか、つい「コードを書かせるもの」と思いがちです。

実際に使い込んでみると、コード生成以外にも活躍する場面が多いことに気づきます。

  • CLIコマンドの組み立てと実行
  • 複雑なJSON設定ファイルの生成
  • エラーメッセージの調査と修正案の提示
  • ドキュメントを読み解いての最適なアプローチ提案

今回は、Cloud RunのAPIダッシュボードをAIとの対話だけで構築した事例を紹介します。コードは1行も書いていません。既存のログとGoogle Cloudの標準機能だけで、エンドポイント別の利用状況を可視化できました。

この記事でわかること

  • Cloud Runの標準アクセスログとLog Analyticsを使ったAPIメトリクスの可視化方法
  • AIとの対話で複雑なJSON/SQLを効率的に作成するコツ
  • 追加実装・追加コストなしでCloud Runの利用状況を把握する方法

想定読者

  • Cloud Runを使っていて、APIの利用状況を可視化したい人
  • AIツール(Claude Code等)を使った開発効率化に興味がある人
  • 追加コストをかけずにObservabilityを改善したいチーム

1. 背景:なぜダッシュボードが必要だったのか

私たちのチームでは、Cloud Run上でAPIを運用していますが、以下のような疑問に答えられない状態でした。

  • どのエンドポイントがどれくらい使われているか?
  • レイテンシはどの程度か?
  • エラーはどこで発生しているか?

非機能要件(SLO/SLI)を検討するためのベースラインを取得したい。まずは現状を「可視化」することが第一歩でした。

最初の方向転換

当初は「カスタムメトリクスの導入」を検討していました。AIに相談したところ、58個もの計測ポイントを提案されました。

「コード追加なしで作成したい。すでに出力されているアクセスログを活用できないか?」

結果的にこれが正解でした。Cloud Runが自動で出力する標準アクセスログを活用することで、追加実装なしでダッシュボードが完成しました。


2. 完成したダッシュボード

Before:Cloud Runの標準メトリクス画面

Cloud Runのデフォルトメトリクスでは、パス情報が含まれていません。全体のリクエスト数やレイテンシは見えますが、「どのエンドポイントが遅いのか」といった詳細な分析はできませんでした。

After:今回作成したカスタムダッシュボード

作成したダッシュボードは以下の構成です。

全体像の把握

  • HTTPステータス分布(円グラフ)
  • HTTPメソッド別リクエスト数

トレンドの把握

  • リクエスト数/分の推移
  • 平均レイテンシ推移

詳細分析

  • エンドポイント別リクエスト数 Top 5
  • 遅いエンドポイント Top 10

フィルタ機能

  • brand_idpathmethod で絞り込み可能

3. なぜLog Analyticsを選んだのか

今回は既存インフラを最大限活用するアプローチを採用しました。

追加費用なしで始められる

Cloud Runを使っている時点で、ログは既にCloud Loggingに送られています。以下はすべて無料です。

  • Log Analyticsの有効化
  • ダッシュボード作成
  • クエリ実行(_Defaultバケットに限る)

注意点

項目 内容
保持期間 _Defaultバケットは30日。30日を超えるログは自動削除される
長期分析が必要な場合 保持期間延長(+$0.01/GiB/月)またはBigQueryエクスポートを検討

参考: Cloud Logging Pricing

選択理由のまとめ

  • まずは「可視化」が目的 → 高機能APMは過剰
  • ログは既にCloud Loggingに転送されている → 追加インフラ不要
  • SQLで柔軟にクエリできる → 欲しい情報を自由に抽出
  • デフォルトの保持期間でも直近のトレンド分析には十分

4. Cloud Run標準アクセスログの活用

Cloud Runは、リクエストごとに自動でアクセスログを出力しています。アプリケーション側での実装は一切不要です。

標準アクセスログの構造

Cloud Runの標準アクセスログは httpRequest フィールドに必要な情報がすべて含まれています。

{
  "httpRequest": {
    "requestMethod": "GET",
    "requestUrl": "https://example.com/users/123/orders",
    "status": 200,
    "latency": "0.068256546s",
    "userAgent": "Mozilla/5.0 ...",
    "remoteIp": "203.0.113.1",
    "requestSize": "1044",
    "responseSize": "34075",
    "referer": "https://example.com/"
  },
  "logName": "projects/{PROJECT_ID}/logs/run.googleapis.com%2Frequests",
  ...
}

利用可能なフィールド

フィールド 説明
requestMethod HTTPメソッド(GET, POST等)
requestUrl 完全なリクエストURL(パスを抽出可能)
status HTTPステータスコード
latency 処理時間(STRUCT型:seconds, nanos)
userAgent ユーザーエージェント
remoteIp クライアントIP
requestSize リクエストサイズ
responseSize レスポンスサイズ

注意: gcloud logging read では latency"0.068s" のような文字列として表示されますが、Log AnalyticsのSQLでは STRUCT<seconds INT64, nanos INT64> 型としてアクセスします。

ノイズの除外

実際のログを確認すると、意図しないトラフィックが混入していることが判明しました。

  • IPアドレス直接アクセス(セキュリティスキャナー等)
  • favicon.ico へのリクエスト
  • / へのルートアクセス(ヘルスチェック以外)

対策として、以下のフィルタをSQLクエリに適用しています。

  • requestUrl で正規のホスト名のみをフィルタ
  • 404ステータスはpath一覧から除外

本来は不正アクセス自体をブロックすべきですが、今回の目的は「正規のAPIエンドポイントの利用状況を可視化する」ことなので、クエリ側でノイズを除外するアプローチを採用しました。

Log Analyticsの有効化

# 初回のみ実行
gcloud logging buckets update _Default --location=global --enable-analytics

参考: Log Analytics を使用してログのクエリと分析を行う


5. AIとの対話でダッシュボード構築

ここからがメインパートです。

使用したツール

  • Claude Code(Anthropic公式CLI)
  • gcloud コマンド

対話の流れ

ステップ1:要件を伝える

👤 メニューマネジャーのAPIで、エンドポイントごとの利用状況を可視化したい。
   Cloud Runの既存メトリクスで作れない?

🤖 Cloud Run標準メトリクスにはパス情報がありません。
   ただし、Cloud Runの標準アクセスログを使えば、
   Log Analyticsでエンドポイント別の分析が可能です。

ステップ2:AIが調査・提案

AIは以下を自律的に実行しました。

  • Log Analyticsのテーブル構造を調査(gcloud logging read で確認)
  • 基本的なSQLクエリを提案
  • ダッシュボードJSONの雛形を生成

ステップ3:試行錯誤のサイクル

👤 このエラーが出た:
   No matching signature for function REGEXP_EXTRACT
   Argument types: STRUCT<seconds INT64, nanos INT64>, STRING

🤖 http_request.latency はSTRUCT型です。
   文字列パースではなく、構造体のフィールドにアクセスする必要があります。

   修正前: CAST(REGEXP_EXTRACT(http_request.latency, r'...') AS FLOAT64) * 1000
   修正後: (http_request.latency.seconds * 1000 + http_request.latency.nanos / 1000000)

エラー → 原因調査 → 修正 → 再実行のサイクルが高速に回りました。

人間は「こうしたい」「このエラーが出た」を伝えるだけ。AIが勝手に別の方法を試してくれるのが便利でした。

ステップ4:一括修正

👤 全ウィジェットにフィルタ条件を追加して

🤖 10個のウィジェットすべてに以下の条件を追加しました:
   AND IF(@path = '*', TRUE, ... = @path)
   AND IF(@method = '*', TRUE, ... = @method)

   gcloudで更新します...(実行)...完了しました。

ステップ5:gcloudコマンドの実行もAIに任せる

# ダッシュボード更新(etag必須)
ETAG=$(gcloud monitoring dashboards describe {DASHBOARD_ID} \
  --project={PROJECT_ID} --format='value(etag)')
jq --arg etag "$ETAG" '. + {etag: $etag}' dashboard.json > dashboard_final.json
gcloud monitoring dashboards update {DASHBOARD_ID} \
  --config-from-file=dashboard_final.json --project={PROJECT_ID}

Google Cloudコンソールを開かずにダッシュボードが完成。etagの取得・付与などの定型作業も自動で処理されました。

AIならではの利点

作業 手作業の場合 AIとの対話
10ウィジェット×3フィルタ条件の追加 1つずつSQL編集(30箇所) 「全ウィジェットに適用して」で一括
巨大なJSON(数百行)の生成 手書きは非現実的 テンプレートを理解して自動生成

6. Log Analyticsの基本と注意点

ログテーブル

デフォルトバケットを使用している場合、テーブル名は以下の形式です。

{PROJECT_ID}.global._Default._Default

カスタムバケットを使用している場合はバケット名が変わります。

ハマりポイント:JSON型とSTRUCT型の違い

Log Analyticsのテーブルでは、カラムによって型が異なります。

SELECT
  timestamp,
  http_request.request_method AS method,
  REGEXP_REPLACE(
    REGEXP_EXTRACT(http_request.request_url, r'https?://[^/]+(/[^?]*)'),
    r'/\d+', '/:id'
  ) AS generalized_path,
  http_request.status AS status,
  (COALESCE(http_request.latency.seconds, 0) * 1000
   + COALESCE(http_request.latency.nanos, 0) / 1000000) AS latency_ms  -- ※1
FROM
  `{PROJECT_ID}.global._Default._Default`
WHERE
  log_name = 'projects/{PROJECT_ID}/logs/run.googleapis.com%2Frequests'
  AND resource.type = 'cloud_run_revision'
  AND JSON_VALUE(resource.labels, '$.service_name') = '{SERVICE_NAME}'
  • http_request.*(STRUCT型)→ 直接アクセス可能
  • resource.labels(JSON型)→ JSON_VALUE() で抽出が必要

この違いを理解していないと、型エラーで悩むことになります。

※1: COALESCEが必要な理由はセクション8.2で解説しています。

ポイント:

  • 標準アクセスログは log_namerun.googleapis.com%2Frequests を指定して絞り込み
  • http_request はSTRUCT型なのでドット記法で直接アクセス可能
  • パスは request_url から正規表現で抽出

7. フィルタで深掘り(Dashboard Variables)

フィルタの種類

動的リスト

  • クエリ結果から値を取得
  • 例:brand_idpath

固定リスト

  • 事前定義の値
  • 例:method(GET/POST/PUT/PATCH/DELETE)

フィルタ定義(JSON)

{
  "dashboardFilters": [
    {
      "filterType": "VALUE_ONLY",
      "templateVariable": "path",
      "valueType": "STRING",
      "timeSeriesQuery": {
        "opsAnalyticsQuery": {
          "sql": "SELECT DISTINCT REGEXP_REPLACE(...) AS path FROM ... ORDER BY path"
        }
      }
    },
    {
      "filterType": "VALUE_ONLY",
      "templateVariable": "method",
      "valueType": "STRING",
      "stringArray": {
        "values": ["GET", "POST", "PUT", "PATCH", "DELETE"]
      }
    }
  ]
}

フィルタ条件の適用(SQL)

ワイルドカード(*)対応のフィルタ条件です。

AND IF(@path = '*', TRUE,
    REGEXP_REPLACE(
      REGEXP_EXTRACT(http_request.request_url, r'https?://[^/]+(/[^?]*)'),
      r'/[0-9]+', '/:id'
    ) = @path)
AND IF(@method = '*', TRUE,
    http_request.request_method = @method)

8. Tips

8.1 createではなくupdateを使う

今回は gcloud monitoring dashboards create を使わず、先にGoogle Cloudコンソールで空のダッシュボードを作成し、その後 update で更新する方法を採用しました。

👤 とりあえず箱を作ってみた(Google Cloudコンソールでダッシュボード作成済み)
   https://console.cloud.google.com/monitoring/dashboards/builder/{DASHBOARD_ID}...

🤖 既存のダッシュボードIDを使ってupdateで更新していきます。

この方法のメリット

  • 他のダッシュボードに影響を与えない(createの誤実行で意図しないダッシュボードが増えない)
  • ダッシュボードIDが固定されるため、URLをブックマークしておける
  • 試行錯誤中に何度更新しても安全
# 既存ダッシュボードの更新(推奨)
ETAG=$(gcloud monitoring dashboards describe {DASHBOARD_ID} \
  --project={PROJECT_ID} --format='value(etag)')
jq --arg etag "$ETAG" '. + {etag: $etag}' dashboard.json > dashboard_final.json
gcloud monitoring dashboards update {DASHBOARD_ID} \
  --config-from-file=dashboard_final.json --project={PROJECT_ID}

8.2 レイテンシのSTRUCT型への対応

gcloud logging read で確認すると latency"0.068s" のような文字列に見えますが、Log AnalyticsのSQLでは STRUCT<seconds INT64, nanos INT64> 型として扱われます。

-- ミリ秒に変換(COALESCEでnull安全に)
(COALESCE(http_request.latency.seconds, 0) * 1000
 + COALESCE(http_request.latency.nanos, 0) / 1000000) AS latency_ms

重要: COALESCEでnullを0に置き換えることで、計算結果がNULLになるのを防ぎます。

この違いに気づかずに文字列パースを試みると、以下のエラーが発生します:

No matching signature for function REGEXP_EXTRACT
Argument types: STRUCT<seconds INT64, nanos INT64>, STRING

ハマりポイント: CLIでは文字列に見えるため、ついREGEXP_EXTRACTで文字列パースしようとしますが、Log AnalyticsではSTRUCT型なので.seconds.nanosでアクセスする必要があります。

8.3 etagの取り扱い

  • 更新時は最新のetagを取得してJSONに含める
  • 古いetagだと ABORTED: The supplied etag is not up to date エラー
  • Google Cloudコンソールで編集した場合もetagは更新されるので注意
  • AIに任せておけば、エラー時に自動で最新etagを取得してリトライしてくれる

8.4 パスの正規化

標準アクセスログでは request_url にフルURLが含まれているため、まずパス部分を抽出し、次に数値IDを置換します。

REGEXP_REPLACE(
  REGEXP_EXTRACT(http_request.request_url, r'https?://[^/]+(/[^?]*)'),
  r'/\d+', '/:id'
)

例:https://api.example.com/users/123/orders/456?limit=10/users/:id/orders/:id


9. まとめ

今回、Cloud Run上で動くAPIの利用状況ダッシュボードを、AIとの対話だけで構築しました。

アプリケーション側でのログ実装は一切不要でした。Cloud Runが自動出力する標準アクセスログ(run.googleapis.com/requests)には、エンドポイント別の利用状況を可視化するのに必要な情報が含まれていました。

振り返ってみると、AIが特に力を発揮したのは以下の場面でした。

  • 数百行のJSON設定ファイルの生成と修正 ― 手作業では非現実的
  • gcloudコマンドの組み立てと実行 ― etagの取得・付与などの定型作業を自動処理
  • エラー発生時の原因調査とリトライ ― 人間は結果を確認するだけ

「AIコーディングツール」という名前から、つい「コードを書かせるもの」と思いがちです。しかし実際には、CLIの操作、設定ファイルの生成、ドキュメントの読み解きなど、開発周辺の面倒な作業こそAIの得意分野かもしれません。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?