Advanced

Designing Logging and Monitoring Infrastructure: Claude Code 활용 가이드

designing logging and monitoring infrastructure: Claude Code 활용. 실용적인 코드 예시를 포함합니다.

모니터링基盤の구축にClaude Codeを활용する

프로덕션 환경のトラブルシューティングには、적절한로그と모니터링が필수적입니다。Claude Code는 構造化로그、分散トレーシング、メトリクス収集の基盤を一括で구축でき、可観測性(Observability)の高いシステムを효율적으로実現します。

構造化로그の구현

> pino를 사용한構造化로그のセットアップ를 생성해줘。
> 요청ID、사용자ID、処理시간を含めて。
> 環境ごとに로그レベルを전환られるようにして。
// src/lib/logger.ts
import pino from 'pino';

export const logger = pino({
  level: process.env.LOG_LEVEL || (process.env.NODE_ENV === 'production' ? 'info' : 'debug'),
  transport: process.env.NODE_ENV !== 'production'
    ? { target: 'pino-pretty', options: { colorize: true } }
    : undefined,
  formatters: {
    level(label) {
      return { level: label };
    },
  },
  base: {
    service: process.env.SERVICE_NAME || 'my-app',
    env: process.env.NODE_ENV,
  },
  timestamp: pino.stdTimeFunctions.isoTime,
  redact: ['password', 'token', 'authorization', 'cookie'],
});

// 요청スコープのロガー
export function createRequestLogger(requestId: string, userId?: string) {
  return logger.child({
    requestId,
    userId,
  });
}

요청로그미들웨어

// src/middleware/request-logger.ts
import { Request, Response, NextFunction } from 'express';
import { createRequestLogger } from '@/lib/logger';
import { randomUUID } from 'crypto';

export function requestLogger(req: Request, res: Response, next: NextFunction) {
  const requestId = (req.headers['x-request-id'] as string) || randomUUID();
  const start = Date.now();

  const log = createRequestLogger(requestId, req.user?.id);
  req.log = log;

  res.setHeader('X-Request-Id', requestId);

  log.info({
    method: req.method,
    url: req.url,
    userAgent: req.headers['user-agent'],
  }, 'Request started');

  res.on('finish', () => {
    const duration = Date.now() - start;
    const level = res.statusCode >= 500 ? 'error' : res.statusCode >= 400 ? 'warn' : 'info';

    log[level]({
      method: req.method,
      url: req.url,
      statusCode: res.statusCode,
      duration,
    }, 'Request completed');
  });

  next();
}

OpenTelemetry에 의한分散トレーシング

> OpenTelemetry를 사용한分散トレーシングを설정して。
> HTTP、DB、Redis呼び出しの自動計装を유효にして。
// src/instrumentation.ts
import { NodeSDK } from '@opentelemetry/sdk-node';
import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http';
import { OTLPMetricExporter } from '@opentelemetry/exporter-metrics-otlp-http';
import { PeriodicExportingMetricReader } from '@opentelemetry/sdk-metrics';
import { getNodeAutoInstrumentations } from '@opentelemetry/auto-instrumentations-node';
import { Resource } from '@opentelemetry/resources';
import { ATTR_SERVICE_NAME, ATTR_SERVICE_VERSION } from '@opentelemetry/semantic-conventions';

const sdk = new NodeSDK({
  resource: new Resource({
    [ATTR_SERVICE_NAME]: process.env.SERVICE_NAME || 'my-app',
    [ATTR_SERVICE_VERSION]: process.env.APP_VERSION || '1.0.0',
  }),
  traceExporter: new OTLPTraceExporter({
    url: process.env.OTEL_EXPORTER_OTLP_ENDPOINT || 'http://localhost:4318/v1/traces',
  }),
  metricReader: new PeriodicExportingMetricReader({
    exporter: new OTLPMetricExporter({
      url: process.env.OTEL_EXPORTER_OTLP_ENDPOINT || 'http://localhost:4318/v1/metrics',
    }),
    exportIntervalMillis: 30000,
  }),
  instrumentations: [
    getNodeAutoInstrumentations({
      '@opentelemetry/instrumentation-http': { enabled: true },
      '@opentelemetry/instrumentation-express': { enabled: true },
      '@opentelemetry/instrumentation-pg': { enabled: true },
      '@opentelemetry/instrumentation-redis': { enabled: true },
    }),
  ],
});

