npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

express-enhanced-logger

v2.9.0

Published

An enhanced Express.js logger with performance monitoring, SQL query formatting, and customizable features

Readme

express-enhanced-logger

npm version Test Coverage TypeScript License: MIT

A Rails-inspired Express.js logger with clean output, performance monitoring, SQL query formatting, and Prisma integration. Built on Winston with full TypeScript support.

✨ Features

  • 🚂 Rails-Style Output - Clean, minimal logging format inspired by Ruby on Rails (no emojis, clear formatting)
  • ⏱️ Timing Breakdown - Rails-style timing breakdown showing Logic, DB, and Total durations for each request
  • 🚀 Performance Monitoring - Track slow requests, memory usage, and response times
  • 🔍 Smart SQL Formatting - Intelligent truncation for large IN clauses with parameter substitution
  • 🗄️ Prisma Integration - Plug-and-play Prisma logging with one line of code + automatic DB time tracking
  • 📏 measure() Helper - Wrap any code block to measure and log its execution time
  • 📁 File Logging - Automatic log rotation with configurable retention (JSON format)
  • ⚙️ Highly Configurable - Extensive customization options for any use case
  • 🔧 TypeScript First - Full type definitions and interfaces included
  • Well Tested - 89% test coverage with 229 passing tests
  • 🪶 Lightweight - Minimal dependencies (winston, chalk, winston-daily-rotate-file)

📦 Installation

npm install express-enhanced-logger
# or
yarn add express-enhanced-logger
# or
pnpm add express-enhanced-logger

Requirements:

  • Node.js >= 18.0.0
  • Express.js >= 4.0.0 or >= 5.0.0 (peer dependency)

Module System Support:

This library supports both ES modules and CommonJS:

// ES modules (import)
import { EnhancedLogger } from 'express-enhanced-logger';

// CommonJS (require)
const { EnhancedLogger } = require('express-enhanced-logger');

🚀 Quick Start

Basic Usage

import express from 'express';
import { createLogger, requestLogger } from 'express-enhanced-logger';

const app = express();

// Create logger with default configuration
const logger = createLogger();

// Add request logging middleware
app.use(requestLogger());

// Use logger in your routes
app.get('/api/users', (req, res) => {
  logger.info('Fetching users');
  res.json({ users: [] });
});

app.listen(3000, () => {
  logger.info('Server started on port 3000');
});

Prisma Integration (One-Line Setup!)

import { PrismaClient } from '@prisma/client';
import { setupPrismaLogging } from 'express-enhanced-logger';

const prismaClient = new PrismaClient({
  log: [
    { emit: 'event', level: 'query' },
    { emit: 'event', level: 'info' },
    { emit: 'event', level: 'warn' },
    { emit: 'event', level: 'error' }
  ]
});

// That's it! Automatic logging for all Prisma events
setupPrismaLogging(prismaClient);

export default prismaClient;

🎯 See the Prisma Integration section below for detailed usage and examples.

🚂 Rails-Style Logging

The logger produces clean, Rails-inspired output that's minimal and easy to read:

Key Characteristics

  • No emojis or fancy symbols - Just clean, professional text
  • 🕐 Timestamps only at request start - Not cluttering every line
  • 📏 Proper indentation - 2 spaces for SQL queries and parameters
  • 🎯 Clear request flow - Started → Processing → Queries → Completed

Example Output

Started GET "/api/users/123" for 127.0.0.1 at 11/21/2025, 10:30:45 CST
Processing by UsersController#show as JSON
  SELECT * FROM users WHERE id = $1 (1.5ms)  ['123']
Completed 200 OK in 15ms

Adding Controller Metadata

For even cleaner logs that show controller actions, add metadata to your routes:

app.get('/users/:id', (req, res) => {
  // Add controller metadata
  req.route.controller = 'UsersController';
  req.route.action = 'show';
  
  // Your route logic
  res.json({ id: req.params.id, name: 'John Doe' });
});

This produces:

Processing by UsersController#show as JSON

Instead of:

Processing by /users/:id as JSON

SQL Query Format

Queries are logged with proper indentation and timing:

  SELECT * FROM users WHERE id = $1 (1.5ms)  ['123']
  INSERT INTO orders (user_id, total) VALUES ($1, $2) (2.3ms)  [123, 99.99]

