motifer
v26.1.1
Published
Production-ready structured logging for Node.js & Express with automatic request ID tracking, log rotation, and Logstash compatibility
Maintainers
Keywords
Readme
🚀 Motifer
The Ultimate Structured Logging Solution for Node.js & Express
✨ Production-Ready | 🔍 Request Tracing | 📊 Logstash Compatible | 🎯 Zero Configuration
Features • Quick Start • Documentation • Examples • Contributing
🌟 Why Motifer?
Tired of scattered, inconsistent logs that make debugging a nightmare? Motifer transforms your logging experience with:
- 🎯 Automatic Request ID Tracking - Every request gets a unique UUID v4, making it trivial to trace requests across microservices
- 📋 Structured Log Patterns - Consistent, parseable logs that work seamlessly with Logstash, Elasticsearch, and CloudTrail
- 🚀 Zero Boilerplate - Set up in 30 seconds, works out of the box
- 🔄 Smart File Rotation - Automatic log rotation with compression and archival
- 🎨 Express.js Native - Built specifically for Express with automatic request/response logging
- 🔍 Microservice-Ready - Request ID chaining across service boundaries
- 📊 Elastic APM Integration - Built-in support for application performance monitoring
✨ Features
🎯 Core Capabilities
- ✅ Pattern Validation - Enforce consistent log patterns across your entire application
- ✅ Request/Response Logging - Automatic HTTP request and response logging with full context
- ✅ Unique Request IDs - UUID v4 tracking for complete request lifecycle visibility
- ✅ Multi-Level Logging - Support for info, debug, warn, error, and custom log levels
- ✅ File Rotation - Time-based and size-based log rotation with compression
- ✅ Multiple Appenders - Configure different log files for different log levels
- ✅ Logstash Compatible - Pre-configured patterns for seamless Logstash integration
- ✅ CloudTrail Support - Ready for AWS CloudTrail logging
- ✅ Elastic APM - Built-in integration for application performance monitoring
- ✅ Express Middleware - Automatic Express.js integration with zero configuration
🚀 Quick Start
Installation
npm install motiferBasic Usage (Express)
const express = require('express');
const bodyParser = require('body-parser');
const { ExpressLoggerFactory } = require('motifer');
const app = express();
app.use(bodyParser.json());
// Initialize Motifer (do this before your routes!)
const Logger = new ExpressLoggerFactory('my-app', 'debug', app);
// Use in your routes
const logger = Logger.getLogger(__filename);
app.get('/api/users', (req, res) => {
logger.info('Fetching users');
logger.debug('Query params:', req.query);
// Your business logic here
res.json({ users: [] });
});Basic Usage (Non-Express)
const { LoggerFactory } = require('motifer');
const Logger = new LoggerFactory('my-service', 'info');
const logger = Logger.getLogger(__filename);
logger.info('Service started successfully');
logger.error('Something went wrong', error);That's it! Your logs are now structured, traceable, and production-ready. 🎉
📅 Versioning
Motifer uses date-based versioning in the format YY.M.S:
- YY: Last two digits of the year (e.g., 25 for 2025)
- M: Month (1-12, no leading zeros)
- S: Sequence number for releases in that month (1, 2, 3, ...)
Examples:
26.1.1- First release in January 202626.1.2- Second release in January 202626.2.1- First release in February 2026
This makes it easy to identify when a version was released and ensures chronological ordering. For detailed version history, see CHANGELOG.md.
📖 Documentation
Table of Contents
- Installation
- Express Setup
- Non-Express Setup
- Configuration Options
- Log Patterns
- Advanced Features
- Examples
- API Reference
- Best Practices
- Troubleshooting
🎯 Express Setup
Step 1: Install Dependencies
npm install motifer express body-parserStep 2: Initialize Motifer
Important: Initialize Motifer after body-parser but before your routes.
const express = require('express');
const bodyParser = require('body-parser');
const { ExpressLoggerFactory } = require('motifer');
const app = express();
// 1. Configure body parser first
app.use(bodyParser.json());
// 2. Initialize Motifer
const Logger = new ExpressLoggerFactory(
'my-awesome-app', // Service name
'debug', // Log level
app, // Express instance
[/* options */] // Optional: file appenders
);
// 3. Use logger in your routes
const logger = Logger.getLogger(__filename);
app.get('/api/status', (req, res) => {
logger.info('Status check requested');
res.json({ status: 'ok' });
});
app.listen(3000, () => {
logger.info('Server started on port 3000');
});Request ID Chaining (Microservices)
Motifer automatically handles request ID propagation across microservices:
// Service A - Generates request ID
app.get('/api/users', (req, res) => {
// Request ID: 47de6d41-6dbd-44fc-9732-e28823755b58
logger.info('Fetching users');
// Forward to Service B with same request ID
axios.get('http://service-b/api/data', {
headers: { 'request-id': req.id }
});
});
// Service B - Receives and uses the same request ID
// All logs will have the same request ID, making tracing effortless!🔧 Non-Express Setup
For non-Express applications or background services:
const { LoggerFactory } = require('motifer');
// Simple setup
const Logger = new LoggerFactory('background-worker', 'info');
const logger = Logger.getLogger(__filename);
// With file logging
const options = [{
rotate: true,
filename: 'worker-%DATE%.log',
datePattern: 'YYYY-MM-DD',
dirname: './logs',
maxSize: '20m',
maxFiles: '14d'
}];
const Logger = new LoggerFactory('background-worker', 'info', options);
const logger = Logger.getLogger(__filename);
logger.info('Worker started');
logger.error('Processing failed', error);⚙️ Configuration Options
File Appender Options
const options = [{
// Basic options
filename: 'app-%DATE%.log', // Log filename (supports %DATE% placeholder)
dirname: './logs', // Directory for log files
level: 'info', // Log level for this appender
// Rotation options
rotate: true, // Enable file rotation
datePattern: 'YYYY-MM-DD', // Date pattern (moment.js format)
frequency: '1d', // Rotation frequency (e.g., '5m', '2h', '1d')
// Size and retention
maxSize: '20m', // Max file size (supports: b, kb, mb, gb)
maxFiles: '14d', // Retention period (e.g., '14d', '30', '100')
archived: true // Compress archived files
}];Multiple File Appenders
const options = [
{
level: 'error',
filename: 'errors.log',
dirname: './logs'
},
{
level: 'warn',
filename: 'warnings.log',
dirname: './logs'
},
{
rotate: true,
filename: 'app-%DATE%.log',
datePattern: 'YYYY-MM-DD',
dirname: './logs',
maxSize: '50m',
maxFiles: '30d'
}
];
const Logger = new ExpressLoggerFactory('my-app', 'debug', app, options);Date Pattern Examples
| Pattern | Description | Example Output |
|---------|-------------|----------------|
| YYYY-MM-DD | Daily rotation | 2024-01-15 |
| YYYY-MM-DD-HH | Hourly rotation | 2024-01-15-14 |
| YYYY-MM-DD-HHmm | Every 5 minutes | 2024-01-15-1430 |
| YYYY-MM | Monthly rotation | 2024-01 |
📋 Log Patterns
Motifer uses structured log patterns that are easy to parse and search.
Request Logs
TIMESTAMP [request] [REQUEST_ID] [APP_NAME] [LOG_LEVEL] [METHOD] [IP] [PATH] [BODY]Examples:
# GET request (no body)
2024-01-15T10:30:45.123Z [request] [47de6d41-6dbd-44fc-9732-e28823755b58] [my-app] [INFO] [GET] [::1] [/api/users?page=1] [{}]
# POST request with JSON body
2024-01-15T10:30:46.234Z [request] [a1b2c3d4-e5f6-7890-abcd-ef1234567890] [my-app] [INFO] [POST] [192.168.1.100] [/api/users] [{"name":"John Doe","email":"[email protected]","age":30}]
# PUT request with body
2024-01-15T10:30:47.345Z [request] [b2c3d4e5-f6a7-8901-bcde-f12345678901] [my-app] [INFO] [PUT] [10.0.0.1] [/api/users/123] [{"email":"[email protected]"}]Service Logs
TIMESTAMP [service] [REQUEST_ID] [APP_NAME] [LOG_LEVEL] [FILENAME] MESSAGEExample:
2024-01-15T10:30:45.125Z [service] [47de6d41-6dbd-44fc-9732-e28823755b58] [my-app] [INFO] [users.controller.js] Fetching users from databaseResponse Logs
TIMESTAMP [response] [REQUEST_ID] [APP_NAME] [LOG_LEVEL] [METHOD] [IP] [PATH] [STATUS] [SIZE] [TIME] [USER_AGENT]Example:
2024-01-15T10:30:45.250Z [response] [47de6d41-6dbd-44fc-9732-e28823755b58] [my-app] [INFO] [GET] [::1] [/api/users?page=1] [200] [1024] [125.5 ms] [Mozilla/5.0...]🎨 Advanced Features
Elastic APM Integration
const { ApmFactory } = require('motifer');
// Initialize APM (do this at the very start of your app)
ApmFactory({
serviceName: 'my-awesome-app',
apmServerUrl: 'https://apm-server.example.com',
secretToken: 'your-secret-token',
environment: 'production',
logLevel: 'error'
});
// Errors are automatically captured
logger.error('Something went wrong', error); // Automatically sent to APMStandard Log Levels
const logger = Logger.getLogger(__filename);
// Standard levels
logger.info('Information message');
logger.debug('Debug message');
logger.warn('Warning message');
logger.error('Error message');Logging with Context
// Simple message
logger.info('User created');
// Message with formatted arguments
logger.debug('Processing user:', { id: 123, name: 'John' });
// Error with stack trace
logger.error(error); // Automatically includes stack trace
// Multiple arguments
logger.info('User', user.id, 'performed action', action.type);📚 Examples
Complete Express Application
const express = require('express');
const bodyParser = require('body-parser');
const { ExpressLoggerFactory } = require('motifer');
const app = express();
app.use(bodyParser.json());
// Configure logger with file rotation
const logOptions = [{
rotate: true,
filename: 'app-%DATE%.log',
datePattern: 'YYYY-MM-DD',
dirname: './logs',
maxSize: '20m',
maxFiles: '14d',
level: 'info'
}, {
level: 'error',
filename: 'errors.log',
dirname: './logs'
}];
const Logger = new ExpressLoggerFactory('ecommerce-api', 'debug', app, logOptions);
const logger = Logger.getLogger(__filename);
// Routes
app.get('/api/products', async (req, res) => {
logger.info('Fetching products');
logger.debug('Query parameters:', req.query);
try {
const products = await fetchProducts(req.query);
logger.info(`Found ${products.length} products`);
res.json(products);
} catch (error) {
logger.error('Failed to fetch products', error);
res.status(500).json({ error: 'Internal server error' });
}
});
app.listen(3000, () => {
logger.info('Server started on port 3000');
});Background Service
const { LoggerFactory } = require('motifer');
const Logger = new LoggerFactory('email-worker', 'info', [{
rotate: true,
filename: 'worker-%DATE%.log',
datePattern: 'YYYY-MM-DD-HH',
dirname: './logs'
}]);
const logger = Logger.getLogger(__filename);
async function processEmails() {
logger.info('Starting email processing');
try {
const emails = await fetchEmails();
logger.debug(`Processing ${emails.length} emails`);
for (const email of emails) {
await sendEmail(email);
logger.info(`Email sent to ${email.to}`);
}
logger.info('Email processing completed');
} catch (error) {
logger.error('Email processing failed', error);
}
}
processEmails();📖 API Reference
ExpressLoggerFactory(service, level, express, options)
Creates a logger factory for Express.js applications.
Parameters:
service(string, required) - Application/service namelevel(string, required) - Log level (info, debug, warn, error, etc.)express(object, required) - Express application instanceoptions(array, optional) - File appender configuration
Returns: Logger factory object with getLogger(filename) method
LoggerFactory(service, level, options)
Creates a logger factory for non-Express applications.
Parameters:
service(string, required) - Application/service namelevel(string, optional) - Log level (default: 'info')options(array, optional) - File appender configuration
Returns: Logger factory object with getLogger(filename) method
ApmFactory(configObject)
Initializes Elastic APM integration.
Parameters:
configObject.serviceName(string, required) - Service nameconfigObject.apmServerUrl(string, required) - APM server URLconfigObject.secretToken(string, required) - APM secret tokenconfigObject.environment(string, optional) - Environment (default: 'production')configObject.logLevel(string, optional) - APM log level (default: 'error')
Logger Methods
logger.info(...args) // Log info message
logger.debug(...args) // Log debug message
logger.warn(...args) // Log warning message
logger.error(...args) // Log error message🎯 Best Practices
1. Initialize Early
// ✅ Good: Initialize at the start
const app = express();
app.use(bodyParser.json());
const Logger = new ExpressLoggerFactory('app', 'info', app);
// ❌ Bad: Initialize after routes
app.use('/api', routes);
const Logger = new ExpressLoggerFactory('app', 'info', app); // Too late!2. Use Appropriate Log Levels
// ✅ Good
logger.error('Database connection failed', error); // For errors
logger.warn('Rate limit approaching'); // For warnings
logger.info('User logged in'); // For important events
logger.debug('Processing request', data); // For debugging
// ❌ Bad
logger.info('Database connection failed', error); // Should be error
logger.error('User logged in'); // Should be info3. Include Context
// ✅ Good
logger.info('Order created', { orderId: order.id, userId: user.id });
logger.error('Payment failed', { orderId: order.id, error: error.message });
// ❌ Bad
logger.info('Order created'); // Missing context4. Use Request IDs
// ✅ Good: Request ID is automatically included in Express apps
app.get('/api/users', (req, res) => {
logger.info('Fetching users'); // Request ID automatically included
});
// For non-Express, you can manually track context5. Configure File Rotation
// ✅ Good: Proper rotation configuration
const options = [{
rotate: true,
filename: 'app-%DATE%.log',
datePattern: 'YYYY-MM-DD',
maxSize: '20m',
maxFiles: '14d'
}];
// ❌ Bad: No rotation (files will grow indefinitely)
const options = [{
filename: 'app.log' // No rotation!
}];🔍 Troubleshooting
Request ID is undefined
Problem: Request ID shows as null in logs.
Solution: Make sure you initialize ExpressLoggerFactory before your routes:
// ✅ Correct order
app.use(bodyParser.json());
const Logger = new ExpressLoggerFactory('app', 'info', app);
app.use('/api', routes);Logs not appearing in files
Problem: Console logs work but file logs don't appear.
Solution: Check file permissions and directory existence:
const options = [{
filename: 'app.log',
dirname: './logs' // Make sure this directory exists and is writable
}];Request body is empty in logs
Problem: Request body shows as {} in request logs.
Solution: Initialize body-parser before Motifer:
// ✅ Correct
app.use(bodyParser.json());
const Logger = new ExpressLoggerFactory('app', 'info', app);
// ❌ Wrong
const Logger = new ExpressLoggerFactory('app', 'info', app);
app.use(bodyParser.json());🤝 Contributing
We love contributions! See CONTRIBUTING.md for guidelines.
Quick start:
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
📄 License
This project is licensed under the Apache License 2.0 - see the LICENSE file for details.
🙏 Acknowledgments
- Inspired by the need for better structured logging in Node.js applications
- Built for developers who value clean, traceable, and production-ready logging
📊 Project Status
Active Development • Production Ready • Well Maintained
🌟 Star History
💬 Community
- 🐛 Found a bug? Open an issue
- 💡 Have a feature request? Open an issue
- 📖 Need help? Check the documentation or open a discussion
- 💬 Want to chat? Join our Discussions
Made with ❤️ by Ankur Mahajan
⭐ Star this repo if you find it helpful!
