api-logging-guidelines について
APIルートのログ記録に関するガイドラインです。ログレベル、イベントの文脈、プライバシー、パフォーマンスを扱い、重複や定型ログを減らしつつ有用な記録を保つ方法を示します。
主な機能
- 01ログレベル選択の指針
- 02重複するリクエストログの回避
- 03エラーとビジネスイベントのパターン
- 04機密データの除外
- 05既存ログの移行チェックリスト
活用シーン
- 01リリース前にAPIルートのログを確認
- 02過剰または重複するログを整理
- 03エラーやセキュリティイベントに文脈を追加
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ inkeep/ agents --skill api-logging-guidelinesインストール後、Claude Code で/api-logging-guidelinesを入力して読み込まれたか確認します。
詳細
API ルートのログ記録ガイドライン
API ルートでログを適切に使用し、クリーンで有用かつパフォーマンスに優れたログを維持するための包括的なガイダンスです。
基本原則
1. 重複したログ記録を避ける
ミドルウェアですでに記録されている内容はログに記録しないでください:
// ❌ BAD - Request details are already logged by middleware
logger.info({ tenantId, projectId }, 'Getting project details');
リクエストミドルウェアのログ記録を活用してください:
- リクエスト/レスポンスミドルウェアは、メソッド、パス、ステータス、所要時間、パスパラメーターをすでにログに記録します
- これらのログには、URL パスから取得したテナント ID/プロジェクト ID が含まれます
- ログを重複して追加すると、価値のないノイズが増えます
2. ログレベルのガイドライン
| レベル | 用途 | 例 |
|---|---|---|
| ERROR | 対応が必要な予期しない障害 | データベース接続の失敗、未処理の例外、重大なサービスエラー |
| WARN | 回復可能な問題や懸念されるパターン | レート制限の発動、非推奨 API の使用、フォールバック動作の有効化 |
| INFO | 重要なビジネスイベント(通常の操作は含みません) | ユーザーアカウントの作成、決済の処理、重要な設定の変更 |
| DEBUG | 詳細な診断情報 | クエリパラメーター、中間計算、キャッシュのヒット/ミスの詳細 |
3. ログに記録する内容
次の重要なイベントをログに記録してください:
// ✅ GOOD - Important business event
logger.info({
userId,
oldPlan: 'free',
newPlan: 'pro',
mrr: 99
}, 'User upgraded subscription');
// ✅ GOOD - Error with context
logger.error({
error,
tenantId,
webhookUrl,
attemptNumber: 3
}, 'Webhook delivery failed after retries');
// ✅ GOOD - Security-relevant event
logger.warn({
ip: c.req.header('x-forwarded-for'),
userId,
attemptedResource
}, 'Unauthorized access attempt');
// ✅ GOOD - Performance issue
logger.warn({
duration: 5234,
query,
resultCount: 10000
}, 'Slow query detected');
4. ログに記録しない内容
通常の操作のログ記録は避けてください:
// ❌ BAD - Routine CRUD operation
logger.info('Getting user by ID');
// ❌ BAD - Already logged by middleware
logger.info(`Processing GET request to /api/users/${id}`);
// ❌ BAD - No actionable information
logger.info('Starting database query');
// ❌ BAD - Sensitive information
logger.info({ password, apiKey }, 'User login attempt');
// ❌ BAD - Overly granular
logger.debug('Entering function processUser');
logger.debug('Exiting function processUser');
API ルートのパターン
パターン 1:エラー処理
// ✅ GOOD - Log errors with context
export const route = router.get('/:id', async (c) => {
try {
const result = await riskyOperation();
return c.json(result);
} catch (error) {
// Log error with relevant context
logger.error({
error,
userId: c.get('userId'),
operation: 'riskyOperation',
// Include any relevant debugging context
requestId: c.get('requestId')
}, 'Operation failed');
// Return generic error to client (don't leak internals)
return c.json({ error: 'Internal server error' }, 500);
}
});
パターン 2:ビジネスイベント
// ✅ GOOD - Log significant business events
export const route = router.post('/subscription/upgrade', async (c) => {
const { planId } = await c.req.json();
const result = await upgradeSubscription(userId, planId);
// This is worth logging - it's a significant business event
logger.info({
userId,
oldPlan: result.previousPlan,
newPlan: result.newPlan,
mrr: result.mrr,
timestamp: new Date().toISOString()
}, 'Subscription upgraded');
return c.json(result);
});
パターン 3:パフォーマンスの監視
// ✅ GOOD - Log performance issues
export const route = router.get('/search', async (c) => {
const start = Date.now();
const results = await performSearch(query);
const duration = Date.now() - start;
// Only log if performance is concerning
if (duration > 1000) {
logger.warn({
duration,
query,
resultCount: results.length,
cached: false
}, 'Slow search query');
}
return c.json(results);
});
パターン 4:セキュリティイベント
// ✅ GOOD - Log security-relevant events
export const route = router.post('/api/admin/*', async (c) => {
const hasPermission = await checkPermission(userId, resource);
if (!hasPermission) {
// Log unauthorized access attempts
logger.warn({
userId,
resource,
ip: c.req.header('x-forwarded-for'),
userAgent: c.req.header('user-agent')
}, 'Unauthorized access attempt');
return c.json({ error: 'Forbidden' }, 403);
}
// Proceed with authorized request...
});
環境別のガイドライン
開発
// More verbose logging acceptable in development
if (process.env.NODE_ENV === 'development') {
logger.debug({ params, body }, 'Request details');
}
本番環境
- INFO レベルのログは重要なイベントのみに絞ります
- 機密データ(パスワード、トークン、キー、個人を特定できる情報)は決してログに記録しません
- 検索しやすくするために構造化ログを使用します
- リクエストを追跡できるように相関 ID を含めます
移行戦略
既存の冗長なログ記録をリファクタリングする際は、次の手順に従います。
- 重複したログを特定する:ミドルウェアのログと重複するログを削除します
- 通常の操作のログレベルを下げる:通常の操作のログを INFO から DEBUG に変更します
- エラーログを強化する:エラーログにより多くのコンテキストを追加します
- ビジネスイベントのログを追加する:重要なビジネスイベントが確実に記録されるようにします
- ログレベルを確認する:各ログに適切なレベルが使用されていることを確認します
変更前:
router.get('/:id', async (c) => {
const { id } = c.req.param();
logger.info({ id }, 'Getting item by ID'); // Redundant
const item = await getItem(id);
logger.info({ item }, 'Retrieved item'); // Too verbose
return c.json(item);
});
変更後:
router.get('/:id', async (c) => {
const { id } = c.req.param();
try {
const item = await getItem(id);
// No logging needed - routine successful operation
return c.json(item);
} catch (error) {
// Only log errors
logger.error({ error, id }, 'Failed to retrieve item');
return c.json({ error: 'Item not found' }, 404);
}
});
冗長なログを使わないデバッグ
冗長なログ記録の代わりに、次の方法を使用します。
- デバッグモードを必要な場合にのみ使用する:トラブルシューティングの際に、特定のモジュールで DEBUG レベルを有効にします
- トレーシングを使用する:分散トレーシングには OpenTelemetry/Jaeger を使用します
- メトリクスを使用する:パフォーマンスメトリクスには Prometheus/StatsD を使用します
- エラー追跡を使用する:エラーの集約には Sentry/Rollbar を使用します
- フィーチャーフラグを使用する:デバッグ時に、特定のユーザー/リクエストについて冗長なログ記録を有効にします
まとめのチェックリスト
ログの記録を追加する前に、次の点を確認します。
- これはミドルウェアですでに記録されていますか?(メソッド、パス、ステータス、パラメーター)
- これは重要なビジネスイベントですか、それとも単なる通常の操作ですか?
- このログは、実際に対応できる情報を提供しますか?
- 適切なログレベルを使用していますか?
- 機密データを含めずに、有用なコンテキストを含めていますか?
- このログは本番環境で役立ちますか、それともノイズを増やすだけですか?
覚えておいてください:優れたログ記録とは、量ではなくシグナルが重要です。すべてのログには目的があるべきです。
コメント
Markdown 対応