Format: QUERY (duration) [params]

With Custom Configuration

import { createLogger, requestLogger } from 'express-enhanced-logger';

const logger = createLogger({
  level: 'debug',
  enableFileLogging: true,
  logsDirectory: 'my-logs',
  slowRequestThreshold: 500, // Log requests slower than 500ms
  slowQueryThreshold: 1000, // Log slow Prisma queries
  enableColors: true,
  maxArrayLength: 10, // Truncate arrays longer than 10 items
});

// Use request logger middleware
app.use(requestLogger());

⚙️ Configuration Options

LoggerConfig Interface

interface LoggerConfig {
  /** Log level (default: 'info') */
  level?: 'error' | 'warn' | 'info' | 'debug' | 'query';

  /** Enable file logging (default: true) */
  enableFileLogging?: boolean;

  /** Directory for log files (default: 'logs') */
  logsDirectory?: string;

  /** Maximum file size before rotation (default: '20m') */
  maxFileSize?: string;

  /** Number of days to keep log files (default: '7d') */
  maxFiles?: string;

  /** Enable gzip compression of rotated logs (default: true) */
  zippedArchive?: boolean;

  /** Slow request threshold in milliseconds (default: 1000) */
  slowRequestThreshold?: number;

  /** Slow query threshold in milliseconds for Prisma queries (default: 1000) */
  slowQueryThreshold?: number;

  /** Memory warning threshold in bytes (default: 100MB) */
  memoryWarningThreshold?: number;

  /** Maximum array length to show in logs before truncating (default: 5) */
  maxArrayLength?: number;

  /** Maximum string length to show in logs before truncating (default: 100) */
  maxStringLength?: number;

  /** Maximum object keys to show in logs before truncating (default: 20) */
  maxObjectKeys?: number;

  /** Enable colored console output (default: true in development, false in production) */
  enableColors?: boolean;

  /** Enable simple logging mode - shows only the message without level or formatting (default: false) */
  simpleLogging?: boolean;

  /** Enable Rails-style timing breakdown in request logs (default: true) */
  enableTimingBreakdown?: boolean;

  /** Custom query formatter function for SQL queries */
  customQueryFormatter?: (query: string, params: string) => string;

  /** Function to extract user information from request */
  getUserFromRequest?: (
    req: Request
  ) => { email?: string; id?: string | number; [key: string]: unknown } | undefined;

  /** Function to extract request ID from request */
  getRequestId?: (req: Request) => string | undefined;

  /** Custom log format function (replaces default formatting) */
  customLogFormat?: (info: WinstonLogInfo) => string;

  /** Additional metadata to include in request logs */
  additionalMetadata?: (req: Request, res: Response) => Record<string, unknown>;
}

📝 Usage Examples

Basic Logging

import { createLogger } from 'express-enhanced-logger';

const logger = createLogger();

// String messages
logger.info('Application started');
logger.warn('This is a warning');
logger.error('An error occurred');
logger.debug('Debug information');

// With metadata
logger.info('User login', { userId: 123, ip: '192.168.1.1' });
logger.error('Database error', { error: 'Connection timeout', query: 'SELECT * FROM users' });

// Object messages
logger.info({ event: 'user_login', userId: 456, success: true });

Using Convenience Functions

import { info, warn, error, debug } from 'express-enhanced-logger';

// Use exported functions directly (uses default logger)
info('Server starting...');
warn('Low memory warning');
error('Failed to connect to database');
debug('Request payload', { body: req.body });

Simple Logging Mode

For even cleaner output without any formatting (just raw messages):

import { createLogger } from 'express-enhanced-logger';

// Enable simple logging mode - shows only the raw message
const logger = createLogger({ simpleLogging: true });

logger.info('Just the message'); // Output: Just the message
logger.warn('A warning message'); // Output: A warning message
logger.error({ code: 500, msg: 'Error' }); // Output: { code: 500, msg: 'Error' }

// Compare with normal Rails-style logging:
const normalLogger = createLogger({ simpleLogging: false });
normalLogger.info('Started GET "/" for 127.0.0.1 at 11/21/2025, 10:30:45 CST');
// Output: Started GET "/" for 127.0.0.1 at 11/21/2025, 10:30:45 CST

