DEV Community

myougaTheAxo
myougaTheAxo

Posted on

Structured Logging with Claude Code: No More console.log in Production

One of the most common issues I see in Claude Code-generated code is console.log in production handlers. Here's the complete setup to prevent it and get proper structured logging instead.


CLAUDE.md: The Foundation

## Logging Rules

### Logger
- Use logger.ts (src/lib/logger.ts) — never console.log/console.error
- Logger is structured JSON (pino or winston)
- Include request context: userId, requestId, traceId where available

### Log Levels
- error: application errors that need investigation (5xx, DB errors)
- warn: unexpected but recoverable situations (retries, fallbacks)
- info: important business events (user registered, order placed)
- debug: development-only details (never in production)

### What to LOG
- Request entry/exit (timing, status)
- Business events (user.created, payment.completed)
- External service calls (start, success, failure, duration)
- Errors with context (userId, requestId, error code)

### What NOT to LOG
- Passwords, tokens, API keys
- PII: email, name, phone, SSN, credit card numbers
- Full request bodies (may contain sensitive data)
- Raw database queries with user data

### Format
Every log must include: { timestamp, level, message, service, ...context }
Enter fullscreen mode Exit fullscreen mode

Logger Setup with Pino

Generate a structured logger module using pino.
Requirements:
- JSON output in production, pretty-print in development
- Default context: { service: 'my-app', version: process.env.npm_package_version }
- redact: ['password', 'token', 'authorization', 'cookie', '*.password', '*.token']
- Child logger support: logger.child({ userId, requestId })
- Never expose as global  inject as dependency

Location: src/lib/logger.ts
Enter fullscreen mode Exit fullscreen mode

Request Logging Middleware

Generate Express request logging middleware.
Requirements:
- Log at request start: method, url, requestId (generate if missing)
- Log at request end: status code, duration (ms)
- Attach logger child with requestId to req.log
- Do NOT log request body (security risk)
- Exclude health check endpoints: /health, /ping

Location: src/middleware/requestLogger.ts
Enter fullscreen mode Exit fullscreen mode

Pre-commit Hook: Block console.log

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [{
          "type": "command",
          "command": "python .claude/hooks/no_console_log.py"
        }]
      }
    ]
  }
}
Enter fullscreen mode Exit fullscreen mode
# .claude/hooks/no_console_log.py
import json, sys, re

data = json.load(sys.stdin)
content = data.get("tool_input", {}).get("content", "") or ""
fp = data.get("tool_input", {}).get("file_path", "")

# Only check production source files
if not fp or "test" in fp or "spec" in fp or "scripts" in fp:
    sys.exit(0)

if not fp.endswith((".ts", ".js", ".py")):
    sys.exit(0)

# Check for console.log (allow console.error as it might be intentional in scripts)
if re.search(r'console\.log\(', content):
    # Allow if there's an explicit override comment
    if '// allow-console' not in content:
        print("[LOG] console.log detected in source file. Use logger.ts instead.", file=sys.stderr)
        sys.exit(2)  # Block

sys.exit(0)
Enter fullscreen mode Exit fullscreen mode

Adding Request Context

Generate a context propagation utility that makes request context
(requestId, userId) available throughout the call chain without
passing it as a function argument.

Use AsyncLocalStorage (Node.js built-in).
Context should be automatically available in logger.ts.

Location: src/lib/context.ts
Enter fullscreen mode Exit fullscreen mode

PII Scrubbing

Generate a log sanitizer for user data objects.
Requirements:
- Remove: password, passwordHash, token, refreshToken, apiKey
- Mask partially: email  user@***.com, phone  ***-***-1234
- Never mutate the original object
- Apply automatically in logger.ts before writing any log

Location: src/lib/logSanitizer.ts
Enter fullscreen mode Exit fullscreen mode

Testing That Logging Works

Generate tests that verify:
1. Sensitive fields are redacted (password, token)
2. PII is masked in user objects
3. request logger includes requestId
4. console.log is NOT called in production code (verify logger.ts is used)
Enter fullscreen mode Exit fullscreen mode

Alerting on Errors

Generate a Pino transport that sends error-level logs to Slack.
Requirements:
- Only error level and above
- Include: message, error.code, error.stack (first 3 lines), requestId
- Debounce: max 1 message/minute per error type
- Never fail the main request if Slack send fails

Location: src/lib/slackTransport.ts
Enter fullscreen mode Exit fullscreen mode

Code Review Pack (¥980) includes /code-review which flags console.log and missing log sanitization.

👉 prompt-works.jp

Myouga (@myougatheaxo) — Security-focused Claude Code engineer.

Top comments (0)