Newer
Older
client_dwatch / lib / logger / api.ts
/**
 * APIルートの catch 節から呼ぶロギングヘルパー。
 *
 * クライアントへ返す `ApiError` を**そのまま**受け取るのが要点。
 * ログ側で code / message を組み立て直すとレスポンスとズレるため、
 * 同一オブジェクトから読む形にして構造的に防ぐ(docs/logging-design.md §3)。
 * 生の例外メッセージは `error_message` に入らない(定型文のみ)。
 */
import type pino from "pino";
import { emitTo, logger } from "@/lib/logger";
import type { ApiError } from "@/lib/types";

export interface ApiErrorLogParams {
  /** 実際のパス。`/api/v1/...` のような架空のパスを書かない */
  route: string;
  method: string;
  /** クライアントへ返すものと同一の body */
  body: ApiError;
  status: number;
  /** 例外そのもの。pino の err シリアライザに渡す */
  err: unknown;
}

/**
 * 出力先を差し替えられる版。
 * 全APIルートのエラー経路が通る関数なので、実出力をテストで検証できる接合部を用意する
 * (`emitTo` / `logEvent` と同じ形)。設定を二重に書かないため target を渡すだけにする。
 */
export function logApiErrorTo(
  target: pino.Logger,
  params: ApiErrorLogParams
): void {
  emitTo(
    target,
    "error",
    {
      event: "api.error",
      route: params.route,
      method: params.method,
      error_code: params.body.error.code,
      error_message: params.body.error.message,
      http_status: params.status,
    },
    params.err
  );
}

export function logApiError(params: ApiErrorLogParams): void {
  logApiErrorTo(logger, params);
}