Note: Simple logging mode disables Rails-style request formatting and SQL query formatting.

Request Logging Middleware

import express from 'express';
import { createLogger, requestLogger } from 'express-enhanced-logger';

const app = express();

// Basic request logging (using default logger)
app.use(requestLogger());

// With custom configuration
const logger = createLogger({
  slowRequestThreshold: 500,
  getUserFromRequest: (req) => req.currentUser,
  additionalMetadata: (req, res) => ({
    tenantId: req.headers['x-tenant-id'],
    apiVersion: req.headers['api-version'],
  }),
});

// Use the request logger
app.use(logger.requestLogger());

Prisma Integration (SQL Query Logging)

The logger includes smart SQL formatting that efficiently truncates large queries, particularly IN clauses with many parameters.

⚠️ IMPORTANT: Prisma integration is incompatible with simpleLogging: true. Make sure to set simpleLogging: false (or omit it, as false is the default) when using logger.query().

🎯 Caller Location Tracking (NEW!)

Track exactly where in your code each Prisma query originates with Rails-style indicators:

import { PrismaClient } from '@prisma/client';
import { createPrismaExtension, setupPrismaLogging, createLogger } from 'express-enhanced-logger';

const prisma = new PrismaClient({
  log: [
    { emit: 'event', level: 'query' },
    { emit: 'event', level: 'error' },
    { emit: 'event', level: 'info' },
    { emit: 'event', level: 'warn' },
  ],
});

// Apply caller location extension
const extendedPrisma = prisma.$extends(createPrismaExtension());

// Setup logging
const logger = createLogger({ level: 'query' });
setupPrismaLogging(prisma);

export default extendedPrisma;

Output with caller location:

  User Load (12.3ms)  SELECT "User"."id", "User"."email" FROM "User" WHERE "User"."id" = $1  ["123"]
  ↳ src/controllers/users.ts:42

The caller location feature uses AsyncLocalStorage to track where queries originate from, helping you debug and optimize database calls.

Plug-and-Play Setup (Recommended)

The easiest way to integrate Prisma logging is using the setupPrismaLogging() function:

import { PrismaClient } from '@prisma/client';
import { createLogger, setupPrismaLogging } from 'express-enhanced-logger';

// Create logger with Prisma integration
const logger = createLogger({
  level: 'query', // Enable query-level logging
  slowQueryThreshold: 1000, // Log queries slower than 1 second as warnings
});

// Create Prisma client with event logging
const prismaClient = new PrismaClient({
  log: [
    { emit: 'event', level: 'query' },
    { emit: 'event', level: 'info' },
    { emit: 'event', level: 'warn' },
    { emit: 'event', level: 'error' }
  ]
});

// Setup Prisma logging - automatically configures all event handlers
logger.setupPrismaLogging(prismaClient);

// That's it! All Prisma events are now logged through the enhanced logger
export default prismaClient;

The setupPrismaLogging() function automatically:

  • Enables Prisma integration
  • Sets up event handlers for query, info, warn, and error events
  • Formats and logs queries with smart truncation
  • Highlights slow queries based on slowQueryThreshold
  • Only activates in non-test environments

Using with Custom Logger Instance

If you're using a custom logger instance:

import { EnhancedLogger } from 'express-enhanced-logger';
import { PrismaClient } from '@prisma/client';

const logger = new EnhancedLogger({
  level: 'query',
  slowQueryThreshold: 500, // Custom threshold
});

const prismaClient = new PrismaClient({
  log: [
    { emit: 'event', level: 'query' },
    { emit: 'event', level: 'info' },
    { emit: 'event', level: 'warn' },
    { emit: 'event', level: 'error' }
  ]
});

// Setup logging on the custom instance
logger.setupPrismaLogging(prismaClient);

Manual Setup (Advanced)

If you need more control, you can manually setup the event handlers:

import { createLogger } from 'express-enhanced-logger';
import { PrismaClient } from '@prisma/client';

const logger = createLogger({
  level: 'query', // Enable query-level logging
  simpleLogging: false,
});

