Cloudflare Logpush セットアップ手順

Workers の構造化ログ (createLogger が出す JSON) を Cloudflare 外の永続ストレージに ストリーミングするための設定。Workers Observability Dashboard だけでは過去 7 日しか 保管されない ため、本番運用には Logpush が必須。

状況 (2026-04-27): R2 bucket + lifecycle (dev 30d / prod 90d) + Logpush job 配信済み。

推奨構成

環境 Destination 保持 用途
dev R2 (parky-logs-dev) 30 日 デバッグ・障害分析
prod R2 (parky-logs-prod) 90 日 インシデント・SLO 計測
prod BigQuery (任意) 365 日 長期分析・ダッシュボード

R2 を主、BigQuery は将来必要になったら追加 (parky の現フェーズでは R2 だけで十分)。

1. R2 bucket 作成

# dev
wrangler r2 bucket create parky-logs-dev

# prod
wrangler r2 bucket create parky-logs-prod

R2 の lifecycle rule で 30 / 90 日後 auto-delete を設定:

wrangler r2 bucket lifecycle set parky-logs-dev  --file infra/r2/lifecycle-logs-dev.json
wrangler r2 bucket lifecycle set parky-logs-prod --file infra/r2/lifecycle-logs-prod.json

JSON 定義は infra/r2/lifecycle-logs-dev.json (30 日) と lifecycle-logs-prod.json (90 日)。

2. Logpush job 作成 (Cloudflare API)

Workers の Logpush は Account 単位 の設定。R2 destination は ownership challenge を経て 作成する 2 ステップフロー。

ACCOUNT_ID=5d8f6201999f8965395396c4674cbe2d
CF_TOKEN=$(op item get fckmphwmq7pccoyg6ye3vf4f34 --vault "PJ|Parky" --fields credential --reveal)
ACCESS_KEY=$(op item get rhxd2jnzp5dqbetn7kiky6aala --vault "PJ|Parky" --fields access_key_id --reveal)
SECRET_KEY=$(op item get rhxd2jnzp5dqbetn7kiky6aala --vault "PJ|Parky" --fields secret_access_key --reveal)

# Step 1: ownership challenge — CF が R2 にチャレンジファイルを書き込む
DEST="r2://parky-logs-dev/dev/{DATE}?account-id=${ACCOUNT_ID}&access-key-id=${ACCESS_KEY}&secret-access-key=${SECRET_KEY}"
CHALLENGE_FILE=$(curl -sS -X POST -H "Authorization: Bearer ${CF_TOKEN}" \
  "https://api.cloudflare.com/client/v4/accounts/${ACCOUNT_ID}/logpush/ownership" \
  --data "{\"destination_conf\":\"${DEST}\"}" \
  | python3 -c "import sys,json; print(json.load(sys.stdin)['result']['filename'])")

# Step 2: チャレンジファイルから token を読む
npx wrangler r2 object get "parky-logs-dev/${CHALLENGE_FILE}" --file /tmp/ownership.txt
OWNERSHIP_TOKEN=$(cat /tmp/ownership.txt)

# Step 3: Job 作成 — kind は付けない (workers_trace_events は kind=edge と非対応)
curl -X POST "https://api.cloudflare.com/client/v4/accounts/${ACCOUNT_ID}/logpush/jobs" \
  -H "Authorization: Bearer ${CF_TOKEN}" -H "Content-Type: application/json" \
  --data "{
    \"name\": \"parky-workers-logs-dev\",
    \"destination_conf\": \"${DEST}\",
    \"dataset\": \"workers_trace_events\",
    \"filter\": \"{\\\"where\\\":{\\\"and\\\":[{\\\"key\\\":\\\"ScriptName\\\",\\\"operator\\\":\\\"startsWith\\\",\\\"value\\\":\\\"parky-\\\"},{\\\"key\\\":\\\"ScriptName\\\",\\\"operator\\\":\\\"contains\\\",\\\"value\\\":\\\"-dev\\\"}]}}\",
    \"output_options\": {
      \"field_names\": [\"ScriptName\",\"EventType\",\"Outcome\",\"Logs\",\"Exceptions\",\"DispatchNamespace\",\"Event\",\"EventTimestampMs\"],
      \"timestamp_format\": \"rfc3339\"
    },
    \"frequency\": \"high\",
    \"enabled\": true,
    \"ownership_challenge\": \"${OWNERSHIP_TOKEN}\"
  }"

R2 access key は既存の Parky R2 API Token (PJ|Parky vault, item id rhxd2jnzp5dqbetn7kiky6aala) が account-scoped で全 bucket 書込み可。新規 token 作成不要 (本セットアップで検証済み)。

filterScriptName contains -dev / -prod で dev/prod の Worker logs を分離 (例: parky-api-dev は dev job、parky-api-prod は prod job)。

3. ログのクエリ

R2 に NDJSON 形式で蓄積されるので、解析は以下のいずれか:

  • DuckDB ローカル: r2 cp s3://parky-logs-dev/dev/2026-04-26/ /tmp/ --recursiveduckdb -c "select * from read_ndjson_auto('/tmp/*.json.gz') where Outcome != 'ok'"
  • Cloudflare R2 SQL (公開待ち): R2 上で直接 SQL クエリ
  • BigQuery 連携: 後追いで Logpush job を増やす

クエリ例 (5xx を時系列で):

SELECT
  EventTimestampMs,
  Logs[0].Message AS log,
  Outcome,
  Event.RequestUrl AS url
FROM read_ndjson_auto('parky-logs-prod/prod/2026-04-26/*.json.gz')
WHERE Outcome != 'ok'
ORDER BY EventTimestampMs DESC
LIMIT 100

4. Tail Workers (リアルタイム転送)

リアルタイム性が必要な signal (例: 5xx burst の即時 Slack 通知) は Logpush では 遅すぎるため、別途 Tail Worker を作って wrangler.tomltail_consumers で 配線する。本書の対象外 (B12 の DLQ monitor と統合する別タスク)。

5. PII / GDPR 注意

  • RequestHeaders フィールドには Authorization / Cookie ヘッダが含まれうる。 Logpush の filter で除外する: "logpull_options": "fields=...&exclude=RequestHeaders.Authorization,RequestHeaders.Cookie"
  • structured log の error.message に email / phone が混入しないよう、 lib/logger.ts 側で redact する責務 (S7 / 別タスク)。

6. SLO / Alert 連携

Logpush の R2 dump を Workers Cron で 5 分毎に集計して analytics.error_reports に書き 戻す案がある (P1 / project_parky_log_aggregation)。実装は本書の対象外。

7. コスト目安

  • Logpush: $0.05 / 1M log lines (Workers Paid Plan に含まれる)
  • R2 storage: 30 日 × 1GB/day × $0.015/GB = $0.45/月
  • R2 egress: 0 (Cloudflare 内なら 0、外部 destination で発生)

関連

↗ Source markdown (logpush-setup.md)