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 (default: debug in development, info in production).

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:

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

Email addresses and phone numbers embedded in freeform text are automatically caught:

logger.info("User [email protected] 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 (debug in dev, info in prod)
DEBUG_MODULES * Module filter for legacy debugLog

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:

Migration from debugLog#

debugLog from @server/lib/debug-logger is deprecated. It now delegates to the Pino logger internally, so existing callers get PII redaction automatically. For new code, use createLogger() directly:

// Before (deprecated):
import { debugLog } from "@server/lib/debug-logger";
debugLog("perception", "Analyzing intent", { messageId: "123" });

// After:
import { createLogger } from "@server/lib/logger";
const logger = createLogger("perception");
logger.info({ messageId: "123" }, "Analyzing intent");

Files#