const prisma = new PrismaClient({
  log: [
    {
      emit: 'event',
      level: 'query',
    },
  ],
});

// Subscribe to Prisma query events
prisma.$on('query', (e) => {
  // Use logger.query() with proper data structure
  logger.query({
    type: e.query.split(' ')[0].toUpperCase(), // Extract query type (SELECT, INSERT, etc.)
    query: e.query,
    params: JSON.stringify(e.params),
    duration: `${e.duration}ms`,
  });
});

Smart Query Truncation:

The logger intelligently truncates only queries with large parameter lists (10+ parameters in IN clauses):

// Short queries - displayed in full
SELECT * FROM users WHERE id IN (1, 2, 3)

// Large IN clauses - smartly truncated
SELECT * FROM users WHERE id IN (1,2,3,...12 more...,18,19,20)

// Works even without parameter values - truncates placeholders
SELECT * FROM lots WHERE id IN (@P1,@P2,@P3,...530 more...,@P534,@P535,@P536)

Common Issues:

  • "undefined" in logs: You have simpleLogging: true enabled. Set it to false.
  • Queries not truncated: Make sure you're calling logger.query() (not logger.info()) with the correct data structure {type, query, params, duration}.
  • Configuration warning on startup: The logger will warn you if you have incompatible settings enabled.

Custom User Extraction

import jwt from 'jsonwebtoken';

// Extract user from JWT token
// Configure logger with custom user extraction
const logger = createLogger({
  getUserFromRequest: (req) => {
    if (req.headers.authorization) {
      try {
        const token = req.headers.authorization.replace('Bearer ', '');
        const decoded = jwt.verify(token, process.env.JWT_SECRET) as any;
        return { email: decoded.email, id: decoded.userId };
      } catch {
        return undefined;
      }
    }
    return undefined;
  },
  getRequestId: (req) => {
    // Extract from header or generate
    return req.headers['x-request-id'] as string || crypto.randomUUID();
  },
});

app.use(requestLogger());

Multiple Logger Instances

import { EnhancedLogger } from 'express-enhanced-logger';

// Create specific loggers for different modules
const authLogger = new EnhancedLogger({
  level: 'debug',
  logsDirectory: 'logs/auth',
  additionalMetadata: (req, res) => ({ module: 'auth' }),
});

const apiLogger = new EnhancedLogger({
  level: 'info',
  logsDirectory: 'logs/api',
  slowRequestThreshold: 2000,
});

// Use different loggers for different routes
app.use('/auth', authLogger.requestLogger);
app.use('/api', apiLogger.requestLogger);

Custom Log Format

const logger = createLogger({
  customLogFormat: (info) => {
    const { timestamp, level, message } = info;
    return `[${timestamp}] ${level.toUpperCase()}: ${JSON.stringify(message)}`;
  },
});

Performance Monitoring

The logger automatically tracks request performance and memory usage:

const logger = createLogger({
  slowRequestThreshold: 1000, // Warn about requests slower than 1 second
  memoryWarningThreshold: 50 * 1024 * 1024, // Warn when heap exceeds 50MB
});

app.use(requestLogger());

What's tracked automatically:

  • ⏱️ Request Duration - Logs slow requests above threshold with warning emoji
  • 💾 Memory Usage - Shows heap memory delta per request
  • 📊 HTTP Status Codes - Color-coded by status ranges (2xx green, 4xx yellow, 5xx red)
  • 📦 Response Sizes - Tracks Content-Length headers
  • 👤 User Context - Includes user email/ID when available
  • 🔗 Request IDs - Tracks request correlation IDs

📊 Sample Output

The logger produces clean, Rails-style output with no emojis or fancy formatting - just clear, readable logs:

HTTP Request Log

Started GET "/api/users/123" for 127.0.0.1 at 11/21/2025, 10:30:45 CST
Processing by UsersController#show as JSON
Completed 200 OK in 245ms

Request with Parameters

Started POST "/api/reports" for 127.0.0.1 at 11/21/2025, 10:30:45 CST
Processing by ReportsController#create as JSON
  Parameters: { type: 'monthly', year: 2025 }
Completed 200 OK in 1850ms

Slow Request Warning