sdk.start();

process.on('SIGTERM', () => {
  sdk.shutdown().then(() => process.exit(0));
});

カスタムメトリクスの収集

// src/lib/metrics.ts
import { metrics } from '@opentelemetry/api';

const meter = metrics.getMeter('my-app');

// 요청カウンター
export const requestCounter = meter.createCounter('http_requests_total', {
  description: 'Total HTTP requests',
});

// 응답タイムのヒストグラム
export const responseTime = meter.createHistogram('http_response_time_ms', {
  description: 'HTTP response time in milliseconds',
  unit: 'ms',
});

// アクティブ사용자数のゲージ
export const activeUsers = meter.createUpDownCounter('active_users', {
  description: 'Number of active users',
});

// ビジネスメトリクス
export const orderCounter = meter.createCounter('orders_total', {
  description: 'Total orders placed',
});

// Usage example
export function recordRequest(method: string, path: string, statusCode: number, duration: number) {
  requestCounter.add(1, { method, path, statusCode: String(statusCode) });
  responseTime.record(duration, { method, path });
}

ヘルスチェック엔드포인트

// src/app/api/health/route.ts
import { NextResponse } from 'next/server';
import { prisma } from '@/lib/db';
import Redis from 'ioredis';

const redis = new Redis(process.env.REDIS_URL!);

export async function GET() {
  const checks = {
    status: 'ok' as 'ok' | 'degraded' | 'error',
    timestamp: new Date().toISOString(),
    uptime: process.uptime(),
    checks: {} as Record<string, { status: string; latency?: number }>,
  };

  // DB接続チェック
  try {
    const start = Date.now();
    await prisma.$queryRaw`SELECT 1`;
    checks.checks.database = { status: 'ok', latency: Date.now() - start };
  } catch {
    checks.checks.database = { status: 'error' };
    checks.status = 'degraded';
  }

  // Redis接続チェック
  try {
    const start = Date.now();
    await redis.ping();
    checks.checks.redis = { status: 'ok', latency: Date.now() - start };
  } catch {
    checks.checks.redis = { status: 'error' };
    checks.status = 'degraded';
  }

  const statusCode = checks.status === 'ok' ? 200 : 503;
  return NextResponse.json(checks, { status: statusCode });
}

에러トラッキング

// src/lib/error-tracker.ts
import { logger } from './logger';

export function trackError(error: Error, context?: Record<string, unknown>) {
  logger.error({
    err: {
      name: error.name,
      message: error.message,
      stack: error.stack,
    },
    ...context,
  }, 'Unhandled error');

  // Sentryなどの外部サービスへ전송
  if (process.env.SENTRY_DSN) {
    // Sentry.captureException(error, { extra: context });
  }
}

// グローバル에러핸들러ー
process.on('uncaughtException', (error) => {
  trackError(error, { type: 'uncaughtException' });
  process.exit(1);
});

process.on('unhandledRejection', (reason) => {
  trackError(reason instanceof Error ? reason : new Error(String(reason)), {
    type: 'unhandledRejection',
  });
});

정리

Claude Code를 활용하면 構造化로그、分散トレーシング、メトリクス収集、ヘルスチェックを含む모니터링基盤を효율적으로구축할 수 있습니다。本番운영に必要な可観測性を最初から組み込むことで、トラブルシューティングの시간を大幅に短縮할 수 있습니다。개발 환경の설정はCLAUDE.mdに記述しておくと、Claude Code가 一貫した구성を생성します。마이크로서비스での모니터링は마이크로서비스설계도 참고하세요.

Claude Code의 상세 정보는Anthropic공식 문서를 확인하세요.OpenTelemetry의 상세 정보는OpenTelemetry공식 사이트를 참고하세요.

#Claude Code #ログ #モニタリング #可観測性 #OpenTelemetry