はじめに
「エラーログがあるがどのリクエストから来たか追えない」——相関ID(Correlation ID)とAsyncLocalStorageでリクエストを追跡し、全ログを関連付ける設計をClaude Codeに生成させる。
CLAUDE.mdに設計ルールを書く
## 相関IDトレーシング設計ルール
- リクエスト受信時にULIDで相関IDを生成
- X-Correlation-Idヘッダーがあれば引き継ぐ
- AsyncLocalStorageで全ての非同期処理に自動伝播
- 全ログに requestId, correlationId, userId を自動付与
- 下流サービスへのHTTPリクエストにも転送
生成される実装(抜粋)
// AsyncLocalStorageでコンテキスト伝播
const storage = new AsyncLocalStorage<RequestContext>();
export const requestContext = {
get: () => storage.getStore(),
run: (ctx, fn) => storage.run(ctx, fn),
};
// Expressミドルウェア
export function requestContextMiddleware(serviceName) {
return (req, res, next) => {
const requestId = ulid();
const correlationId = req.headers['x-correlation-id'] ?? requestId;
res.setHeader('X-Correlation-Id', correlationId);
requestContext.run({ requestId, correlationId, startedAt: Date.now(), service: serviceName }, next);
};
}
// コンテキスト付きロガー(どこでも使える)
export const logger = {
info: (obj, msg?) => {
const ctx = requestContext.get();
const context = ctx ? { requestId: ctx.requestId, correlationId: ctx.correlationId } : {};
baseLogger.info({ ...context, ...obj }, msg);
},
};
// 下流サービスへの相関ID転送
export async function tracedFetch(url, options = {}) {
const ctx = requestContext.get();
const headers = new Headers(options.headers);
if (ctx) {
headers.set('X-Correlation-Id', ctx.correlationId);
headers.set('X-Upstream-Service', ctx.service);
}
const start = Date.now();
const response = await fetch(url, { ...options, headers });
logger.info({ method: 'GET', url, status: response.status, durationMs: Date.now() - start }, 'Outbound HTTP');
return response;
}
まとめ
- CLAUDE.md にULIDで相関ID生成・X-Correlation-Idヘッダー引き継ぎ・全ログへの自動付与・下流サービスへのヘッダー転送を明記
- AsyncLocalStorage でPromiseチェーン全体にコンテキストを自動伝播——どこからでも相関IDを取得可能
- tracedFetch で外部HTTP呼び出しに相関IDを自動付与——マイクロサービス間をまたいで同じcorrelationIdで追跡
- Prismaクエリ拡張 でスロークエリ(100ms超)を相関ID付きでログ——ボトルネックのリクエストを特定するのが容易
観測可能性のレビューは **Code Review Pack(¥980)* の /code-review で確認できます。*
みょうが (@myougatheaxo) — ウーパールーパーのVTuber。
Top comments (0)