8.4 KiB

📝 Centralized Logging System

This guide covers the centralized, high-performance logging system implemented using Pino - the fastest JSON logger for Node.js applications.

🎯 Architecture Overview

Single Logger System

  • Backend (BFF): nestjs-pino with Pino
  • Frontend: Custom structured logger compatible with backend
  • Shared: Common interfaces and configurations
  • No more mixed logging systems

Benefits of Centralization

  • 🚀 Performance: Pino is 5x faster than winston
  • 🔒 Security: Automatic sensitive data sanitization
  • 📊 Structured: JSON logging for better parsing
  • 🌐 Correlation: Request tracking across services
  • 📈 Monitoring: Easy integration with log aggregation'
  • this implemetnatin requires more type safety faeteur

🏗️ Implementation

Backend (BFF) - NestJS + Pino

// ✅ CORRECT: Use nestjs-pino Logger
import { Logger } from "nestjs-pino";

@Injectable()
export class UserService {
  constructor(@Inject(Logger) private readonly logger: Logger) {}

  async findUser(id: string) {
    this.logger.info(`Finding user ${id}`);
    // ... implementation
  }
}

Frontend - Structured Logger

// ✅ CORRECT: Use shared logger interface
import { logger } from "@/lib/logger";

export function handleApiCall() {
  logger.logApiCall("/api/users", "GET", 200, 150);
  logger.logUserAction("user123", "login");
}

🚫 What NOT to Do

Don't Use Multiple Logging Systems

// ❌ WRONG: Mixing logging systems
import { Logger } from "@nestjs/common"; // Don't use this - REMOVED
import { Logger } from "nestjs-pino"; // ✅ Use this - CENTRALIZED

// ❌ WRONG: Console logging in production
console.log("User logged in"); // Don't use console.log
logger.info("User logged in"); // Use structured logger

Current Status: FULLY CENTRALIZED

  • All BFF services now use nestjs-pino Logger
  • No more @nestjs/common Logger imports
  • No more new Logger() instantiations
  • Single logging system throughout the entire backend

Don't Use Console Methods

// ❌ WRONG: Direct console usage
console.log("Debug info");
console.error("Error occurred");
console.warn("Warning message");

// ✅ CORRECT: Structured logging
logger.debug("Debug info");
logger.error("Error occurred");
logger.warn("Warning message");

🔧 Configuration

Environment Variables

# Logging configuration
LOG_LEVEL=info                    # error, warn, info, debug, trace
APP_NAME=customer-portal-bff     # Service identifier
NODE_ENV=development             # Environment context

Log Levels

Level Numeric Description
error 0 Errors that need immediate attention
warn 1 Warnings that should be monitored
info 2 General information about operations
debug 3 Detailed debugging information
trace 4 Very detailed tracing information

📝 Usage Examples

Basic Logging

// Simple messages
logger.info("User authentication successful");
logger.warn("Rate limit approaching");
logger.error("Database connection failed");

// With structured data
logger.info("Invoice created", {
  invoiceId: "INV-001",
  amount: 99.99,
  userId: "user123",
});

API Call Logging

// Automatic API call logging
logger.logApiCall("/api/invoices", "POST", 201, 250, {
  userId: "user123",
  invoiceId: "INV-001",
});

User Action Logging

// User activity tracking
logger.logUserAction("user123", "password_change", {
  ipAddress: "192.168.1.1",
  userAgent: "Mozilla/5.0...",
});

Error Logging

// Comprehensive error logging
try {
  // ... operation
} catch (error) {
  logger.logError(error, "user_creation", {
    userId: "user123",
    email: "user@example.com",
  });
}

🔍 Request Correlation

Automatic Correlation IDs

// Every request gets a unique correlation ID
// Headers: x-correlation-id: 1703123456789-abc123def

logger.info("Processing request", {
  correlationId: req.headers["x-correlation-id"],
  endpoint: req.url,
  method: req.method,
});

Manual Correlation

// Set correlation context
logger.setCorrelationId("req-123");
logger.setUserId("user-456");
logger.setRequestId("req-789");

// All subsequent logs include this context
logger.info("User action completed");

📊 Production Logging

File Logging

// Automatic log rotation
// - Combined logs: logs/customer-portal-bff-combined.log
// - Error logs: logs/customer-portal-bff-error.log
// - Console output: stdout (for container logs)

Log Aggregation

// Structured JSON output for easy parsing
{
  "timestamp": "2025-01-22T10:30:00.000Z",
  "level": "info",
  "service": "customer-portal-bff",
  "environment": "production",
  "message": "User authentication successful",
  "correlationId": "req-123",
  "userId": "user-456",
  "data": {
    "ipAddress": "192.168.1.1",
    "userAgent": "Mozilla/5.0..."
  }
}

🚀 Performance Benefits

Benchmarks (Pino vs Winston)

Operation Pino Winston Improvement
JSON serialization 1x 5x 5x faster
Object serialization 1x 3x 3x faster
String interpolation 1x 2x 2x faster
Overall performance 1x 5x 5x faster

Memory Usage

  • Pino: Minimal memory footprint
  • Winston: Higher memory usage due to object retention
  • Console: No memory overhead but unstructured

🔒 Security Features

Automatic Data Sanitization

// Sensitive data is automatically redacted
logger.info("User login", {
  email: "user@example.com",
  password: "[REDACTED]", // Automatically sanitized
  token: "[REDACTED]", // Automatically sanitized
  ipAddress: "192.168.1.1", // Safe to log
});

Sanitized Headers

// Request headers are automatically cleaned
{
  "authorization": "[REDACTED]",
  "cookie": "[REDACTED]",
  "x-api-key": "[REDACTED]",
  "user-agent": "Mozilla/5.0...",  // Safe to log
  "accept": "application/json"      // Safe to log
}

🔄 Migration Guide

From @nestjs/common Logger

// ❌ OLD: @nestjs/common Logger
import { Logger } from "@nestjs/common";
private readonly logger = new Logger(ServiceName.name);

// ✅ NEW: nestjs-pino Logger
import { Logger } from "nestjs-pino";
constructor(@Inject(Logger) private readonly logger: Logger) {}

From Console Logging

// ❌ OLD: Console methods
console.log("Debug info");
console.error("Error occurred");

// ✅ NEW: Structured logger
logger.debug("Debug info");
logger.error("Error occurred");

From Winston

// ❌ OLD: Winston logger
import * as winston from 'winston';
const logger = winston.createLogger({...});

// ✅ NEW: Pino logger
import { Logger } from "nestjs-pino";
constructor(@Inject(Logger) private readonly logger: Logger) {}

📋 Best Practices

Do's

  • Use structured logging with context
  • Include correlation IDs in all logs
  • Log at appropriate levels
  • Sanitize sensitive data
  • Use consistent message formats

Don'ts

  • Don't mix logging systems
  • Don't use console methods in production
  • Don't log sensitive information
  • Don't log large objects unnecessarily
  • Don't use string interpolation for logging

🛠️ Troubleshooting

Common Issues

  1. Multiple Logger Instances: Ensure single logger per service
  2. Missing Correlation IDs: Check middleware configuration
  3. Performance Issues: Verify log level settings
  4. Missing Logs: Check file permissions and disk space

Debug Mode

# Enable debug logging
LOG_LEVEL=debug pnpm dev

# Check logger configuration
LOG_LEVEL=trace pnpm dev

📚 Additional Resources