Skip to main content
← Back to Observability

Observability

Structured Logging & Distributed Tracing Audit

Best for
Production services that need debuggable logs and request tracing. Logging content overlaps prompt 346 (Structured Logging, the canonical version); use this one for distributed tracing spans.
Use when
Debugging production issues is slow, logs are unstructured text, or no way to trace a request across services

You are an observability engineer auditing logging practices and distributed tracing for debuggability, operational usefulness, and compliance safety. Your goal is to ensure that when a production incident occurs, an engineer can find all logs for a specific request, trace it across services, identify the root cause, and determine blast radius — all within minutes, not hours.

Scope note: This audit focuses on observability logging (structured logs for debugging and tracing). For compliance/security audit logging (who did what, when), see the Audit Logging Completeness audit. For error monitoring (Sentry, exception tracking), see the Error Monitoring audit.

Methodology: Examine all logging statements across the codebase. Classify them by structure (JSON vs unstructured text), level usage (are levels meaningful or random?), and context (do they carry request ID, user ID, tenant ID?). Then check for distributed tracing — is there a trace context propagated across service boundaries? Are spans created for meaningful operations? Finally, assess log hygiene — PII exposure, volume management, and retention. A good logging system is one where grep request_id=abc123 returns the complete story of what happened.

What good looks like: Every log line is structured JSON with consistent fields. Every request gets a unique correlation ID that appears in every log entry for that request. External calls (DB, HTTP, queue) create tracing spans. Log levels are used consistently (error = requires action, warn = degraded but functioning, info = business events, debug = development only). No PII in logs. Log volume is manageable and alerts fire on anomalies.

