LogStack Examples
Real-world examples and implementations for various use cases
๐ Basic Setup
Simple setup for small applications with local storage:
basic-setup.js
const { init, createDailyJobs, saveApiLog } = require('logstack');
async function basicSetup() {
const config = {
dbUri: 'mongodb://localhost:27017/basic-logs',
uploadProvider: 'local',
outputDirectory: './logs',
retention: {
dbRetentionDays: 7,
fileRetentionDays: 30
}
};
try {
await init(config);
await createDailyJobs();
console.log('โ
Basic LogStack setup complete!');
// Example: Save a log
await saveApiLog({
method: 'GET',
path: '/api/users',
statusCode: 200,
responseTime: 45,
userId: 'user123'
});
} catch (error) {
console.error('โ Setup failed:', error);
}
}
basicSetup();
Expected Output:
โ Database connected โ Local storage configured โ Daily jobs created (24 hourly tasks) โ API log saved successfully ๐ Files will be stored in: ./logs/
๐ญ Production Ready Implementation
Complete production setup with AWS S3, monitoring, and error handling:
production-implementation.js
const { init, createDailyJobs } = require('logstack');
const express = require('express');
class ProductionLogStack {
constructor() {
this.config = {
dbUri: process.env.MONGODB_URI,
uploadProvider: 's3',
// Daily folder structure with organized sub-folders
folderStructure: {
type: 'daily',
subFolders: {
enabled: true,
byHour: true,
byStatus: true,
custom: ['processed']
},
naming: {
prefix: 'production-logs',
dateFormat: 'YYYY-MM-DD',
includeTime: false
}
},
// Security settings
dataMasking: {
enabled: true,
maskPasswords: true,
maskEmails: true,
customFields: ['token', 'secret', 'authorization']
},
// Retention policies
retention: {
enabled: true,
dbRetentionDays: 14,
fileRetentionDays: 180,
cleanupIntervalHours: 24
},
// AWS S3 configuration
s3: {
accessKeyId: process.env.AWS_ACCESS_KEY_ID,
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
region: process.env.AWS_REGION || 'us-east-1',
bucket: process.env.S3_BUCKET,
serverSideEncryption: 'AES256'
},
// Production collections
collections: {
jobsCollectionName: 'production_jobs',
logsCollectionName: 'production_logs',
apiLogsCollectionName: 'production_api_logs'
}
};
}
async initialize() {
try {
console.log('๐ Initializing Production LogStack...');
// Validate environment
this.validateEnvironment();
// Initialize LogStack
await init(this.config);
await createDailyJobs();
// Setup health check endpoint
this.setupHealthCheck();
// Setup monitoring
this.setupMonitoring();
console.log('โ
Production LogStack initialized successfully!');
return true;
} catch (error) {
console.error('๐ฅ Production initialization failed:', error);
throw error;
}
}
validateEnvironment() {
const required = [
'MONGODB_URI',
'AWS_ACCESS_KEY_ID',
'AWS_SECRET_ACCESS_KEY',
'S3_BUCKET'
];
const missing = required.filter(env => !process.env[env]);
if (missing.length > 0) {
throw new Error(`Missing environment variables: ${missing.join(', ')}`);
}
}
setupHealthCheck() {
const app = express();
app.get('/health', async (req, res) => {
try {
// Check database connection
const dbStatus = await this.checkDatabase();
// Check S3 connectivity
const s3Status = await this.checkS3();
res.json({
status: 'healthy',
timestamp: new Date().toISOString(),
database: dbStatus,
storage: s3Status,
uptime: Math.floor(process.uptime()),
memory: Math.round(process.memoryUsage().heapUsed / 1024 / 1024)
});
} catch (error) {
res.status(503).json({
status: 'unhealthy',
error: error.message,
timestamp: new Date().toISOString()
});
}
});
app.listen(3000, () => {
console.log('๐ฅ Health check available at http://localhost:3000/health');
});
}
async checkDatabase() {
// Implementation for database health check
return 'connected';
}
async checkS3() {
// Implementation for S3 health check
return 'accessible';
}
setupMonitoring() {
// Log system metrics every 5 minutes
setInterval(() => {
const metrics = {
timestamp: new Date().toISOString(),
memory: Math.round(process.memoryUsage().heapUsed / 1024 / 1024),
uptime: Math.floor(process.uptime() / 60),
cpu: process.cpuUsage()
};
console.log('๐ System Metrics:', JSON.stringify(metrics));
}, 5 * 60 * 1000);
// Graceful shutdown
process.on('SIGTERM', () => {
console.log('๐ค Received SIGTERM, shutting down gracefully...');
process.exit(0);
});
}
}
// Initialize and start
async function start() {
const logStack = new ProductionLogStack();
await logStack.initialize();
}
if (require.main === module) {
start().catch(console.error);
}
module.exports = ProductionLogStack;
โ๏ธ Multi-Provider Setup
Configure multiple storage providers with fallback support:
multi-provider.js
const { init, createDailyJobs } = require('logstack');
async function multiProviderSetup() {
// Primary configuration with S3
const primaryConfig = {
dbUri: process.env.MONGODB_URI,
uploadProvider: 's3',
s3: {
accessKeyId: process.env.AWS_ACCESS_KEY_ID,
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
region: 'us-east-1',
bucket: 'primary-logs-bucket'
}
};
// Fallback configuration with local storage
const fallbackConfig = {
dbUri: process.env.MONGODB_URI,
uploadProvider: 'local',
outputDirectory: './fallback-logs'
};
// Secondary S3 backup configuration
const secondaryS3Config = {
dbUri: process.env.MONGODB_URI,
uploadProvider: 's3',
s3: {
accessKeyId: process.env.AWS_SECONDARY_ACCESS_KEY_ID,
secretAccessKey: process.env.AWS_SECONDARY_SECRET_ACCESS_KEY,
region: 'us-west-2',
bucket: 'secondary-logs-bucket'
}
};
try {
console.log('๐ Attempting primary provider (S3)...');
await init(primaryConfig);
console.log('โ
Primary provider initialized');
} catch (s3Error) {
console.log('โ ๏ธ Primary provider failed, trying secondary S3...');
try {
await init(secondaryS3Config);
console.log('โ
Secondary S3 provider initialized');
} catch (secondaryError) {
console.log('โ ๏ธ Secondary S3 failed, using local storage...');
await init(fallbackConfig);
console.log('โ
Local fallback provider initialized');
}
}
await createDailyJobs();
console.log('๐
Daily jobs created successfully');
}
// Provider-specific configurations
const providerConfigs = {
s3: {
uploadProvider: 's3',
s3: {
accessKeyId: process.env.AWS_ACCESS_KEY_ID,
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
region: 'us-west-2',
bucket: 'logs-s3-bucket',
storageClass: 'STANDARD_IA' // Cost-effective for logs
}
},
s3_backup: {
uploadProvider: 's3',
s3: {
accessKeyId: process.env.AWS_BACKUP_ACCESS_KEY_ID,
secretAccessKey: process.env.AWS_BACKUP_SECRET_ACCESS_KEY,
region: 'eu-west-1',
bucket: 'logs-backup-bucket',
storageClass: 'GLACIER' // Long-term storage
}
},
local: {
uploadProvider: 'local',
outputDirectory: './production-logs',
preserveLocalFiles: true,
compression: {
enabled: true,
format: 'gzip'
}
}
};
// Function to switch providers dynamically
async function switchProvider(providerName) {
const config = {
...providerConfigs[providerName],
dbUri: process.env.MONGODB_URI
};
try {
await init(config);
console.log(`โ
Switched to ${providerName} provider`);
return true;
} catch (error) {
console.error(`โ Failed to switch to ${providerName}:`, error.message);
return false;
}
}
// Command line interface
if (require.main === module) {
const provider = process.argv[2] || 'multi';
if (provider === 'multi') {
multiProviderSetup();
} else if (providerConfigs[provider]) {
switchProvider(provider);
} else {
console.log('Available providers: s3, s3_backup, local, multi');
}
}
module.exports = { multiProviderSetup, switchProvider, providerConfigs };
๐ Advanced Data Masking
Comprehensive data masking for sensitive information:
data-masking-example.js
const { init, saveApiLog } = require('logstack');
async function dataMaskingExample() {
const config = {
dbUri: 'mongodb://localhost:27017/secure-logs',
uploadProvider: 'local',
outputDirectory: './secure-logs',
dataMasking: {
enabled: true,
// Basic masking options
maskEmails: true,
maskIPs: false, // Keep IPs for debugging
maskPasswords: true,
showLastChars: 2, // Show last 2 characters for emails
maskingChar: '*',
// Custom field patterns
customFields: [
'token', 'secret', 'key', 'authorization',
'cookie', 'session', 'apiKey', 'accessToken'
],
// Advanced pattern matching
patterns: {
// Credit card numbers
creditCard: {
pattern: /\b\d{4}[\s-]?\d{4}[\s-]?\d{4}[\s-]?\d{4}\b/g,
replacement: '****-****-****-$1',
preserveLast: 4
},
// Phone numbers
phone: {
pattern: /(\+?\d{1,3}[\s-]?)?\(?\d{3}\)?[\s-]?\d{3}[\s-]?\d{4}/g,
replacement: '***-***-$1',
preserveLast: 4
},
// Social Security Numbers
ssn: {
pattern: /\b\d{3}-\d{2}-\d{4}\b/g,
replacement: '***-**-$1',
preserveLast: 4
},
// API Keys (various formats)
apiKey: {
pattern: /\b[A-Za-z0-9]{32,}\b/g,
replacement: '****...$1',
preserveLast: 6
}
},
// Context-aware masking
contextRules: {
// Mask differently based on user role
admin: {
showEmails: true,
showIPs: true,
maskPasswords: true
},
user: {
showEmails: false,
showIPs: false,
maskPasswords: true
}
}
}
};
await init(config);
// Test data with sensitive information
const testLogs = [
{
method: 'POST',
path: '/api/login',
statusCode: 200,
request: {
email: 'user@example.com',
password: 'mySecretPassword123',
rememberMe: true
},
response: {
token: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ',
user: {
id: '12345',
email: 'user@example.com',
phone: '+1-555-123-4567'
}
},
clientIP: '192.168.1.100',
userAgent: 'Mozilla/5.0...'
},
{
method: 'POST',
path: '/api/payment',
statusCode: 201,
request: {
amount: 99.99,
creditCard: '4532-1234-5678-9012',
cvv: '123',
billingAddress: {
name: 'John Doe',
phone: '555-123-4567',
ssn: '123-45-6789'
}
},
response: {
transactionId: 'tx_abc123xyz789',
status: 'completed'
}
}
];
// Save logs with automatic masking
for (const log of testLogs) {
await saveApiLog(log);
console.log('โ
Masked log saved:', log.path);
}
// Demonstrate different masking levels
await demonstrateMaskingLevels();
}
async function demonstrateMaskingLevels() {
console.log('\n๐ Masking Examples:\n');
const originalData = {
email: 'john.doe@company.com',
password: 'SuperSecretPassword123!',
creditCard: '4532-1234-5678-9012',
phone: '+1-555-123-4567',
ssn: '123-45-6789',
token: 'sk_live_abc123def456ghi789'
};
// Simulate different masking configurations
const maskingConfigs = [
{
name: 'Standard Masking',
showLastChars: 0,
description: 'Complete masking for maximum security'
},
{
name: 'Partial Masking',
showLastChars: 4,
description: 'Show last 4 characters for identification'
},
{
name: 'Development Mode',
showLastChars: 8,
description: 'More visible for debugging'
}
];
maskingConfigs.forEach(config => {
console.log(`\n${config.name} (${config.description}):`);
console.log('โ'.repeat(50));
// Simulate masking function
const masked = applyMasking(originalData, config.showLastChars);
Object.entries(masked).forEach(([key, value]) => {
console.log(`${key.padEnd(12)}: ${value}`);
});
});
}
function applyMasking(data, showLastChars) {
const mask = (str, chars = 0) => {
if (!str || str.length <= chars) return '*'.repeat(8);
return '*'.repeat(Math.max(str.length - chars, 4)) +
(chars > 0 ? str.slice(-chars) : '');
};
return {
email: showLastChars > 0 ? mask(data.email, 4) : '****@****.**',
password: mask(data.password, 0), // Never show password chars
creditCard: mask(data.creditCard, showLastChars),
phone: mask(data.phone, showLastChars),
ssn: mask(data.ssn, showLastChars),
token: mask(data.token, Math.min(showLastChars, 6))
};
}
// Environment-based masking
async function environmentBasedMasking() {
const isDevelopment = process.env.NODE_ENV === 'development';
const isProduction = process.env.NODE_ENV === 'production';
const config = {
dbUri: process.env.MONGODB_URI,
uploadProvider: 'local',
outputDirectory: './logs',
dataMasking: {
enabled: isProduction, // Only mask in production
// Different rules for different environments
...(isDevelopment && {
maskEmails: false,
maskIPs: false,
showLastChars: 8,
patterns: {} // Disable pattern matching in dev
}),
...(isProduction && {
maskEmails: true,
maskIPs: true,
showLastChars: 0,
customFields: [
'token', 'secret', 'key', 'authorization',
'cookie', 'session', 'apiKey', 'accessToken',
'password', 'pin', 'otp'
]
})
}
};
console.log(`๐ Environment: ${process.env.NODE_ENV}`);
console.log(`๐ Masking enabled: ${config.dataMasking.enabled}`);
await init(config);
}
if (require.main === module) {
const mode = process.argv[2];
switch(mode) {
case 'demo':
dataMaskingExample();
break;
case 'environment':
environmentBasedMasking();
break;
default:
dataMaskingExample();
}
}
module.exports = {
dataMaskingExample,
environmentBasedMasking,
demonstrateMaskingLevels
};
๐ Custom Folder Structure
Advanced folder organization patterns:
custom-folder-structure.js
const { init, createDailyJobs } = require('logstack');
async function customFolderExample() {
const configs = [
{
name: 'Microservice Architecture',
config: {
folderStructure: {
type: 'daily',
subFolders: {
enabled: true,
byService: true, // Custom: by microservice
byEnvironment: true, // Custom: by environment
byHour: true,
custom: ['api-gateway', 'user-service', 'payment-service']
},
naming: {
prefix: 'microservices',
dateFormat: 'YYYY-MM-DD',
includeEnvironment: true
}
}
},
expectedStructure: `
microservices_production_2025-09-02/
โโโ api-gateway/
โ โโโ hour-09-10/
โ โ โโโ success/
โ โ โโโ failed/
โ โโโ hour-10-11/
โโโ user-service/
โ โโโ hour-09-10/
โโโ payment-service/
โโโ hour-09-10/`
},
{
name: 'Multi-tenant SaaS',
config: {
folderStructure: {
type: 'monthly',
subFolders: {
enabled: true,
byTenant: true, // Custom: by tenant
byPlan: true, // Custom: by subscription plan
byRegion: true, // Custom: by geographic region
custom: ['enterprise', 'professional', 'starter']
},
naming: {
prefix: 'saas-logs',
dateFormat: 'YYYY-MM',
includeTenant: true
}
}
},
expectedStructure: `
saas-logs_2025-09/
โโโ tenant-acme-corp/
โ โโโ enterprise/
โ โ โโโ us-east/
โ โ โโโ eu-west/
โ โโโ professional/
โโโ tenant-startup-inc/
โ โโโ starter/
โโโ tenant-global-ltd/
โโโ enterprise/`
},
{
name: 'E-commerce Platform',
config: {
folderStructure: {
type: 'daily',
subFolders: {
enabled: true,
byModule: true, // Custom: by e-commerce module
byOperation: true, // Custom: by operation type
byStatus: true,
custom: ['orders', 'payments', 'inventory', 'users']
},
naming: {
prefix: 'ecommerce',
dateFormat: 'YYYY-MM-DD',
includeModule: true
}
}
},
expectedStructure: `
ecommerce_2025-09-02/
โโโ orders/
โ โโโ create/
โ โ โโโ success/
โ โ โโโ failed/
โ โโโ update/
โ โโโ cancel/
โโโ payments/
โ โโโ process/
โ โโโ refund/
โ โโโ dispute/
โโโ inventory/
โโโ check/
โโโ update/`
}
];
for (const example of configs) {
console.log(`\n๐ ${example.name} Structure:`);
console.log('โ'.repeat(50));
const config = {
dbUri: 'mongodb://localhost:27017/custom-folders',
uploadProvider: 'local',
outputDirectory: `./logs/${example.name.toLowerCase().replace(/\s+/g, '-')}`,
...example.config
};
try {
await init(config);
console.log('โ
Configuration applied successfully');
console.log('\n๐ Expected folder structure:');
console.log(example.expectedStructure);
} catch (error) {
console.error('โ Configuration failed:', error.message);
}
}
}
// Dynamic folder structure based on request data
async function dynamicFolderStructure() {
const config = {
dbUri: 'mongodb://localhost:27017/dynamic-folders',
uploadProvider: 'local',
outputDirectory: './logs/dynamic',
folderStructure: {
type: 'custom',
dynamicPattern: '${environment}/${service}/${date}/${hour}/${status}',
variables: {
environment: (req) => req.headers['x-environment'] || 'unknown',
service: (req) => req.headers['x-service'] || 'default',
date: () => new Date().toISOString().split('T')[0],
hour: () => new Date().getHours().toString().padStart(2, '0'),
status: (req, res) => res.statusCode >= 400 ? 'errors' : 'success'
}
},
// Custom naming functions
fileNaming: {
pattern: '${service}_${timestamp}_${requestId}.json',
variables: {
service: (req) => req.headers['x-service'] || 'api',
timestamp: () => Date.now(),
requestId: (req) => req.headers['x-request-id'] || 'unknown'
}
}
};
await init(config);
console.log('โ
Dynamic folder structure initialized');
// Simulate requests with different headers
const simulatedRequests = [
{
headers: { 'x-environment': 'production', 'x-service': 'user-api' },
path: '/api/users',
statusCode: 200
},
{
headers: { 'x-environment': 'staging', 'x-service': 'payment-api' },
path: '/api/payments',
statusCode: 500
}
];
for (const req of simulatedRequests) {
const folderPath = generateDynamicPath(config.folderStructure, req);
console.log(`๐ Request to ${req.path} โ ${folderPath}`);
}
}
function generateDynamicPath(structure, req, res = { statusCode: req.statusCode }) {
let path = structure.dynamicPattern;
for (const [variable, resolver] of Object.entries(structure.variables)) {
const value = typeof resolver === 'function' ? resolver(req, res) : resolver;
path = path.replace(`\${${variable}}`, value);
}
return path;
}
// Organized output with compression and rotation
async function organizedOutputExample() {
const config = {
dbUri: 'mongodb://localhost:27017/organized-logs',
uploadProvider: 'local',
outputDirectory: './logs/organized',
folderStructure: {
type: 'daily',
subFolders: {
enabled: true,
byHour: true,
byStatus: true,
bySize: true, // Split by file size
custom: ['raw', 'processed', 'archived']
},
rotation: {
enabled: true,
maxFileSize: '10MB', // Rotate when file exceeds 10MB
maxFilesPerHour: 5, // Maximum 5 files per hour
compressionDelay: 60 // Compress files after 60 minutes
}
},
compression: {
enabled: true,
format: 'gzip',
level: 6,
archiveOldFiles: true,
deleteAfterCompression: true
},
monitoring: {
enabled: true,
logFolderSizes: true,
alertOnLargeFiles: true,
maxFolderSize: '1GB'
}
};
await init(config);
console.log('โ
Organized output structure initialized');
// Display expected organization
console.log('\n๐ Organized structure with rotation:');
console.log(`
logs/organized/
โโโ logs_2025-09-02/
โ โโโ hour-09/
โ โ โโโ success/
โ โ โ โโโ raw/
โ โ โ โ โโโ api_logs_001.json
โ โ โ โ โโโ api_logs_002.json
โ โ โ โโโ processed/
โ โ โ โ โโโ api_logs_processed.json
โ โ โ โโโ archived/
โ โ โ โโโ api_logs_001.json.gz
โ โ โโโ failed/
โ โ โโโ raw/
โ โ โโโ processed/
โ โโโ hour-10/
โโโ logs_2025-09-01/ // Previous day
โ โโโ archived/ // All compressed
โโโ statistics/
โโโ folder_sizes.json
โโโ rotation_log.json`);
}
if (require.main === module) {
const mode = process.argv[2];
switch(mode) {
case 'custom':
customFolderExample();
break;
case 'dynamic':
dynamicFolderStructure();
break;
case 'organized':
organizedOutputExample();
break;
default:
customFolderExample();
}
}
module.exports = {
customFolderExample,
dynamicFolderStructure,
organizedOutputExample,
generateDynamicPath
};
๐ Express.js Integration
Complete integration with Express.js applications:
express-integration.js
const express = require('express');
const { init, saveApiLog, createDailyJobs } = require('logstack');
class ExpressLogStackIntegration {
constructor(config = {}) {
this.config = {
dbUri: process.env.MONGODB_URI || 'mongodb://localhost:27017/express-logs',
uploadProvider: 'local',
outputDirectory: './logs/express',
// Express-specific settings
dataMasking: {
enabled: true,
maskPasswords: true,
customFields: ['authorization', 'cookie', 'session']
},
// Filter settings
filters: {
skipRoutes: ['/health', '/metrics', '/favicon.ico'],
skipMethods: ['OPTIONS'],
skipStatusCodes: [], // Empty = log all status codes
onlyLogErrors: false // Set true to only log 4xx/5xx responses
},
...config
};
}
async initialize() {
await init(this.config);
await createDailyJobs();
console.log('โ
LogStack initialized for Express.js');
}
// Middleware factory
createMiddleware(options = {}) {
const {
includeRequestBody = false,
includeResponseBody = false,
includeHeaders = true,
maxBodySize = 10000, // 10KB limit for body logging
customExtractor = null
} = options;
return async (req, res, next) => {
const startTime = Date.now();
let requestBody = null;
let responseBody = null;
// Skip if route should be filtered
if (this.shouldSkip(req)) {
return next();
}
// Capture request body if needed
if (includeRequestBody && req.body) {
requestBody = JSON.stringify(req.body).length <= maxBodySize
? req.body
: '[Body too large]';
}
// Capture response body if needed
if (includeResponseBody) {
const originalSend = res.send;
res.send = function(body) {
responseBody = typeof body === 'string' && body.length <= maxBodySize
? body
: '[Response too large]';
return originalSend.call(this, body);
};
}
// Continue to next middleware
next();
// Log after response is sent
res.on('finish', async () => {
try {
const responseTime = Date.now() - startTime;
let logData = {
timestamp: new Date(),
method: req.method,
path: req.path,
query: req.query,
statusCode: res.statusCode,
responseTime,
userAgent: req.get('User-Agent'),
clientIP: req.ip || req.connection.remoteAddress,
referer: req.get('Referer')
};
// Add headers if requested
if (includeHeaders) {
logData.headers = this.sanitizeHeaders(req.headers);
}
// Add request body if captured
if (requestBody) {
logData.requestBody = requestBody;
}
// Add response body if captured
if (responseBody) {
logData.responseBody = responseBody;
}
// Add user info if available (from auth middleware)
if (req.user) {
logData.user = {
id: req.user.id,
email: req.user.email,
role: req.user.role
};
}
// Custom data extraction
if (customExtractor && typeof customExtractor === 'function') {
const customData = customExtractor(req, res);
logData = { ...logData, ...customData };
}
await saveApiLog(logData);
} catch (error) {
console.error('โ Failed to log request:', error);
}
});
};
}
shouldSkip(req) {
const { skipRoutes, skipMethods, onlyLogErrors } = this.config.filters;
// Skip specific routes
if (skipRoutes.includes(req.path)) {
return true;
}
// Skip specific methods
if (skipMethods.includes(req.method)) {
return true;
}
return false;
}
sanitizeHeaders(headers) {
const sanitized = { ...headers };
// Remove sensitive headers
const sensitiveHeaders = [
'authorization',
'cookie',
'x-api-key',
'x-auth-token'
];
sensitiveHeaders.forEach(header => {
if (sanitized[header]) {
sanitized[header] = '[REDACTED]';
}
});
return sanitized;
}
// Error logging middleware
createErrorMiddleware() {
return async (error, req, res, next) => {
try {
const errorLog = {
timestamp: new Date(),
level: 'error',
method: req.method,
path: req.path,
statusCode: res.statusCode || 500,
error: {
name: error.name,
message: error.message,
stack: error.stack
},
user: req.user ? { id: req.user.id } : null,
clientIP: req.ip
};
await saveApiLog(errorLog);
} catch (logError) {
console.error('โ Failed to log error:', logError);
}
next(error);
};
}
// Performance monitoring middleware
createPerformanceMiddleware() {
return (req, res, next) => {
const startTime = process.hrtime.bigint();
res.on('finish', async () => {
const endTime = process.hrtime.bigint();
const responseTime = Number(endTime - startTime) / 1000000; // Convert to ms
// Log slow requests (> 1 second)
if (responseTime > 1000) {
await saveApiLog({
timestamp: new Date(),
level: 'warning',
type: 'slow_request',
method: req.method,
path: req.path,
responseTime,
statusCode: res.statusCode,
message: `Slow request detected: ${responseTime}ms`
});
}
});
next();
};
}
}
// Complete Express.js application example
async function createExpressApp() {
const app = express();
// Initialize LogStack
const logStack = new ExpressLogStackIntegration({
uploadProvider: 'local',
outputDirectory: './logs/express-app'
});
await logStack.initialize();
// Basic middleware
app.use(express.json({ limit: '10mb' }));
app.use(express.urlencoded({ extended: true }));
// LogStack middlewares
app.use(logStack.createMiddleware({
includeRequestBody: true,
includeResponseBody: false,
includeHeaders: true
}));
app.use(logStack.createPerformanceMiddleware());
// Health check endpoint (excluded from logging)
app.get('/health', (req, res) => {
res.json({ status: 'healthy', timestamp: new Date() });
});
// Sample API routes
app.get('/api/users', async (req, res) => {
// Simulate database query
await new Promise(resolve => setTimeout(resolve, 100));
res.json({
users: [
{ id: 1, name: 'John Doe', email: 'john@example.com' },
{ id: 2, name: 'Jane Smith', email: 'jane@example.com' }
]
});
});
app.post('/api/users', async (req, res) => {
const { name, email, password } = req.body;
// Simulate user creation
if (!name || !email) {
return res.status(400).json({ error: 'Name and email required' });
}
res.status(201).json({
id: Date.now(),
name,
email,
created: new Date()
});
});
app.post('/api/login', async (req, res) => {
const { email, password } = req.body;
// Simulate authentication
if (email === 'admin@example.com' && password === 'admin123') {
res.json({
token: 'jwt_token_here',
user: { id: 1, email, role: 'admin' }
});
} else {
res.status(401).json({ error: 'Invalid credentials' });
}
});
// Slow endpoint for performance testing
app.get('/api/slow', async (req, res) => {
await new Promise(resolve => setTimeout(resolve, 2000)); // 2 second delay
res.json({ message: 'This was slow!' });
});
// Error endpoint for testing error logging
app.get('/api/error', (req, res) => {
throw new Error('Test error for logging');
});
// Error handling middleware (must be last)
app.use(logStack.createErrorMiddleware());
app.use((error, req, res, next) => {
res.status(500).json({ error: 'Internal server error' });
});
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`๐ Express server running on port ${PORT}`);
console.log('๐ LogStack integration active');
console.log('\n๐งช Test endpoints:');
console.log(` GET http://localhost:${PORT}/api/users`);
console.log(` POST http://localhost:${PORT}/api/users`);
console.log(` POST http://localhost:${PORT}/api/login`);
console.log(` GET http://localhost:${PORT}/api/slow`);
console.log(` GET http://localhost:${PORT}/api/error`);
});
return app;
}
if (require.main === module) {
createExpressApp().catch(console.error);
}
module.exports = {
ExpressLogStackIntegration,
createExpressApp
};