Logging Guide#

Hay uses Pino for structured, PII-redacting logging across the server.

Quick Start#

import { createLogger } from "@server/lib/logger";

const logger = createLogger("my-module");

logger.info("Operation completed");
logger.info({ userId: "123", duration: 45 }, "Operation completed with context");
logger.error({ err: error }, "Operation failed");
logger.warn("Deprecation notice");
logger.debug("Detailed trace info");

Pino calling convention — object first, message second:

// Structured data + message
logger.info({ orderId, status }, "Order processed");

// Error logging (use `err` key for proper serialization)
logger.error({ err: error, orderId }, "Failed to process order");

// Simple message only
logger.info("Server started");

Log Levels#

Level Use for
error Failures requiring attention
warn Degraded behavior, recoverable issues
info Important state changes, startup, success
debug Diagnostic detail, request tracing, dev output

Configure via LOG_LEVEL environment variable. Defaults to info in production and debug otherwise.

PII Redaction#

All logs are automatically redacted at two layers:

Layer 1: Field-based redaction (structured data)#

Sensitive fields are replaced with [REDACTED] before serialization. The list below is a representative subset; see server/lib/logger/redaction.ts for the full source of truth.

Layer 2: Regex-based string redaction (log messages)#

Sensitive patterns embedded in freeform text are automatically caught, including email addresses, phone numbers, JWTs, Bearer/Basic auth tokens, IPv4 addresses, credit card numbers, SSNs, and common API key prefixes (e.g., sk-, pk_live_):

logger.info("User john@example.com signed up");
// Output: "User [EMAIL_REDACTED] signed up"

logger.info("Call +1-555-123-4567 for support");
// Output: "Call [PHONE_REDACTED] for support"

Output Format#

Configuration#

Variable Default Description
LOG_LEVEL debug/info Log level (info in production, debug otherwise)
DEBUG_MODULES * Module filter for debug output

ESLint Enforcement#

console.* is banned in server code (no-console: "error"). Exceptions:

Log Retention#

Production logs must be retained for no more than 30 days per GDPR requirements.
This is configured at the infrastructure level:

Files#