[Sub-issue #80-B]: Backend Pino Migration + Correlation IDs #82

Closed
opened 2026-02-03 02:18:39 +00:00 by egullickson · 0 comments
Owner

Parent Issue

Refs #80 - Unified Debug Logging System

Scope

Migrate backend from Winston to Pino using API-compatible wrapper. Add correlation ID propagation.

Files to Modify

  • backend/src/core/logging/logger.ts - Replace Winston with Pino wrapper
  • backend/src/core/plugins/logging.plugin.ts - Use @fastify/pino for request logging
  • backend/package.json - Add pino, @fastify/pino; remove winston
  • docker-compose.yml - Add POSTGRES_LOG_* and REDIS_LOGLEVEL env vars

Implementation Details

logger.ts (Pino wrapper maintaining Winston API)

import pino from 'pino';

type LogLevel = 'debug' | 'info' | 'warn' | 'error';
const validLevels: LogLevel[] = ['debug', 'info', 'warn', 'error'];

const rawLevel = (process.env.LOG_LEVEL?.toLowerCase() || 'info') as LogLevel;
const level = validLevels.includes(rawLevel) ? rawLevel : 'info';

if (rawLevel !== level) {
  console.warn(`Invalid LOG_LEVEL "${process.env.LOG_LEVEL}", falling back to "info"`);
}

const pinoLogger = pino({
  level,
  formatters: { level: (label) => ({ level: label }) },
});

// Wrapper maintains logger.info(msg, meta) API for 79 importing files
export const logger = {
  info: (msg: string, meta?: object) => pinoLogger.info(meta || {}, msg),
  warn: (msg: string, meta?: object) => pinoLogger.warn(meta || {}, msg),
  error: (msg: string, meta?: object) => pinoLogger.error(meta || {}, msg),
  debug: (msg: string, meta?: object) => pinoLogger.debug(meta || {}, msg),
  child: (bindings: object) => {
    const childPino = pinoLogger.child(bindings);
    return {
      info: (msg: string, meta?: object) => childPino.info(meta || {}, msg),
      warn: (msg: string, meta?: object) => childPino.warn(meta || {}, msg),
      error: (msg: string, meta?: object) => childPino.error(meta || {}, msg),
      debug: (msg: string, meta?: object) => childPino.debug(meta || {}, msg),
    };
  },
};

export default logger;

Correlation ID Middleware

  • Extract X-Request-Id from Traefik headers or generate UUID
  • Use AsyncLocalStorage for context propagation
  • All logs include requestId field

PostgreSQL/Redis Config

Add to docker-compose.yml postgres and redis services:

  • POSTGRES_LOG_STATEMENT, POSTGRES_LOG_MIN_DURATION_STATEMENT
  • REDIS_LOGLEVEL (via command args)

Acceptance Criteria

  • LOG_LEVEL env var controls backend log verbosity
  • Invalid LOG_LEVEL shows warning and falls back to 'info'
  • All logs include requestId field for correlation
  • 79 importing files require NO changes (API compatible)
  • Winston removed from package.json
  • PostgreSQL logs configured per LOG_LEVEL
  • Redis loglevel configured per LOG_LEVEL

Dependencies

Depends on #80-A (config generator)

Milestone

Milestone 2: Backend Logging

## Parent Issue Refs #80 - Unified Debug Logging System ## Scope Migrate backend from Winston to Pino using API-compatible wrapper. Add correlation ID propagation. ## Files to Modify - `backend/src/core/logging/logger.ts` - Replace Winston with Pino wrapper - `backend/src/core/plugins/logging.plugin.ts` - Use @fastify/pino for request logging - `backend/package.json` - Add pino, @fastify/pino; remove winston - `docker-compose.yml` - Add POSTGRES_LOG_* and REDIS_LOGLEVEL env vars ## Implementation Details ### logger.ts (Pino wrapper maintaining Winston API) ```typescript import pino from 'pino'; type LogLevel = 'debug' | 'info' | 'warn' | 'error'; const validLevels: LogLevel[] = ['debug', 'info', 'warn', 'error']; const rawLevel = (process.env.LOG_LEVEL?.toLowerCase() || 'info') as LogLevel; const level = validLevels.includes(rawLevel) ? rawLevel : 'info'; if (rawLevel !== level) { console.warn(`Invalid LOG_LEVEL "${process.env.LOG_LEVEL}", falling back to "info"`); } const pinoLogger = pino({ level, formatters: { level: (label) => ({ level: label }) }, }); // Wrapper maintains logger.info(msg, meta) API for 79 importing files export const logger = { info: (msg: string, meta?: object) => pinoLogger.info(meta || {}, msg), warn: (msg: string, meta?: object) => pinoLogger.warn(meta || {}, msg), error: (msg: string, meta?: object) => pinoLogger.error(meta || {}, msg), debug: (msg: string, meta?: object) => pinoLogger.debug(meta || {}, msg), child: (bindings: object) => { const childPino = pinoLogger.child(bindings); return { info: (msg: string, meta?: object) => childPino.info(meta || {}, msg), warn: (msg: string, meta?: object) => childPino.warn(meta || {}, msg), error: (msg: string, meta?: object) => childPino.error(meta || {}, msg), debug: (msg: string, meta?: object) => childPino.debug(meta || {}, msg), }; }, }; export default logger; ``` ### Correlation ID Middleware - Extract X-Request-Id from Traefik headers or generate UUID - Use AsyncLocalStorage for context propagation - All logs include requestId field ### PostgreSQL/Redis Config Add to docker-compose.yml postgres and redis services: - POSTGRES_LOG_STATEMENT, POSTGRES_LOG_MIN_DURATION_STATEMENT - REDIS_LOGLEVEL (via command args) ## Acceptance Criteria - [ ] LOG_LEVEL env var controls backend log verbosity - [ ] Invalid LOG_LEVEL shows warning and falls back to 'info' - [ ] All logs include requestId field for correlation - [ ] 79 importing files require NO changes (API compatible) - [ ] Winston removed from package.json - [ ] PostgreSQL logs configured per LOG_LEVEL - [ ] Redis loglevel configured per LOG_LEVEL ## Dependencies Depends on #80-A (config generator) ## Milestone Milestone 2: Backend Logging
egullickson added the
status
backlog
type
feature
labels 2026-02-03 02:19:39 +00:00
egullickson added
status
in-progress
and removed
status
backlog
labels 2026-02-04 02:02:29 +00:00
egullickson added
status
review
and removed
status
in-progress
labels 2026-02-04 02:04:51 +00:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: egullickson/motovaultpro#82