Skip to main content

Structured Logging

TrickBook uses console.log with ad-hoc [API] prefixes everywhere. There are 410+ console statements in the backend and 54+ in the mobile app. In production, these are unsearchable, unfilterable, and mix debug noise with actual errors.

The Problem

// Current state - scattered across 14+ files
console.log('[Auth] Attempting login for:', email);
console.log('[TrickLists] Fetching:', url);
console.log('[TrickLists] Success, count:', data?.length || 0);
console.error('Error during Google authentication:', error);
console.log('DEBUG Stats - searching for userId:', id);

No log levels. No structured output. No way to filter errors from debug noise. Potential PII leaks (logging emails, user IDs).

Backend: Logger Setup

cd Backend
npm install winston
// lib/logger.js
const winston = require('winston');

const logger = winston.createLogger({
level: process.env.LOG_LEVEL || 'info',
format: winston.format.combine(
winston.format.timestamp(),
winston.format.errors({ stack: true }),
process.env.NODE_ENV === 'production'
? winston.format.json()
: winston.format.combine(
winston.format.colorize(),
winston.format.simple()
)
),
defaultMeta: { service: 'trickbook-api' },
transports: [
new winston.transports.Console(),
],
});

module.exports = logger;

Option B: Pino (faster, simpler)

cd Backend
npm install pino pino-pretty
// lib/logger.js
const pino = require('pino');

const logger = pino({
level: process.env.LOG_LEVEL || 'info',
transport: process.env.NODE_ENV !== 'production'
? { target: 'pino-pretty' }
: undefined,
});

module.exports = logger;

Usage

const logger = require('../lib/logger');

// Instead of console.log
logger.info('User logged in', { userId: user._id });
logger.warn('Rate limit approaching', { ip: req.ip, count: 90 });
logger.error('Database query failed', { error: err.message, collection: 'users' });

// NEVER log sensitive data
logger.info('Login attempt', { email }); // BAD - PII
logger.info('Login attempt', { userId }); // GOOD - internal ID only

HTTP Request Logging

npm install morgan
// index.js
const morgan = require('morgan');
const logger = require('./lib/logger');

// Pipe Morgan through your logger
const morganStream = {
write: (message) => logger.info(message.trim(), { type: 'http' }),
};

app.use(morgan(':method :url :status :res[content-length] - :response-time ms', {
stream: morganStream,
}));

Mobile: Logger Setup

For React Native, wrap console with levels and disable in production:

// src/lib/logger.ts
type LogLevel = 'debug' | 'info' | 'warn' | 'error';

const LOG_LEVELS: Record<LogLevel, number> = {
debug: 0,
info: 1,
warn: 2,
error: 3,
};

const currentLevel: LogLevel = __DEV__ ? 'debug' : 'warn';

function shouldLog(level: LogLevel): boolean {
return LOG_LEVELS[level] >= LOG_LEVELS[currentLevel];
}

export const logger = {
debug: (message: string, data?: Record<string, unknown>) => {
if (shouldLog('debug')) console.log(`[DEBUG] ${message}`, data ?? '');
},
info: (message: string, data?: Record<string, unknown>) => {
if (shouldLog('info')) console.log(`[INFO] ${message}`, data ?? '');
},
warn: (message: string, data?: Record<string, unknown>) => {
if (shouldLog('warn')) console.warn(`[WARN] ${message}`, data ?? '');
},
error: (message: string, data?: Record<string, unknown>) => {
if (shouldLog('error')) console.error(`[ERROR] ${message}`, data ?? '');
},
};

In production, only warn and error output. All the [API] Fetching... debug noise is silenced.

Migration Strategy

Don't rewrite all 400+ console statements at once. Follow this approach:

  1. Add the logger module to both repos
  2. Update Biome config to warn on console.log
  3. Migrate file-by-file as you touch code
  4. New code must use the logger (enforced by lint rule)

The noConsoleLog Biome rule will flag every console.log as a warning, creating a natural migration pressure.

Log Levels Guide

LevelWhen to UseExample
errorSomething broke, needs attentionDatabase connection failed
warnSomething unexpected but handledRate limit approaching
infoNormal operations worth notingUser logged in, server started
debugDetailed info for troubleshootingAPI request/response details