---
id: wiki-2026-0508-logging-and-error-handling
title: Logging and Error Handling
category: 10_Wiki/Topics
status: verified
canonical_id: self
aliases: [Structured Logging, Observability Logging]
duplicate_of: none
source_trust_level: A
confidence_score: 0.9
verification_status: applied
tags: [observability, logging, error-handling]
raw_sources: []
last_reinforced: 2026-05-10
github_commit: pending
tech_stack:
  language: TypeScript/Go
  framework: OpenTelemetry/pino/zap
---

# Logging and Error Handling

## 매 한 줄
> **"매 logs 는 events, errors 는 values"**. 매 modern stack (2026) 의 structured JSON logging + correlation IDs + OpenTelemetry trace propagation 의 default. Errors 는 typed values (Result/Either) 의 throw 보다 explicit propagation 의 prefer.

## 매 핵심

### 매 Logging levels
- **TRACE**: extreme detail, dev only.
- **DEBUG**: variables, branch decisions.
- **INFO**: lifecycle events (request start/end, job success).
- **WARN**: degraded but recoverable (retry, fallback).
- **ERROR**: failed operation, attention needed.
- **FATAL**: process-terminating.

### 매 Structured logging
- 매 string concatenation 의 X — JSON object emit.
- 매 stable field names (`user_id`, `request_id`, `trace_id`).
- 매 PII redaction at serialization (never log passwords, tokens).
- 매 sampling (1% INFO in hot path) for cost control.

### 매 Error handling philosophies
- **Exceptions**: Java, Python, Ruby. Easy default, but invisible control flow.
- **Return values**: Go (`err`), Rust (`Result<T, E>`). Explicit, ugly.
- **Effect systems**: Effect-TS, ZIO. Typed effects, composable.
- **Panics**: Rust/Go for unrecoverable bugs.

### 매 응용
1. SRE postmortems (logs as evidence).
2. Distributed tracing (correlation across services).
3. Audit trails (compliance — SOC 2, GDPR).
4. Anomaly detection feeds.
5. Customer support debugging.

## 💻 패턴

### Structured logging (TypeScript with pino)
```typescript
import pino from 'pino';

const logger = pino({
  level: process.env.LOG_LEVEL ?? 'info',
  redact: ['*.password', '*.token', 'req.headers.authorization'],
  formatters: {
    level: (label) => ({ level: label }),
  },
});

logger.info(
  { user_id: 123, request_id: req.id, latency_ms: 45 },
  'request handled',
);
```

### Trace correlation (OpenTelemetry)
```typescript
import { trace, context } from '@opentelemetry/api';

function handler(req, res) {
  const span = trace.getActiveSpan();
  const traceId = span?.spanContext().traceId;

  logger.info({ trace_id: traceId, request_id: req.id }, 'received');

  // child operation auto-inherits trace_id
  context.with(trace.setSpan(context.active(), span!), () => {
    processRequest(req);
  });
}
```

### Result type (Rust-style in TypeScript)
```typescript
type Ok<T> = { ok: true; value: T };
type Err<E> = { ok: false; error: E };
type Result<T, E> = Ok<T> | Err<E>;

async function fetchUser(id: string): Promise<Result<User, 'NotFound' | 'NetworkError'>> {
  try {
    const r = await fetch(`/users/${id}`);
    if (r.status === 404) return { ok: false, error: 'NotFound' };
    if (!r.ok) return { ok: false, error: 'NetworkError' };
    return { ok: true, value: await r.json() };
  } catch {
    return { ok: false, error: 'NetworkError' };
  }
}

// Caller forced to handle both branches
const r = await fetchUser('42');
if (!r.ok) {
  if (r.error === 'NotFound') return res.status(404).end();
  return res.status(503).end();
}
```

### Error boundaries (React/preact)
```tsx
class ErrorBoundary extends Component {
  componentDidCatch(error: Error, info: ErrorInfo) {
    logger.error({ error: error.message, stack: error.stack, ...info }, 'render error');
    Sentry.captureException(error, { extra: info });
  }

  render() {
    if (this.state.hasError) return <Fallback />;
    return this.props.children;
  }
}
```

### Go error wrapping
```go
import "fmt"

func fetchOrder(id string) (*Order, error) {
    raw, err := db.Query(id)
    if err != nil {
        return nil, fmt.Errorf("fetchOrder %s: %w", id, err)
    }
    return parseOrder(raw)
}

// Caller can unwrap
if errors.Is(err, sql.ErrNoRows) { /* ... */ }
```

### Error budgets and alerting
```typescript
// Log every error, but only alert on rate
const errorRate = new Counter({
  name: 'http_errors_total',
  labelNames: ['route', 'code'],
});

app.use((err, req, res, next) => {
  errorRate.inc({ route: req.route?.path, code: err.code });
  logger.error({ err, req_id: req.id }, 'unhandled');
  res.status(500).json({ error: 'internal' });
});
// Prometheus rule: rate(http_errors_total[5m]) > 0.01 → page
```

### Sampling for high-volume logs
```typescript
const sampledLogger = logger.child({}, {
  level: 'info',
  // 1% of info logs, 100% of warn+
  hooks: {
    logMethod(args, method, level) {
      if (level === 30 && Math.random() > 0.01) return;
      method.apply(this, args);
    },
  },
});
```

## 매 결정 기준
| 상황 | Approach |
|---|---|
| Web service errors | Result types or wrapped exceptions |
| Critical assertion failure | Panic/process exit |
| Expected user input failure | Domain error type, never exception |
| Cross-service correlation | OpenTelemetry trace_id |
| PII in logs | Redact at serializer + DLP scan |
| Log retention | Hot 7d / warm 30d / cold 1y |

**기본값**: structured JSON + OTel trace IDs + Result types for domain logic + Sentry for unhandled.

## 🔗 Graph
- 부모: [[Observability]] · [[SRE]]
- 변형: [[Distributed-Tracing]] · [[Type-safe Error Handling  Exhaustiveness Checking]]
- 응용: [[Engineering Metrics (DORA)]] · [[Anomaly-Detection]]
- Adjacent: [[Flame_Graphs]] · [[경고 피로 (Alert Fatigue)]]

## 🤖 LLM 활용
**언제**: generate structured log statements with consistent fields, refactor exception-throws to Result types.
**언제 X**: never ask LLM to invent error taxonomy from scratch — derive from product domain.

## ❌ 안티패턴
- **`console.log` in prod**: no levels, no structure.
- **Catch and swallow**: `try { } catch { }` — invisible failure.
- **Generic exceptions**: `throw new Error("oops")` — caller can't discriminate.
- **PII in logs**: passwords, full credit card, JWT bodies.
- **Log spam**: per-iteration debug logs in tight loops.
- **Stringly-typed errors**: `if (err.message === "not found")` — fragile.

## 🧪 검증 / 중복
- Verified (OpenTelemetry spec, Google SRE book ch.16, Effect-TS docs).
- 신뢰도 A.

## 🕓 Changelog
| 날짜 | 변경 |
|---|---|
| 2026-05-08 | Phase 1 |
| 2026-05-10 | Manual cleanup — structured logging + Result patterns |