Started POST "/api/reports" for 127.0.0.1 at 11/21/2025, 10:30:45 CST
Processing by ReportsController#create as JSON
Completed 200 OK in 1850ms (Slow request)

SQL Query Log (with Prisma)

Started GET "/api/users/123" for 127.0.0.1 at 11/21/2025, 10:30:45 CST
Processing by UsersController#show as JSON
  SELECT * FROM users WHERE id = $1 AND status = $2 (45ms)  ['123', 'ACTIVE']
Completed 200 OK in 245ms

Large IN Clause (Smart Truncation)

  SELECT * FROM orders WHERE id IN (1,2,3,...47 more...,53,54,55) (120ms)

Error Log

Started GET "/api/users/999" for 127.0.0.1 at 11/21/2025, 10:30:45 CST
Processing by UsersController#show as JSON
User not found
Completed 500 Error in 15ms

Complete Request Flow Example

Started POST "/api/users" for 127.0.0.1 at 11/21/2025, 10:30:45 CST
Processing by UsersController#create as JSON
  Parameters: { name: 'John Doe', email: '[email protected]' }
  INSERT INTO "users" ("name", "email", "created_at") VALUES ($1, $2, $3) RETURNING "id" (2.5ms)  ['John Doe', '[email protected]', '2025-11-21T16:30:45.123Z']
Completed 201 Created in 25ms

Rails-Style Timing Breakdown

When enableTimingBreakdown: true (default), the logger automatically tracks and displays timing breakdown:

Started GET "/api/reports" for 127.0.0.1 at 11/21/2025, 10:30:45 CST
Processing by ReportsController#index as JSON
  SELECT * FROM reports WHERE user_id = $1 (45ms)  ['123']
  SELECT * FROM categories (15ms)
Completed 200 OK in 204ms (Logic: 144.0ms | DB: 60.0ms)

Breaking down the timing:

  • Total: 204ms (end-to-end request time)
  • DB: 60.0ms (cumulative time spent in database queries: 45ms + 15ms)
  • Logic: 144.0ms (application logic time: Total - DB = 204ms - 60ms)

This helps identify whether performance issues are due to slow queries or application logic.

Using the measure() Helper

Track custom operations within your request handlers:

import { measure } from 'express-enhanced-logger';

app.get('/api/report.pdf', async (req, res) => {
  const logger = getLogger();
  
  // Measure PDF generation
  const pdf = await measure('Rendering PDF', async () => {
    return await generatePDF(reportData);
  }, logger);
  
  res.send(pdf);
});

Output:

Started GET "/api/report.pdf" for 127.0.0.1 at 11/21/2025, 10:30:45 CST
Processing by /api/report.pdf as HTML
  Rendering PDF (Duration: 145.2ms)
Completed 200 OK in 152ms

The measure() helper works both inside and outside request contexts, and can track:

  • PDF/report generation
  • External API calls
  • Heavy computations
  • File I/O operations
  • Any async or sync operation

Complete Request Flow Example

import { createLogger, measure } from 'express-enhanced-logger';

const logger = createLogger({ enableTimingBreakdown: true });

app.get('/api/dashboard', async (req, res) => {
  // DB queries are automatically tracked via Prisma integration
  const users = await prisma.user.findMany();  // 25ms
  const posts = await prisma.post.findMany();  // 30ms
  
  // Custom operations can be measured
  const stats = await measure('Calculating statistics', async () => {
    return calculateDashboardStats(users, posts);
  }, logger);
  
  // External API call
  const weather = await measure('Fetching weather data', async () => {
    return await fetch('https://api.weather.com/...');
  }, logger);
  
  res.json({ users, posts, stats, weather });
});

Output:

Started GET "/api/dashboard" for 127.0.0.1 at 11/21/2025, 10:30:45 CST
Processing by /api/dashboard as JSON
  SELECT * FROM users (25ms)
  SELECT * FROM posts (30ms)
  Calculating statistics (Duration: 15.3ms)
  Fetching weather data (Duration: 234.7ms)
Completed 200 OK in 320ms (Logic: 265.0ms | DB: 55.0ms)

Timing breakdown:

  • Total: 320ms
  • DB: 55.0ms (Prisma queries: 25ms + 30ms)
  • Logic: 265.0ms (includes both measure() operations and other processing)

