ログ運用仕様

Parky 全ランタイム(Astro SSG / Vite React SPA / Cloudflare Workers / Flutter)に わたるログ出力の共通スキーマと運用ルール

各ランタイムは異なるロガー実装を使ってよい。ただし出力する JSON イベントは本文書のスキーマに従う。 これにより将来どのログ集約基盤に送っても串刺し検索ができる。


1. 共通スキーマ

すべてのロガーは次の JSON を 1 イベント = 1 行で stdout に出す:

{
  "level":   "debug|info|warn|error",
  "time":    "2026-04-17T10:00:00.123Z",
  "service": "admin|home|api|mobile",
  "env":     "dev|staging|prod",
  "msg":     "人が読める短い説明",
  "scope":   { "userId": "...", "requestId": "..." },
  "error":   { "name": "...", "message": "...", "stack": "..." },
  "extra":   { "任意のコンテキスト": "..." }
}

必須フィールド

キー 意味
level ログレベル(下記 § 2)
time ISO 8601 UTC
service どのランタイムから出たか
env 環境(import.meta.env.MODE 等から)
msg 1 行の説明文(日本語OK)

任意フィールド

キー 意味
scope リクエスト/セッション全体を通したコンテキスト(user_id / trace_id 等)
error Error オブジェクトのシリアライズ(error レベル時は推奨)
extra その場限りの追加情報

禁止: PII(メール・氏名・電話)をそのまま出力しない。必要なら user_id のみ。


2. ログレベル

level 用途 本番での扱い
debug 開発時の詳細追跡 出力しない
info 重要な状態遷移(セッション開始、ジョブ完了) 出力
warn 想定内の異常(retry、フォールバック発動) 出力
error 想定外のエラー(Sentry へも自動転送) 出力 + Sentry

3. 業務監査ログは分離する

ロガー基盤と業務監査は別物:

種類 保存先 目的
アプリログ(本ドキュメント) stdout → 集約基盤 運用・障害対応
ユーザー行動ログ user_activity_logs テーブル 分析・バッジ付与
管理者操作ログ admin_activity_logs テーブル 監査・変更追跡

ロガーでアプリログを出す際に、業務監査テーブルにも書いてしまわないこと。 業務監査は専用 API(logAdminActivity() 等)経由で明示的に行う。


4. ランタイム別の実装指針

4.1 admin / home(TypeScript ブラウザ + Astro SSR)

  • @parky/logger パッケージを使う(web/packages/logger/
  • ブラウザでは console に出力 + error は Sentry に自動転送
  • Astro SSR(Node)も同じパッケージで動作
  • 使用例:
    import { createLogger } from '@parky/logger';
    const log = createLogger({ service: 'admin', env: import.meta.env.MODE });
    log.info('ユーザー更新完了', { extra: { user_id } });
    log.error(err, { scope: { action: 'user.update_status' } });
    

4.2 Cloudflare Workers BFF(Hono)

  • api/src/lib/logger.ts に薄いラッパーを置いて console.log(JSON.stringify({...})) で stdout 出力
  • スキーマは本ドキュメントに準拠
  • wrangler.toml[observability] enabled = true を入れてあるので、Cloudflare Dashboard の Workers Observability から構造化ログとして検索可能
  • Logpush を繋いで外部基盤(Datadog / Logtail 等)に流す設計も可能(未配線)

4.3 Flutter(Dart)

  • logger パッケージ(Dart)を使用
  • 本番クラッシュは Firebase Crashlytics に送る
  • JSON 出力は dev では不要、本番でログ集約基盤にパイプするタイミングで検討

5. 転送先(バックエンド)

現時点:

  • Sentry: @sentry/react 導入済み(DSN 未設定時は素通り)
  • stdout: ブラウザコンソール / Cloudflare Pages 実行ログ / Cloudflare Workers Observability

将来(別タスク):

  • ログ集約: Datadog / Logtail / Grafana Loki 等を 1 つ選定し Cloudflare Logpush で流す
  • trace_id を跨がせて OpenTelemetry 対応

6. 疎結合の原則

@parky/logger のコア実装は転送先(Sentry / CloudWatch 等)を知らない。 転送は Transport 関数として差し替え可能:

const log = createLogger({
  service: 'admin',
  env: 'prod',
  transports: [consoleTransport(), sentryTransport()],  // 後付けで追加
});

これにより:

  • コアは依存ゼロで変更不要
  • 転送先の切替はアプリ起動時の設定のみ
  • テスト時は transports を空にできる

7. 命名規則(参考)

msg は短く動作・結果を書く:

good:  "駐車セッション作成失敗"
good:  "role.update_permissions 成功"
bad:   "エラーが発生しました"
bad:   "The user (id=abc) tried to update ..." ← 詳細は extra/scope へ

scope.action はドット区切りの resource.verb を推奨: user.update_status / role.update_permissions / vehicle.soft_delete

↗ Source markdown (logging.md)