Logging
Alchemify uses pino for structured JSON logging with automatic request context via pino-http.
Configuration
Section titled “Configuration”Set LOG_LEVEL in your .env file:
# trace | debug | info | warn | error | fatal | silentLOG_LEVEL=debugDefaults: debug in development, info in production.
In development, logs are pretty-printed via pino-pretty. In production, logs are plain JSON (one object per line), ready for any log aggregator.
What gets logged
Section titled “What gets logged”Every HTTP request (except /health) is automatically logged with:
- Request ID — unique per request
- HTTP method and URL
- Response status and duration
- userId — attached after JWT verification
Additional events at specific log levels:
| Level | Event |
|---|---|
debug | JWT authentication result, SQL queries (statement only), function calls |
info | Server start, auth success (login/verify/invite), magic-link requested |
warn | Auth failures (invalid credentials, expired tokens) |
error | Unhandled errors in request handlers |
Security rules
Section titled “Security rules”The following are never logged:
- Passwords
- JWT tokens
- SQL parameter values (only the parameter count is logged)
Magic-link tokens are logged at debug level only when NODE_ENV is not production.
Using the logger
Section titled “Using the logger”Import the shared logger instance:
import { logger } from "./logger.js";
// Structured fields go in the first argumentlogger.info({ port: 3000 }, "server started");Inside Express request handlers, use req.log instead — it automatically includes the request ID and other context:
app.post("/example", (req, res) => { req.log.info({ userId: "..." }, "something happened");});Silencing logs in tests
Section titled “Silencing logs in tests”Tests run with LOG_LEVEL=silent (configured in vitest.config.ts) so log output does not clutter test results.