TypeScript Types

// Query log data for Prisma integration
interface QueryLogData {
  type: string;        // Query type (e.g., 'query', 'SELECT', 'INSERT')
  query: string;       // SQL query string
  params: string;      // JSON stringified parameters
  duration: string;    // Query duration (e.g., '50' or '50ms')
}

// Request log data (internal)
interface RequestLogData {
  timestamp: string;
  requestId?: string;
  method: string;
  url: string;
  status: number;
  statusText: string;
  duration: number;
  memoryUsed: string;
  userEmail?: string;
  body?: unknown;
  // ... additional fields
}

Express Type Extensions

The package extends Express types for better TypeScript support:

declare module 'express' {
  interface Request {
    requestId?: string;
    currentUser?: {
      email?: string;
      id?: string | number;
      [key: string]: unknown;
    };
  }
}

You can use these in your Express routes:

app.get('/api/profile', (req, res) => {
  // TypeScript knows about these properties
  console.log(req.requestId);
  console.log(req.currentUser);
});

📁 File Logging

Logs are automatically rotated and organized by date:

logs/
├── combined-2025-11-17.log      # All logs for today
├── error-2025-11-17.log         # Error logs only for today
├── combined-2025-11-16.log.gz   # Compressed logs from yesterday
└── error-2025-11-16.log.gz      # Compressed error logs from yesterday

Configuration:

const logger = createLogger({
  enableFileLogging: true,
  logsDirectory: 'logs',
  maxFileSize: '20m',      // Rotate when file reaches 20MB
  maxFiles: '7d',          // Keep logs for 7 days
  zippedArchive: true,     // Compress old logs
});

Log Format:

File logs use JSON format for easy parsing and analysis:

{
  "timestamp": "2025-11-17T10:30:45.123Z",
  "level": "info",
  "message": "GET /api/users 200 OK",
  "requestId": "req_abc123",
  "method": "GET",
  "url": "/api/users",
  "status": 200,
  "duration": 245,
  "userEmail": "[email protected]"
}

🔌 API Reference

Exported Functions

createLogger(config?: LoggerConfig): EnhancedLogger

Creates a new logger instance and sets it as the default logger.

const logger = createLogger({ level: 'debug' });

getLogger(): EnhancedLogger

Gets the default logger instance (creates one with default config if none exists).

const logger = getLogger();
logger.info('Using default logger');

requestLogger(config?: LoggerConfig): RequestHandler

Returns Express middleware for request logging.

app.use(requestLogger());

Convenience Logging Functions

Use the default logger directly without creating an instance:

import { error, warn, info, debug, query } from 'express-enhanced-logger';

info('Server started');
warn('Low disk space');
error('Database connection failed', { code: 'ECONNREFUSED' });
debug('Processing request', { requestId: '123' });
query({ type: 'query', query: 'SELECT ...', params: '[]', duration: '50' });

measure<T>(name, fn, logger?): Promise<T>

Measure the execution time of a synchronous or async function. Returns the result of the function.

import { measure, createLogger } from 'express-enhanced-logger';

const logger = createLogger();

// Measure async operations
const users = await measure('Fetching users from database', async () => {
  return await prisma.user.findMany();
}, logger);

// Measure sync operations
const result = measure('Heavy calculation', () => {
  return performComplexCalculation();
}, logger);

// Without logger (no logging, just execution)
const data = await measure('Silent operation', async () => {
  return await fetchData();
});

Parameters:

  • name (string) - Name of the operation being measured
  • fn (() => T | Promise) - Function to execute and measure
  • logger (optional) - Logger instance to log the operation. If omitted, the operation runs silently.

Returns: Promise - The result of executing the function

EnhancedLogger Class

Methods

  • error(message, meta?) - Log error message
  • warn(message, meta?) - Log warning message
  • info(message, meta?) - Log info message
  • debug(message, meta?) - Log debug message
  • query(data: QueryLogData, meta?) - Log SQL query with optional metadata
  • getWinstonLogger(): winston.Logger - Get underlying Winston instance
  • updateConfig(newConfig: Partial<LoggerConfig>) - Update configuration at runtime

Properties

  • requestLogger - Express middleware function for request logging

