@rexp/logger
v2.1.0
Published
A comprehensive Winston-based logger with request correlation, data redaction, structured logging helpers, and Express middleware for Node.js microservices
Readme
@rexp/logger
A comprehensive Winston-based logger with request correlation, data redaction, structured logging helpers, and Express middleware for Node.js microservices.
📋 Table of Contents
✨ Features
- 🚀 Winston-based - Built on the industry-standard Winston logger
- 🔗 Request Correlation - Automatic correlation ID tracking across requests
- 🔒 Auto Redaction - Sensitive data automatically redacted from logs
- 🎨 Colorized Output - Beautiful console logs in development
- 📝 Structured Helpers - Pre-built helpers for common logging scenarios
- 🌐 Cloud-Ready - EKS/Kubernetes-compatible IP extraction
- 🔐 Specialized Loggers - Dedicated security and audit loggers
- 📦 Express Middleware - Drop-in middleware for Express apps
- 🔄 File Rotation - Optional daily log rotation with compression
- 💪 Error Resilient - Fallback mechanisms prevent logging failures
- 🎯 Zero Config - Works out of the box with sensible defaults
🚀 Quick Start
npm install @rexp/loggerconst express = require("express");
const logger = require("@rexp/logger");
const { requestLogger } = require("@rexp/logger/src/middleware");
const app = express();
app.use(requestLogger());
app.get("/", (req, res) => {
logger.info("Hello from @rexp/logger!");
res.json({ message: "Hello, World!" });
});
app.listen(3000, () => {
logger.info("Server running on port 3000");
});📦 Installation
Basic Installation (Console Logging Only)
npm install @rexp/loggerWith File Rotation Support
npm install @rexp/logger winston-daily-rotate-fileFor CI/CD Pipelines
# Install with optional dependencies
npm ci --include=optional
# Install without optional dependencies (console only)
npm ci --omit=optional⚙️ Configuration
Configure the logger using environment variables:
Core Configuration
| Variable | Default | Description |
| ----------------- | ----------------------------- | --------------------------------------------------- |
| SERVICE_NAME | unknown-service | Your service/application name |
| SERVICE_VERSION | 0.0.0 | Your service version |
| NODE_ENV | development | Environment (development/production) |
| LOG_LEVEL | debug (dev) / info (prod) | Log level: silly, debug, verbose, info, warn, error |
| LOG_PRETTY | false | Enable pretty-printed logs (good for development) |
Data Redaction
| Variable | Default | Description |
| ------------------- | ------- | ------------------------------------------------------ |
| LOG_REDACT_FIELDS | - | Comma-separated fields to redact (e.g., email,phone) |
Default redacted fields: password, pass, token, otp, secret, ssn
File Logging (Requires winston-daily-rotate-file)
| Variable | Default | Description |
| ------------------ | -------- | --------------------------------- |
| LOG_FILE_ENABLED | false | Enable file logging |
| LOG_DIR | ./logs | Directory for log files |
| LOG_MAX_SIZE | 20m | Max file size before rotation |
| LOG_MAX_FILES | 14d | Retention period (e.g., 14d, 30d) |
| LOG_ZIP_ARCHIVES | true | Compress rotated log files |
Example .env File
# Service Info
SERVICE_NAME=auth-service
SERVICE_VERSION=1.2.0
NODE_ENV=production
# Logging
LOG_LEVEL=info
LOG_PRETTY=false
LOG_REDACT_FIELDS=email,phone,creditCard
# File Logging
LOG_FILE_ENABLED=true
LOG_DIR=/var/log/app
LOG_MAX_SIZE=50m
LOG_MAX_FILES=30d
LOG_ZIP_ARCHIVES=true📖 Usage Guide
Basic Logging
const logger = require("@rexp/logger");
// Different log levels
logger.silly("Very detailed debug info");
logger.debug("Debug information");
logger.verbose("Verbose information");
logger.info("General information");
logger.warn("Warning messages");
logger.error("Error messages");
// With metadata
logger.info("User logged in", {
userId: "user123",
ip: "192.168.1.1",
timestamp: new Date().toISOString(),
});
// Error with stack trace
try {
throw new Error("Something went wrong");
} catch (error) {
logger.error("An error occurred", {
error: error.message,
stack: error.stack,
});
}Express Middleware
Request Logger Middleware
Automatically logs all incoming requests with correlation IDs:
const express = require("express");
const { requestLogger } = require("@rexp/logger/src/middleware");
const app = express();
app.use(requestLogger());
app.get("/api/users", (req, res) => {
// All logs in this request will include the correlation ID
res.json({ users: [] });
});HTTP Request Logging
const logger = require("@rexp/logger");
logger.http(req, res, { customField: "value" });
// Automatically logs with appropriate level based on status codeStructured Logging Helpers
Pre-built functions for common logging scenarios:
🔐 Authentication Logging
const { logAuth } = require("@rexp/logger");
// Successful login
logAuth(
"user_login",
userId,
{
success: true,
ip: "192.168.1.1",
userAgent: req.get("User-Agent"),
},
req
);
// Failed login
logAuth(
"login_failed",
userId,
{
success: false,
reason: "Invalid password",
ip: "192.168.1.1",
},
req
);🛡️ Authorization Logging
const { logAuthz } = require("@rexp/logger");
logAuthz(
"resource_access",
userId, // User ID
"/api/admin/users", // Resource
"read", // Action
true, // Granted (true/false)
{ ip: "192.168.1.1" }, // Additional details
req // Request object (optional)
);🚨 Security Incident Logging
const { logSecurityIncident } = require("@rexp/logger");
logSecurityIncident(
"brute_force_attempt", // Incident type
"high", // Severity: low, medium, high, critical
"Multiple failed login attempts", // Description
{
userId: "user123",
ip: "192.168.1.1",
attemptCount: 5,
}
);📊 Audit Logging
const { logAudit } = require("@rexp/logger");
logAudit(
"user_update", // Action
adminUserId, // Who performed the action
"users/user123", // Resource affected
{
oldValue: { email: "[email protected]" },
newValue: { email: "[email protected]" },
ip: "192.168.1.1",
success: true,
}
);🌐 API Request Logging
const { logApiRequest } = require("@rexp/logger");
logApiRequest({
method: "POST",
url: "/api/users",
statusCode: 201,
responseTime: 45, // milliseconds
userId: userId,
req: req, // For automatic IP extraction
});💾 Database Operation Logging
const { logDbOperation } = require("@rexp/logger");
logDbOperation(
"INSERT", // Operation: INSERT, UPDATE, DELETE, SELECT
"users", // Table name
userId, // User ID
{
recordsAffected: 1,
query: "INSERT INTO users...",
success: true,
}
);📁 File Operation Logging
const { logFileOperation } = require("@rexp/logger");
logFileOperation(
"upload", // Operation: upload, download, delete
"document.pdf", // Filename
userId, // User ID
{
fileSize: 1024000,
mimeType: "application/pdf",
path: "/uploads/document.pdf",
success: true,
}
);🖥️ System Event Logging
const { logSystemEvent } = require("@rexp/logger");
logSystemEvent(
"database_connected", // Event type
"database", // Component
{
message: "Connected to PostgreSQL",
data: { host: "localhost", port: 5432 },
}
);🌍 HTTP Request Logging with Timing
const { logHttpRequest } = require("@rexp/logger");
app.use((req, res, next) => {
const start = Date.now();
res.on("finish", () => {
const responseTime = Date.now() - start;
logHttpRequest(req, res, responseTime);
});
next();
});Specialized Loggers
Dedicated loggers for specific use cases:
const { securityLogger, auditLogger } = require("@rexp/logger");
// Security events (automatically categorized)
securityLogger.warn("Suspicious activity detected", {
userId: "user123",
ip: "192.168.1.1",
pattern: "rapid_requests",
});
// Audit events (automatically categorized)
auditLogger.info("Configuration changed", {
userId: "admin123",
setting: "max_upload_size",
oldValue: "10MB",
newValue: "50MB",
});Request Correlation
Correlation IDs automatically track requests across your application:
const { runWithContext } = require("@rexp/logger/src/context");
// Manual context creation
runWithContext({ correlationId: "custom-id", userId: "user123" }, () => {
logger.info("This log will include the correlation ID");
// All logs within this context will include correlationId and userId
});
// The requestLogger middleware handles this automatically for Express appsData Redaction
Sensitive fields are automatically redacted:
logger.info("User data", {
username: "john_doe",
password: "secret123", // Will be redacted
email: "[email protected]",
token: "abc123", // Will be redacted
});
// Output:
// {
// username: 'john_doe',
// password: '***REDACTED***',
// email: '[email protected]',
// token: '***REDACTED***'
// }Add custom redaction fields:
LOG_REDACT_FIELDS=email,phone,ssn,creditCardChild Loggers
Create child loggers with persistent context:
const logger = require("@rexp/logger");
// Create child logger with context
const userLogger = logger.child({
userId: "user123",
module: "user-service",
});
userLogger.info("User action performed");
// All logs will include userId and module
const orderLogger = logger.child({
orderId: "order456",
module: "order-service",
});
orderLogger.info("Order created");
// All logs will include orderId and moduleIP Extraction (Cloud-Ready)
Extract real client IP from requests (handles load balancers, proxies, EKS, Kubernetes):
const { extractClientIP } = require("@rexp/logger");
app.use((req, res, next) => {
const clientIP = extractClientIP(req);
console.log("Real client IP:", clientIP);
// Automatically handles headers from:
// - x-forwarded-for (most common)
// - x-real-ip (Nginx)
// - x-client-ip (Apache)
// - cf-connecting-ip (Cloudflare)
// - x-cluster-client-ip (Kubernetes)
// - and more...
next();
});File Logging & Rotation
Enable file logging with automatic rotation:
# .env file
LOG_FILE_ENABLED=true
LOG_DIR=/var/log/myapp
LOG_MAX_SIZE=50m
LOG_MAX_FILES=30d
LOG_ZIP_ARCHIVES=true# Install rotation package
npm install winston-daily-rotate-fileLog files will be organized like this:
/var/log/myapp/
├── application-2025-10-14.log # Today's log
├── application-2025-10-13.log.gz # Yesterday (compressed)
├── application-2025-10-12.log.gz
└── ...Features:
- ✅ Daily rotation (new file each day)
- ✅ Size-based rotation (when file exceeds max size)
- ✅ Automatic compression (.gz)
- ✅ Retention policy (auto-delete old logs)
- ✅ Event monitoring (rotate, archive, logRemoved)
📚 API Reference
Core Logger Methods
| Method | Description |
| -------------------------------- | ---------------------------------- |
| logger.silly(message, meta?) | Silly level logs (lowest priority) |
| logger.debug(message, meta?) | Debug information |
| logger.verbose(message, meta?) | Verbose information |
| logger.info(message, meta?) | General information |
| logger.warn(message, meta?) | Warning messages |
| logger.error(message, meta?) | Error messages (highest priority) |
| logger.child(fields) | Create child logger with context |
| logger.http(req, res, extra?) | Log HTTP request/response |
Structured Logging Helpers
| Function | Parameters | Description |
| ------------------------------------------------------------------- | ---------------------------------------------------------- | ----------------------------- |
| logAuth(event, userId, details, req?) | event, userId, details, request | Log authentication events |
| logAuthz(event, userId, resource, action, granted, details, req?) | event, userId, resource, action, granted, details, request | Log authorization events |
| logSecurityIncident(type, severity, description, details) | type, severity, description, details | Log security incidents |
| logAudit(action, userId, resource, details) | action, userId, resource, details | Log audit trail |
| logApiRequest(options) | options object | Log API requests |
| logDbOperation(operation, table, userId, details) | operation, table, userId, details | Log database operations |
| logFileOperation(operation, filename, userId, details) | operation, filename, userId, details | Log file operations |
| logSystemEvent(event, component, details) | event, component, details | Log system events |
| logHttpRequest(req, res, responseTime) | request, response, responseTime | Log HTTP requests with timing |
Utilities
| Function | Parameters | Returns | Description |
| ----------------------------- | ----------------- | ---------- | -------------------------------------- |
| extractClientIP(req) | request | string | Extract real client IP from request |
| requestLogger() | - | middleware | Express middleware for request logging |
| runWithContext(context, fn) | context, function | any | Run function within logging context |
| getContext() | - | object | Get current logging context |
Specialized Loggers
| Logger | Description |
| ---------------- | -------------------------------------- |
| securityLogger | Logger with security category |
| auditLogger | Logger with audit category |
| logHelpers | Object containing all helper functions |
💡 Examples
Complete Express App Example
const express = require("express");
const logger = require("@rexp/logger");
const { requestLogger } = require("@rexp/logger/src/middleware");
const { logAuth, logSecurityIncident, extractClientIP } = logger;
const app = express();
app.use(express.json());
app.use(requestLogger());
// Login endpoint with authentication logging
app.post("/login", (req, res) => {
const { username, password } = req.body;
if (password === "correct") {
logAuth(
"user_login",
username,
{
success: true,
userAgent: req.get("User-Agent"),
},
req
);
res.json({ success: true, token: "jwt-token" });
} else {
logAuth(
"login_failed",
username,
{
success: false,
reason: "Invalid password",
},
req
);
logSecurityIncident(
"failed_login_attempt",
"medium",
"Failed login detected",
{
username,
ip: extractClientIP(req),
}
);
res.status(401).json({ error: "Invalid credentials" });
}
});
// Protected route with authorization
app.get("/admin/users", (req, res) => {
const userId = req.user?.id || "anonymous";
const isAdmin = req.user?.role === "admin";
logger.logAuthz(
"resource_access",
userId,
"/admin/users",
"read",
isAdmin,
{},
req
);
if (!isAdmin) {
return res.status(403).json({ error: "Forbidden" });
}
res.json({ users: [] });
});
// Error handling
app.use((err, req, res, next) => {
logger.error("Application error", {
error: err.message,
stack: err.stack,
url: req.url,
method: req.method,
});
res.status(500).json({ error: "Internal server error" });
});
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
logger.logSystemEvent("server_started", "api-server", {
message: `Server started on port ${PORT}`,
data: { port: PORT, env: process.env.NODE_ENV },
});
});Morgan Integration
const morgan = require("morgan");
const logger = require("@rexp/logger");
// Use logger.stream with Morgan
app.use(morgan("combined", { stream: logger.stream }));Microservice Example
// service-a/index.js
const logger = require("@rexp/logger");
async function processOrder(orderId) {
const orderLogger = logger.child({ orderId, service: "order-processor" });
orderLogger.info("Processing order");
try {
// Business logic
orderLogger.debug("Validating order");
orderLogger.logDbOperation("SELECT", "orders", "system", {
recordsAffected: 1,
success: true,
});
orderLogger.info("Order processed successfully");
} catch (error) {
orderLogger.error("Order processing failed", {
error: error.message,
stack: error.stack,
});
throw error;
}
}🔧 Troubleshooting
Issue: File logging not working
Solution: Ensure winston-daily-rotate-file is installed:
npm install winston-daily-rotate-fileAnd set the environment variable:
LOG_FILE_ENABLED=trueIssue: Logs not appearing
Solution: Check your LOG_LEVEL setting. If set to error, only error logs will appear.
# Show all logs
LOG_LEVEL=debug
# Production (info and above)
LOG_LEVEL=infoIssue: Correlation ID not appearing
Solution: Use the requestLogger() middleware:
const { requestLogger } = require("@rexp/logger/src/middleware");
app.use(requestLogger());Issue: Sensitive data appearing in logs
Solution: Add fields to redaction list:
LOG_REDACT_FIELDS=email,phone,customFieldIssue: Wrong IP address in logs
Solution: Use the extractClientIP function which handles load balancers and proxies:
const { extractClientIP } = require("@rexp/logger");
const clientIP = extractClientIP(req);🤝 Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add some amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
📄 License
This project is licensed under the MIT License - see the LICENSE file for details.
🔗 Links
Made with ❤️ for the Node.js community
