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.
High because unstructured logs make production incident investigation hours slower, directly extending mean time to resolution for every outage.
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.
// 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.
ID: error-resilience.logging-observability.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:
// lib/logger.ts — structured logging
import pino from 'pino'
export const logger = pino({ level: 'info', timestamp: pino.stdTimeFunctions.isoTime })
// 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()
})