# Structured logging in JSON format is implemented

- **Pattern:** `ab-001279` (`error-resilience.logging-observability.structured-logging`)
- **Severity:** high
- **Lifecycle:** active
- **Last modified:** 2026-04-18
- **Canonical URL:** https://auditbuffet.com/patterns/ab-001279
- **License:** CC-BY-4.0 — attribute to AuditBuffet Pattern Catalog (https://auditbuffet.com/patterns/ab-001279)

## Why it matters

Unstructured `console.log` output is unsearchable, unqueryable, and useless when diagnosing production incidents. Without consistent fields — `level`, `timestamp`, `traceId`, `userId` — correlating a user-reported failure to a server-side log line can take hours. NIST SP 800-53 AU-3 mandates that audit records contain sufficient information to establish what events occurred and who was responsible. ISO 25010 reliability.fault-tolerance requires detectable failure conditions; logs that can't be filtered by severity or correlated by trace ID fail that standard.

## Severity rationale

High because unstructured logs make production incident investigation hours slower, directly extending mean time to resolution for every outage.

## Remediation

Replace `console.log` calls with a structured logging library. Pino is the lowest-overhead choice for Node.js; winston is more configurable for multi-transport needs.

```ts
// lib/logger.ts
import pino from 'pino'

export const logger = pino({
  level: process.env.LOG_LEVEL ?? 'info',
  base: { service: 'api' },
})

// Usage:
logger.info(
  { userId: session.userId, traceId: req.headers['x-trace-id'] },
  'Payment initiated'
)
```

Every log call must include at minimum `level`, `timestamp` (automatic with pino), and one correlation ID (`traceId` or `requestId`). Configure `LOG_LEVEL=warn` in production to suppress debug noise.

## Detection

- **ID:** `structured-logging`
- **Severity:** `high`
- **What to look for:** Count all logging statements. Enumerate which use structured logging (JSON format with level, timestamp, context) vs. unstructured console.log calls. Check for a logging library (winston, pino, bunyan, loglevel, datadog logger, etc.) configured in the codebase. Look for logs that are output in JSON format with consistent fields. Check both client-side logging (browser console) and server-side logging (Node.js).
- **Pass criteria:** A structured logging library is configured and logs include consistent fields: timestamp, level (info, warn, error), message, traceId, userId, and request context. At least 80% of logging should use structured format with level, timestamp, and context fields.
- **Fail criteria:** No structured logging library found, or logs are plain text instead of JSON, or logging lacks consistent schema (some logs have userId, others don't; some include traceId, others don't).
- **Skip (N/A) when:** The application is a static site with no backend or logging infrastructure.
- **Do NOT pass when:** A logging library is installed but all actual log calls still use `console.log` instead of the library's methods — the library must be actively used, not just configured.

- **Cross-reference:** For error tracking service integration, see `error-tracking-service`. For sensitive data scrubbing, see `scrub-sensitive-logs`.
- **Detail on fail:** Describe the logging gap. Example: `"No structured logging library found. Logs are plain console.log() calls with no JSON formatting"` or `"Winston is installed but logs lack consistent traceId and userId fields across the application"`
- **Remediation:** Set up structured logging with a library like pino (lightweight for Node.js) or winston:

  ```ts
  // lib/logger.ts — structured logging
  import pino from 'pino'
  export const logger = pino({ level: 'info', timestamp: pino.stdTimeFunctions.isoTime })
  ```

  ```ts
  // lib/logger.ts
  import pino from 'pino'

  const logger = pino({
    level: process.env.LOG_LEVEL || 'info',
    transport: {
      target: 'pino-pretty',
      options: {
        colorize: true,
      }
    }
  })

  export default logger

  // Usage in your app:
  logger.info({
    userId: user.id,
    traceId: request.headers['x-trace-id'],
    message: 'User logged in',
    timestamp: new Date().toISOString()
  })
  ```

## External references

- iso-25010 reliability.fault-tolerance
- nist AU-3

Taxons: observability

HTML version: https://auditbuffet.com/patterns/ab-001279