Example

import { EnhancedLogger } from 'express-enhanced-logger';

const logger = new EnhancedLogger({
  level: 'info',
  enableFileLogging: true,
});

logger.info('Application started');
logger.error('Something went wrong', { errorCode: 500 });

// Update config at runtime
logger.updateConfig({ level: 'debug' });

// Access Winston logger directly
const winston = logger.getWinstonLogger();
winston.log('custom', 'Custom log level');

🔄 Migration from Winston

If you're migrating from Winston, it's straightforward:

// Before (Winston)
import winston from 'winston';
const logger = winston.createLogger({
  level: 'info',
  transports: [
    new winston.transports.Console(),
    new winston.transports.File({ filename: 'app.log' })
  ]
});

// After (Enhanced Logger)
import { createLogger } from 'express-enhanced-logger';
const logger = createLogger({
  level: 'info',
  enableFileLogging: true,
  logsDirectory: 'logs'
});

// The logging methods remain the same
logger.info('message');
logger.error('error message', { context: 'additional data' });

// Plus you get additional features
app.use(logger.requestLogger); // Built-in request logging

🎯 Best Practices

1. Environment-Based Configuration

const logger = createLogger({
  level: process.env.LOG_LEVEL || 'info',
  enableFileLogging: process.env.NODE_ENV === 'production',
  enableColors: process.env.NODE_ENV !== 'production',
  slowRequestThreshold: Number(process.env.SLOW_REQUEST_THRESHOLD) || 1000,
});

2. Structured Logging

Always include context in your logs:

// Good ✅
logger.info('User login successful', {
  userId: user.id,
  ip: req.ip,
  timestamp: new Date().toISOString(),
});

// Less useful ❌
logger.info('Login successful');

3. Error Handling

Log errors with full context:

try {
  await processPayment(order);
} catch (error) {
  logger.error('Payment processing failed', {
    orderId: order.id,
    amount: order.total,
    error: error.message,
    stack: error.stack,
  });
  throw error;
}

4. Request Correlation

Use request IDs to trace requests through your system:

// Configure logger with custom request ID extraction
const logger = createLogger({
  getRequestId: (req) => {
    // Use existing ID or generate new one
    return req.headers['x-request-id'] as string || crypto.randomUUID();
  },
});

app.use(requestLogger());

// Then in your route handlers
app.get('/api/users/:id', async (req, res) => {
  logger.info('Fetching user', { 
    requestId: req.requestId,
    userId: req.params.id 
  });
});

5. Sensitive Data

Never log sensitive information:

// Bad ❌
logger.info('User login', { 
  email: user.email,
  password: user.password  // Never log passwords!
});

// Good ✅
logger.info('User login', {
  userId: user.id,
  email: user.email,
  // password is omitted
});

🤝 Contributing

We welcome contributions! Please see CONTRIBUTING.md for detailed guidelines.

Quick Start for Contributors:

# Clone and install
git clone https://github.com/Deetss/express-enhanced-logger.git
cd express-enhanced-logger
npm install

# Run tests
npm test

# Run tests with coverage
npm run test:coverage

# Build
npm run build

# Lint and format
npm run lint
npm run format

Current Test Coverage: 89% (229 passing tests)

🎮 Demo Application

Want to see all features in action? Check out the comprehensive demo application at examples/demo-app:

# Install dependencies
npm install

# Generate Prisma client and setup database
cd examples/demo-app
npx prisma generate
npx prisma migrate dev --name init

# Run the demo
cd ../..
npm run demo

The demo showcases:

  • ✨ All logging features (info, warn, error, debug)
  • 🚀 Request/response logging with performance tracking
  • 🗄️ Prisma integration with AsyncLocalStorage duration tracking
  • ⏱️ Custom timing with the measure() helper
  • 🎯 Controller helpers for Rails-style organization
  • 🔍 Error handling and context logging
  • And much more!

Visit http://localhost:3000 after starting the demo for a full route listing.

📖 Read the demo documentation for detailed setup instructions and usage examples.

📄 License

MIT License - see LICENSE file for details.

🙏 Acknowledgments

Built with:

📞 Support

🔗 Related Projects


Made with ❤️ by Dylan Dietz