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