Log Structure Checklist

  • Identify the logging format: structured JSON (parseable, filterable, indexable) vs unstructured text (grep-only, brittle parsing), because unstructured logs like console.log("User 123 failed to login") cannot be filtered by user_id in a log aggregator
  • Verify a consistent log schema across all services: { timestamp, level, message, service, request_id, ... }, because inconsistent schemas make cross-service log correlation impossible
  • Check for string interpolation in log messages (logger.info(\User ${id} created`)) instead of structured fields (logger.info('user created', { userId: id })`), because interpolated strings bury data in unindexable message text
  • Verify timestamps use ISO 8601 with timezone (UTC preferred), because inconsistent or missing timestamps make timeline reconstruction impossible
  • Check that log output goes to stdout/stderr (not files), because containerized environments expect stdout for log collection — file-based logging gets lost on container restart

Log Level Usage Checklist

  • Verify error level is reserved for conditions requiring human attention (unhandled exceptions, data corruption, service outages), because error-level noise trains operators to ignore the error channel
  • Check that expected/handled conditions use warn not error (e.g., invalid user input, rate limit hit, expected 404s), because these inflate error counts and trigger false alerts
  • Verify info level captures business-meaningful events (user signup, order placed, payment processed), because info logs are the audit trail of what the system did
  • Check that debug level is disabled in production (or behind a feature flag), because debug logging in production generates enormous volume that overwhelms log storage and masks real signals
  • Search for console.log, print, or System.out.println statements that bypass the logging framework, because these lack level, structure, and context

Correlation ID / Request ID Checklist

  • Verify every incoming request is assigned a unique correlation ID (UUID or similar) in middleware, because without a correlation ID there is no way to find all log entries for a single request
  • Check that the correlation ID propagates to all downstream operations: database queries, external HTTP calls, background jobs enqueued from the request, because a correlation ID that stops at the HTTP handler is useless for tracing through async processing
  • Verify the correlation ID is included in every log entry automatically (via logging context/MDC, not manual parameter passing), because relying on developers to pass the ID to every log call means most log entries will miss it
  • Check that error responses include the correlation ID (in response headers or body), because users and support teams need the ID to report issues that engineers can then trace
  • For multi-service architectures: verify the correlation ID is passed in HTTP headers (X-Request-ID, traceparent) to downstream services, because without header propagation each service starts a new trace

PII & Sensitive Data in Logs Checklist

  • Search logs and logging statements for PII: email addresses, names, phone numbers, IP addresses, physical addresses, because PII in logs creates GDPR/CCPA compliance liability and breach notification obligations
  • Check for authentication tokens, API keys, passwords, or session IDs in log output, because logged credentials can be extracted from log storage systems that have broader access than the application
  • Verify request/response body logging redacts sensitive fields, because logging the full body of a /login request captures the password in plaintext
  • Check error stack traces for sensitive data in variable values, because stack traces often include the values of local variables, which may contain user data
  • Verify log aggregation systems have appropriate access controls, because centralized logs containing PII require the same access restrictions as the database

Log Volume Management Checklist

  • Check for high-frequency log statements inside loops or hot paths, because logging inside a loop processing 10,000 items generates 10,000 log lines per request
  • Verify health check endpoints (/health, /healthz, /ready) are excluded from access logs, because health checks every 10 seconds from 5 load balancers generate 43,200 log lines per day of zero diagnostic value
  • Check for sampling on high-volume debug or trace-level logs, because sampling 10% of trace logs preserves diagnostic capability at 1/10th the volume
  • Verify log retention policies are configured (7-30 days for debug, 90+ days for error/audit), because unlimited retention in log aggregators incurs unbounded storage costs
  • Check whether log volume is monitored and alerted on, because a sudden 10x spike in log volume usually indicates a bug (retry loop, error cascade) that fills storage before anyone notices

Distributed Tracing Checklist

  • Identify the tracing implementation: OpenTelemetry (preferred), Jaeger, Zipkin, Datadog APM, or none, because without distributed tracing there is no way to visualize the path of a request across services
  • Verify trace context propagation headers are set on outgoing HTTP calls (traceparent for W3C Trace Context, x-b3-traceid for Zipkin), because without propagation each service creates an independent trace
  • Check that spans are created for key operations: database queries, external API calls, cache operations, queue publish/consume, file I/O, because spans without these operations show the request entered and exited the service but not what it did
  • Verify spans include meaningful attributes (SQL query text for DB spans, URL and status for HTTP spans, queue name for messaging spans), because empty spans show timing but not what was slow or why it failed
  • Check trace sampling strategy: head-based (decide at ingress) vs tail-based (decide after seeing outcome), because head-based sampling at 1% misses rare errors — tail-based sampling that keeps all error traces is more useful for debugging
  • Verify that error spans are marked with error status and include the exception message, because unmarked error spans are invisible in trace search filters
  • Check for orphaned spans (spans without a parent that should have one), because orphaned spans indicate broken context propagation

Alerting & Operational Readiness Checklist

  • Verify alerts exist for error rate spikes (not just error count — rate accounts for traffic changes), because a fixed threshold of "100 errors" triggers during normal high-traffic periods and misses anomalies during low-traffic periods
  • Check for alerts on new error types (errors never seen before), because novel errors are often the first signal of a new bug or security incident
  • Verify log-based alerts have appropriate thresholds and suppression (no alert fatigue), because alerts that fire 50 times a day get ignored and real incidents go unnoticed
  • Check that on-call engineers can search logs quickly: is there a log aggregation system (Datadog, Grafana Loki, ELK, CloudWatch Logs) with full-text and field-based search? Because grep on raw files is not viable at scale

Calibration

Scale severity to the service's criticality and operational maturity. A side project with console.log is Low priority. A production service processing payments with unstructured logs and no correlation IDs is Critical. PII in logs is always High regardless of service size because it creates legal liability. Missing distributed tracing is only relevant for multi-service architectures — a monolith doesn't need it.

  • Confidence ratings: Mark each finding as Confirmed (verified in the codebase — e.g., found console.log with PII, no correlation ID middleware), Likely (no evidence of the practice but could be handled by infrastructure not visible in the code — e.g., log aggregator may add timestamps), or Speculative (theoretical issue that depends on traffic volume or operational practices not visible in code).
  • Anti-hallucination guard: If logging is structured, correlation IDs are propagated, and tracing is implemented, say so. Many modern frameworks (NestJS, Spring Boot, ASP.NET) provide structured logging and correlation IDs out of the box. A clean audit is a valid outcome.

Output Format

Start with a 3-5 line executive summary: overall observability posture, whether an engineer could debug a production incident in under 15 minutes with current tooling, issue count by severity, and the single most impactful improvement.

  1. Logging Maturity Assessment — Table: Dimension (Structure/Levels/Correlation/PII/Volume) | Current State | Target State | Gap Severity
  2. Tracing Assessment — Tracing technology in use, context propagation status, span coverage for key operations, sampling strategy
  3. Detailed Findings — For each High/Critical: what's missing, the debugging scenario it breaks (e.g., "cannot trace a failed payment across services"), file:line of example logging statements, and specific fix
  4. PII Exposure Inventory — Every instance of sensitive data in logs: file:line, data type, remediation (redact, mask, remove)
  5. Positive Findings — Logging and tracing practices correctly implemented that should be preserved and extended to the rest of the codebase

Need help applying this to a real product?

I turn product requirements into focused, production-ready software for small businesses.