mcp-adr-analysis-server
Version:
MCP server for analyzing Architectural Decision Records and project architecture
449 lines • 14.5 kB
JavaScript
/**
* Enhanced Logging System
*
* Provides comprehensive logging with structured error reporting,
* diagnostic context, and actionable error messages.
*/
import { EnhancedError } from '../types/enhanced-errors.js';
/**
* Enhanced Logger with comprehensive error reporting and diagnostics
*/
export class EnhancedLogger {
config;
logBuffer = [];
maxBufferSize = 1000;
constructor(config = {}) {
this.config = {
level: 'info',
enableConsole: true,
enableFile: false,
enableStructuredLogging: true,
enablePerformanceMetrics: true,
maxFileSize: 10 * 1024 * 1024, // 10MB
maxFiles: 5,
...config,
};
}
/**
* Log debug information
*/
debug(message, component, context) {
this.log('debug', message, component, context ? { context } : {});
}
/**
* Log informational messages
*/
info(message, component, context) {
this.log('info', message, component, context ? { context } : {});
}
/**
* Log warning messages
*/
warn(message, component, context) {
this.log('warn', message, component, context ? { context } : {});
}
/**
* Log error messages
*/
error(message, component, error, context) {
const options = {};
if (error)
options.error = error;
if (context)
options.context = context;
this.log('error', message, component, options);
}
/**
* Log critical errors
*/
critical(message, component, error, context) {
const options = {};
if (error)
options.error = error;
if (context)
options.context = context;
this.log('critical', message, component, options);
}
/**
* Log enhanced errors with full diagnostic information
*/
logEnhancedError(error) {
const entry = {
timestamp: new Date(),
level: this.mapSeverityToLogLevel(error.severity),
message: error.message,
component: error.diagnostics.component,
...(error.diagnostics.operation && { operation: error.diagnostics.operation }),
context: {
code: error.code,
severity: error.severity,
recoverable: error.recoverable,
retryable: error.retryable,
suggestions: error.suggestions,
...error.diagnostics.context,
},
error: error.toLogObject(),
diagnostics: error.diagnostics,
...(error.diagnostics.performanceMetrics && {
performanceMetrics: error.diagnostics.performanceMetrics,
}),
};
this.writeLogEntry(entry);
}
/**
* Log operation start with performance tracking
*/
logOperationStart(operation, component, context) {
const operationId = this.generateOperationId();
this.log('info', `Operation started: ${operation}`, component, {
context: { operationId, ...context },
operation,
});
return operationId;
}
/**
* Log operation completion with performance metrics
*/
logOperationComplete(operationId, operation, component, duration, context) {
this.log('info', `Operation completed: ${operation}`, component, {
context: { operationId, duration, ...context },
operation,
performanceMetrics: { duration },
});
}
/**
* Log operation failure with recovery suggestions
*/
logOperationFailure(operationId, operation, component, error, duration, context) {
this.log('error', `Operation failed: ${operation}`, component, {
error,
context: { operationId, duration, ...context },
operation,
performanceMetrics: { duration },
});
}
/**
* Log performance metrics
*/
logPerformanceMetrics(component, operation, metrics) {
this.log('info', `Performance metrics for ${operation}`, component, {
operation,
performanceMetrics: metrics,
});
}
/**
* Get recent log entries for debugging
*/
getRecentLogs(count = 100) {
return this.logBuffer.slice(-count);
}
/**
* Get logs filtered by level and component
*/
getFilteredLogs(filters) {
return this.logBuffer.filter(entry => {
if (filters.level && !this.levelMeetsFilter(filters.level, entry.level)) {
return false;
}
if (filters.component && entry.component !== filters.component) {
return false;
}
if (filters.operation && entry.operation !== filters.operation) {
return false;
}
if (filters.since && entry.timestamp < filters.since) {
return false;
}
return true;
});
}
/**
* Clear log buffer
*/
clearLogs() {
this.logBuffer = [];
}
/**
* Core logging method
*/
log(level, message, component, options = {}) {
if (!this.isLevelEnabled(level)) {
return;
}
const entry = {
timestamp: new Date(),
level,
message,
component,
...(options.operation && { operation: options.operation }),
...(options.context && { context: options.context }),
...(options.error && { error: this.serializeError(options.error) }),
...((this.config.enablePerformanceMetrics || options.performanceMetrics) && {
performanceMetrics: this.config.enablePerformanceMetrics
? {
memoryUsage: this.getMemoryUsage(),
...options.performanceMetrics,
}
: options.performanceMetrics,
}),
};
this.writeLogEntry(entry);
}
/**
* Write log entry to configured outputs
*/
writeLogEntry(entry) {
// Add to buffer
this.logBuffer.push(entry);
if (this.logBuffer.length > this.maxBufferSize) {
this.logBuffer.shift();
}
// Console output
if (this.config.enableConsole) {
this.writeToConsole(entry);
}
// File output (if configured)
if (this.config.enableFile && this.config.filePath) {
this.writeToFile(entry);
}
}
/**
* Write to console with appropriate formatting
*/
writeToConsole(entry) {
const timestamp = entry.timestamp.toISOString();
const prefix = `[${timestamp}] [${entry.level.toUpperCase()}] [${entry.component}]`;
if (this.config.enableStructuredLogging) {
const logData = {
...entry,
timestamp: timestamp,
};
switch (entry.level) {
case 'critical':
case 'error':
console.error(prefix, entry.message, logData);
break;
case 'warn':
console.warn(prefix, entry.message, logData);
break;
case 'debug':
console.debug(prefix, entry.message, logData);
break;
default:
console.log(prefix, entry.message, logData);
}
}
else {
const message = `${prefix} ${entry.message}`;
switch (entry.level) {
case 'critical':
case 'error':
console.error(message);
if (entry.error) {
console.error('Error details:', entry.error);
}
break;
case 'warn':
console.warn(message);
break;
case 'debug':
console.debug(message);
break;
default:
console.log(message);
}
}
}
/**
* Write to file (placeholder - would need file system implementation)
*/
writeToFile(_entry) {
// File logging would be implemented here
// For now, this is a placeholder as file operations depend on environment
}
/**
* Check if log level is enabled
*/
isLevelEnabled(level, entryLevel) {
const levels = ['debug', 'info', 'warn', 'error', 'critical'];
const configLevelIndex = levels.indexOf(this.config.level);
const checkLevelIndex = levels.indexOf(entryLevel || level);
return checkLevelIndex >= configLevelIndex;
}
/**
* Check if a specific level meets the filter criteria
*/
levelMeetsFilter(filterLevel, entryLevel) {
const levels = ['debug', 'info', 'warn', 'error', 'critical'];
const filterLevelIndex = levels.indexOf(filterLevel);
const entryLevelIndex = levels.indexOf(entryLevel);
return entryLevelIndex >= filterLevelIndex;
}
/**
* Map error severity to log level
*/
mapSeverityToLogLevel(severity) {
switch (severity) {
case 'critical':
return 'critical';
case 'high':
return 'error';
case 'medium':
return 'warn';
case 'low':
return 'info';
default:
return 'info';
}
}
/**
* Serialize error for logging
*/
serializeError(error) {
if (error instanceof EnhancedError) {
return error.toLogObject();
}
return {
name: error.name,
message: error.message,
stack: error.stack,
cause: error.cause ? this.serializeError(error.cause) : undefined,
};
}
/**
* Get current memory usage
*/
getMemoryUsage() {
if (typeof process !== 'undefined' && process.memoryUsage) {
return Math.round(process.memoryUsage().heapUsed / 1024 / 1024); // MB
}
return 0;
}
/**
* Generate unique operation ID
*/
generateOperationId() {
return `op_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
}
}
/**
* Global logger instance
*/
export const logger = new EnhancedLogger();
/**
* Create component-specific logger
*/
export function createComponentLogger(component, config) {
return new ComponentLogger(component, config);
}
/**
* Component-specific logger wrapper
*/
export class ComponentLogger {
logger;
component;
constructor(component, config) {
this.component = component;
this.logger = config ? new EnhancedLogger(config) : logger; // Use global logger if no config
}
debug(message, context) {
this.logger.debug(message, this.component, context);
}
info(message, context) {
this.logger.info(message, this.component, context);
}
warn(message, context) {
this.logger.warn(message, this.component, context);
}
error(message, error, context) {
this.logger.error(message, this.component, error, context);
}
critical(message, error, context) {
this.logger.critical(message, this.component, error, context);
}
logEnhancedError(error) {
this.logger.logEnhancedError(error);
}
logOperationStart(operation, context) {
return this.logger.logOperationStart(operation, this.component, context);
}
logOperationComplete(operationId, operation, duration, context) {
this.logger.logOperationComplete(operationId, operation, this.component, duration, context);
}
logOperationFailure(operationId, operation, error, duration, context) {
this.logger.logOperationFailure(operationId, operation, this.component, error, duration, context);
}
logPerformanceMetrics(operation, metrics) {
this.logger.logPerformanceMetrics(this.component, operation, metrics);
}
}
/**
* Error recovery utilities
*/
export class ErrorRecoveryManager {
logger;
recoveryStrategies = new Map();
constructor(logger) {
this.logger = logger || new EnhancedLogger();
}
/**
* Register recovery strategy for specific error codes
*/
registerRecoveryStrategy(errorCode, strategy) {
this.recoveryStrategies.set(errorCode, strategy);
}
/**
* Attempt to recover from an enhanced error
*/
async attemptRecovery(error) {
if (!error.recoverable) {
this.logger.warn('Error is not recoverable', 'ErrorRecoveryManager', {
errorCode: error.code,
message: error.message,
});
return false;
}
const strategy = this.recoveryStrategies.get(error.code);
if (!strategy) {
this.logger.warn('No recovery strategy found', 'ErrorRecoveryManager', {
errorCode: error.code,
message: error.message,
});
return false;
}
try {
this.logger.info('Attempting error recovery', 'ErrorRecoveryManager', {
errorCode: error.code,
message: error.message,
});
const recovered = await strategy(error);
if (recovered) {
this.logger.info('Error recovery successful', 'ErrorRecoveryManager', {
errorCode: error.code,
message: error.message,
});
}
else {
this.logger.warn('Error recovery failed', 'ErrorRecoveryManager', {
errorCode: error.code,
message: error.message,
});
}
return recovered;
}
catch (recoveryError) {
this.logger.error('Error recovery threw exception', 'ErrorRecoveryManager', recoveryError, {
originalErrorCode: error.code,
originalMessage: error.message,
});
return false;
}
}
/**
* Get recovery suggestions for an error
*/
getRecoverySuggestions(error) {
return error.suggestions.map(s => `${s.action}: ${s.description}`);
}
}
//# sourceMappingURL=enhanced-logging.js.map