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)
npm install express-enhanced-logger
# or
yarn add express-enhanced-logger
# or
pnpm add express-enhanced-loggerRequirements:
- 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');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');
});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:
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]
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());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>;
}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 });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):
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 CSTNote: Simple logging mode disables Rails-style request formatting and SQL query formatting.
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.
simpleLogging: true. Make sure to set simpleLogging: false (or omit it, as false is the default) when using logger.query().
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.
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, anderrorevents - Formats and logs queries with smart truncation
- Highlights slow queries based on
slowQueryThreshold - Only activates in non-test environments
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);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: trueenabled. Set it tofalse. - Queries not truncated: Make sure you're calling
logger.query()(notlogger.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.
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());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);const logger = createLogger({
customLogFormat: (info) => {
const { timestamp, level, message } = info;
return `[${timestamp}] ${level.toUpperCase()}: ${JSON.stringify(message)}`;
},
});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
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:
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
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)
// 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:
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);
});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": "john@example.com"
}Creates a new logger instance and sets it as the default logger.
const logger = createLogger({ level: 'debug' });Gets the default logger instance (creates one with default config if none exists).
const logger = getLogger();
logger.info('Using default logger');Returns Express middleware for request logging.
app.use(requestLogger());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 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 measuredfn(() => T | Promise) - Function to execute and measurelogger(optional) - Logger instance to log the operation. If omitted, the operation runs silently.
Returns: Promise - The result of executing the function
error(message, meta?)- Log error messagewarn(message, meta?)- Log warning messageinfo(message, meta?)- Log info messagedebug(message, meta?)- Log debug messagequery(data: QueryLogData, meta?)- Log SQL query with optional metadatagetWinstonLogger(): winston.Logger- Get underlying Winston instanceupdateConfig(newConfig: Partial<LoggerConfig>)- Update configuration at runtime
requestLogger- Express middleware function for request logging
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:
// 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 loggingconst 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:
// 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:
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:
// 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:
// 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:
# 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 formatCurrent Test Coverage: 89% (229 passing tests)
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 demoThe 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