aethercall
Version:
A scalable WebRTC video calling API built with Node.js and OpenVidu
293 lines (256 loc) • 8.87 kB
JavaScript
/**
* Abstracted Logging Utility
* Provides structured logging with different transports
*/
const config = require('./config');
class Logger {
constructor() {
this.logLevel = config.logLevel;
this.levels = {
error: 0,
warn: 1,
info: 2,
debug: 3
};
this.colors = {
error: '\x1b[31m', // Red
warn: '\x1b[33m', // Yellow
info: '\x1b[36m', // Cyan
debug: '\x1b[90m', // Gray
reset: '\x1b[0m'
};
}
/**
* Check if message should be logged based on level
* @param {string} level - Log level
* @returns {boolean}
*/
shouldLog(level) {
return this.levels[level] <= this.levels[this.logLevel];
}
/**
* Format log message
* @param {string} level - Log level
* @param {string} message - Log message
* @param {Object} meta - Additional metadata
* @returns {string}
*/
formatMessage(level, message, meta = {}) {
const timestamp = new Date().toISOString();
const processId = process.pid;
let logEntry = {
timestamp,
level: level.toUpperCase(),
pid: processId,
message
};
// Add metadata if provided
if (Object.keys(meta).length > 0) {
logEntry.meta = meta;
}
// In development, use colored console output
if (config.isDevelopment()) {
const color = this.colors[level] || '';
const reset = this.colors.reset;
return `${color}[${timestamp}] ${level.toUpperCase()} (${processId}): ${message}${reset}${
Object.keys(meta).length > 0 ? '\n ' + JSON.stringify(meta, null, 2) : ''
}`;
}
// In production, use JSON format
return JSON.stringify(logEntry);
}
/**
* Log error message
* @param {string} message - Error message
* @param {Object|Error} meta - Error object or metadata
*/
error(message, meta = {}) {
if (!this.shouldLog('error')) return;
// Handle Error objects
if (meta instanceof Error) {
meta = {
error: meta.message,
stack: meta.stack,
name: meta.name
};
}
const formatted = this.formatMessage('error', message, meta);
console.error(formatted);
// In production, you might want to send to external logging service
if (config.isProduction()) {
this.sendToExternalService('error', message, meta);
}
}
/**
* Log warning message
* @param {string} message - Warning message
* @param {Object} meta - Additional metadata
*/
warn(message, meta = {}) {
if (!this.shouldLog('warn')) return;
const formatted = this.formatMessage('warn', message, meta);
console.warn(formatted);
}
/**
* Log info message
* @param {string} message - Info message
* @param {Object} meta - Additional metadata
*/
info(message, meta = {}) {
if (!this.shouldLog('info')) return;
const formatted = this.formatMessage('info', message, meta);
console.log(formatted);
}
/**
* Log debug message
* @param {string} message - Debug message
* @param {Object} meta - Additional metadata
*/
debug(message, meta = {}) {
if (!this.shouldLog('debug')) return;
const formatted = this.formatMessage('debug', message, meta);
console.log(formatted);
}
/**
* Log HTTP request
* @param {Object} req - Express request object
* @param {Object} res - Express response object
* @param {number} duration - Request duration in ms
*/
logRequest(req, res, duration) {
const meta = {
method: req.method,
url: req.originalUrl,
status: res.statusCode,
duration: `${duration}ms`,
userAgent: req.get('User-Agent'),
ip: req.ip || req.connection.remoteAddress,
requestId: req.context?.requestId
};
const level = res.statusCode >= 400 ? 'warn' : 'info';
const message = `${req.method} ${req.originalUrl} ${res.statusCode} - ${duration}ms`;
this[level](message, meta);
}
/**
* Log session events
* @param {string} event - Event type
* @param {string} sessionId - Session ID
* @param {Object} data - Event data
*/
logSession(event, sessionId, data = {}) {
this.info(`Session ${event}`, {
event,
sessionId,
...data
});
}
/**
* Log connection events
* @param {string} event - Event type
* @param {string} connectionId - Connection ID
* @param {Object} data - Event data
*/
logConnection(event, connectionId, data = {}) {
this.info(`Connection ${event}`, {
event,
connectionId,
...data
});
}
/**
* Log recording events
* @param {string} event - Event type
* @param {string} recordingId - Recording ID
* @param {Object} data - Event data
*/
logRecording(event, recordingId, data = {}) {
this.info(`Recording ${event}`, {
event,
recordingId,
...data
});
}
/**
* Log authentication events
* @param {string} event - Event type
* @param {string} clientId - Client ID
* @param {Object} data - Event data
*/
logAuth(event, clientId, data = {}) {
this.info(`Auth ${event}`, {
event,
clientId,
...data
});
}
/**
* Create child logger with context
* @param {Object} context - Context to include in all logs
* @returns {Object} Child logger
*/
child(context = {}) {
const parent = this;
return {
error: (message, meta = {}) => parent.error(message, { ...context, ...meta }),
warn: (message, meta = {}) => parent.warn(message, { ...context, ...meta }),
info: (message, meta = {}) => parent.info(message, { ...context, ...meta }),
debug: (message, meta = {}) => parent.debug(message, { ...context, ...meta }),
child: (additionalContext) => parent.child({ ...context, ...additionalContext })
};
}
/**
* Send logs to external service (placeholder)
* @param {string} level - Log level
* @param {string} message - Log message
* @param {Object} meta - Metadata
*/
sendToExternalService(level, message, meta) {
// TODO: Implement external logging service integration
// Examples: Winston with transports, Logstash, Datadog, etc.
// For now, this is a placeholder
// In production, you might want to:
// - Send to Elasticsearch/Logstash
// - Send to cloud logging services (AWS CloudWatch, Google Cloud Logging, etc.)
// - Send to monitoring services (Datadog, New Relic, etc.)
// - Store in database for analysis
}
/**
* Performance timer utility
* @param {string} label - Timer label
* @returns {Function} Function to end timer and log duration
*/
timer(label) {
const start = Date.now();
return (message = '', meta = {}) => {
const duration = Date.now() - start;
this.debug(`${label} ${message}`, {
duration: `${duration}ms`,
...meta
});
return duration;
};
}
/**
* Log system metrics
*/
logMetrics() {
const memUsage = process.memoryUsage();
const cpuUsage = process.cpuUsage();
this.debug('System metrics', {
memory: {
rss: `${Math.round(memUsage.rss / 1024 / 1024)}MB`,
heapTotal: `${Math.round(memUsage.heapTotal / 1024 / 1024)}MB`,
heapUsed: `${Math.round(memUsage.heapUsed / 1024 / 1024)}MB`,
external: `${Math.round(memUsage.external / 1024 / 1024)}MB`
},
cpu: {
user: cpuUsage.user,
system: cpuUsage.system
},
uptime: `${Math.round(process.uptime())}s`
});
}
}
// Create and export singleton instance
const logger = new Logger();
module.exports = logger;