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



A Rails-inspired Express.js logger with clean output, performance monitoring, SQL query formatting, and Prisma integration. Built on Winston with full TypeScript support.
- š 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)
``bash`
npm install express-enhanced-loggeror
yarn add express-enhanced-loggeror
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:
`javascript
// ES modules (import)
import { EnhancedLogger } from 'express-enhanced-logger';
// CommonJS (require)
const { EnhancedLogger } = require('express-enhanced-logger');
`
`typescript
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');
});
`
`typescript
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.
The logger produces clean, Rails-inspired output that's minimal and easy to read:
- ⨠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
``
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
For even cleaner logs that show controller actions, add metadata to your routes:
`typescript`
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
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]
`typescript
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());
`
`typescript
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
}
`
`typescript
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 });
`
`typescript
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 });
`
For even cleaner output without any formatting (just raw messages):
`typescript
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.
`typescript
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());
`
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:
`typescript
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:
`text`
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:
`typescript
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 eventsslowQueryThreshold
- Formats and logs queries with smart truncation
- Highlights slow queries based on
- Only activates in non-test environments
#### Using with Custom Logger Instance
If you're using a custom logger instance:
`typescript
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:
`typescript
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):
`typescript
// 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.logger.query()
- Queries not truncated: Make sure you're calling (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.
`typescript
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());
`
`typescript
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);
`
`typescript[${timestamp}] ${level.toUpperCase()}: ${JSON.stringify(message)}
const logger = createLogger({
customLogFormat: (info) => {
const { timestamp, level, message } = info;
return ;`
},
});
The logger automatically tracks request performance and memory usage:
`typescript
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
The logger produces clean, Rails-style output with no emojis or fancy formatting - just clear, readable logs:
``
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
``
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
``
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)
``
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
``
SELECT * FROM orders WHERE id IN (1,2,3,...47 more...,53,54,55) (120ms)
``
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
``
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: 'john@example.com' }
INSERT INTO "users" ("name", "email", "created_at") VALUES ($1, $2, $3) RETURNING "id" (2.5ms) ['John Doe', 'john@example.com', '2025-11-21T16:30:45.123Z']
Completed 201 Created in 25ms
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.
Track custom operations within your request handlers:
`typescript
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
`typescript
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
// 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
}
`
The package extends Express types for better TypeScript support:
`typescript`
declare module 'express' {
interface Request {
requestId?: string;
currentUser?: {
email?: string;
id?: string | number;
[key: string]: unknown;
};
}
}
You can use these in your Express routes:
`typescript`
app.get('/api/profile', (req, res) => {
// TypeScript knows about these properties
console.log(req.requestId);
console.log(req.currentUser);
});
Logs are automatically rotated and organized by date:
`text`
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:
`typescript`
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:
`json`
{
"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": "john@example.com"
}
#### createLogger(config?: LoggerConfig): EnhancedLogger
Creates a new logger instance and sets it as the default logger.
`typescript`
const logger = createLogger({ level: 'debug' });
#### getLogger(): EnhancedLogger
Gets the default logger instance (creates one with default config if none exists).
`typescript`
const logger = getLogger();
logger.info('Using default logger');
#### requestLogger(config?: LoggerConfig): RequestHandler
Returns Express middleware for request logging.
`typescript`
app.use(requestLogger());
#### Convenience Logging Functions
Use the default logger directly without creating an instance:
`typescript
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
Measure the execution time of a synchronous or async function. Returns the result of the function.
`typescript
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 measuredfn
- (() => T | Promiselogger
- (optional) - Logger instance to log the operation. If omitted, the operation runs silently.
Returns: Promise
#### 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
#### Properties
- requestLogger - Express middleware function for request logging
#### Example
`typescript
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');
`
If you're migrating from Winston, it's straightforward:
`typescript
// 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
`
`typescript`
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,
});
Always include context in your logs:
`typescript
// Good ā
logger.info('User login successful', {
userId: user.id,
ip: req.ip,
timestamp: new Date().toISOString(),
});
// Less useful ā
logger.info('Login successful');
`
Log errors with full context:
`typescript`
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;
}
Use request IDs to trace requests through your system:
`typescript
// 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
});
});
`
Never log sensitive information:
`typescript
// 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
});
`
We welcome contributions! Please see CONTRIBUTING.md for detailed guidelines.
Quick Start for Contributors:
`bashClone and install
git clone https://github.com/Deetss/express-enhanced-logger.git
cd express-enhanced-logger
npm install
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:`bash
Install dependencies
npm installGenerate Prisma client and setup database
cd examples/demo-app
npx prisma generate
npx prisma migrate dev --name initRun 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.
MIT License - see LICENSE file for details.
Built with:
- Winston - Logging framework
- Chalk - Terminal string styling
- winston-daily-rotate-file - Log rotation
- š Report bugs
- š” Request features
- š Documentation
- š¦ NPM Package
- express-winston - Alternative Express logging middleware
- morgan - HTTP request logger middleware
- pino - Fast JSON logger
---
Made with ā¤ļø by Dylan Dietz