@hivetechs/hive-ai
Version:
Real-time streaming AI consensus platform with HTTP+SSE MCP integration for Claude Code, VS Code, Cursor, and Windsurf - powered by OpenRouter's unified API
395 lines • 11.3 kB
JavaScript
/**
* Structured Logger for Enhanced Error Reporting
*
* Provides comprehensive logging with structured data, error tracking,
* and integration with the health monitoring system.
*/
export const LOG_LEVELS = {
DEBUG: 0,
INFO: 1,
WARN: 2,
ERROR: 3,
FATAL: 4
};
export class StructuredLogger {
logs = [];
maxLogs = 10000;
currentLevel = 'INFO';
enableConsole = true;
constructor(options) {
if (options?.maxLogs)
this.maxLogs = options.maxLogs;
if (options?.level)
this.currentLevel = options.level;
if (options?.enableConsole !== undefined)
this.enableConsole = options.enableConsole;
}
/**
* Set logging level
*/
setLevel(level) {
this.currentLevel = level;
}
/**
* Enable or disable console output
*/
setConsoleOutput(enabled) {
this.enableConsole = enabled;
}
/**
* Log debug message
*/
debug(message, context = {}) {
this.log('DEBUG', message, context);
}
/**
* Log info message
*/
info(message, context = {}) {
this.log('INFO', message, context);
}
/**
* Log warning message
*/
warn(message, context = {}) {
this.log('WARN', message, context);
}
/**
* Log error message
*/
error(message, context = {}, error) {
const errorInfo = error ? {
name: error.name,
message: error.message,
stack: error.stack
} : undefined;
this.log('ERROR', message, context, errorInfo);
}
/**
* Log fatal error message
*/
fatal(message, context = {}, error) {
const errorInfo = error ? {
name: error.name,
message: error.message,
stack: error.stack
} : undefined;
this.log('FATAL', message, context, errorInfo);
}
/**
* Log consensus stage start
*/
stageStart(stage, context = {}) {
this.info(`Starting ${stage} stage`, {
...context,
stage,
event: 'stage_start'
});
}
/**
* Log consensus stage completion
*/
stageComplete(stage, duration, context = {}) {
this.info(`Completed ${stage} stage`, {
...context,
stage,
duration,
event: 'stage_complete'
});
}
/**
* Log consensus stage failure
*/
stageError(stage, error, context = {}) {
this.error(`Failed ${stage} stage`, {
...context,
stage,
event: 'stage_error'
}, error);
}
/**
* Log API request start
*/
apiRequestStart(provider, model, context = {}) {
this.debug(`API request started`, {
...context,
provider,
model,
event: 'api_request_start'
});
}
/**
* Log API request completion
*/
apiRequestComplete(provider, model, duration, context = {}) {
this.debug(`API request completed`, {
...context,
provider,
model,
duration,
event: 'api_request_complete'
});
}
/**
* Log API request error
*/
apiRequestError(provider, model, error, context = {}) {
this.error(`API request failed`, {
...context,
provider,
model,
event: 'api_request_error'
}, error);
}
/**
* Log circuit breaker event
*/
circuitBreakerEvent(provider, model, state, context = {}) {
this.warn(`Circuit breaker ${state}`, {
...context,
provider,
model,
circuitState: state,
event: 'circuit_breaker'
});
}
/**
* Log fallback event
*/
fallbackEvent(fromProvider, fromModel, toProvider, toModel, context = {}) {
this.warn(`Fallback triggered`, {
...context,
fromProvider,
fromModel,
toProvider,
toModel,
event: 'fallback'
});
}
/**
* Log health check results
*/
healthCheck(service, status, responseTime, context = {}) {
this.info(`Health check: ${service}`, {
...context,
service,
healthStatus: status,
responseTime,
event: 'health_check'
});
}
/**
* Core logging method
*/
log(level, message, context, error) {
// Check if we should log this level
if (LOG_LEVELS[level] < LOG_LEVELS[this.currentLevel]) {
return;
}
const logEntry = {
timestamp: new Date().toISOString(),
level,
message,
context,
error
};
// Store in memory
this.logs.push(logEntry);
// Trim logs if needed
if (this.logs.length > this.maxLogs) {
this.logs = this.logs.slice(-this.maxLogs);
}
// Console output
if (this.enableConsole) {
this.outputToConsole(logEntry);
}
}
/**
* Output log entry to console with formatting
*/
outputToConsole(entry) {
const timestamp = entry.timestamp.split('T')[1].split('.')[0];
const level = entry.level.padEnd(5);
let color = '';
let reset = '\x1b[0m';
switch (entry.level) {
case 'DEBUG':
color = '\x1b[36m';
break; // Cyan
case 'INFO':
color = '\x1b[32m';
break; // Green
case 'WARN':
color = '\x1b[33m';
break; // Yellow
case 'ERROR':
color = '\x1b[31m';
break; // Red
case 'FATAL':
color = '\x1b[35m';
break; // Magenta
}
let output = `${color}[${timestamp}] ${level}${reset} ${entry.message}`;
// Add context if significant
const contextKeys = Object.keys(entry.context);
if (contextKeys.length > 0) {
const significantContext = contextKeys
.filter(key => ['provider', 'model', 'stage', 'duration', 'healthStatus'].includes(key))
.map(key => `${key}=${entry.context[key]}`)
.join(' ');
if (significantContext) {
output += ` [${significantContext}]`;
}
}
console.log(output);
// Log error details if present
if (entry.error && entry.level === 'ERROR') {
console.log(`${color} Error: ${entry.error.message}${reset}`);
}
}
/**
* Get recent logs
*/
getRecentLogs(count = 100) {
return this.logs.slice(-count);
}
/**
* Get logs by level
*/
getLogsByLevel(level, hours = 1) {
const cutoff = new Date(Date.now() - hours * 60 * 60 * 1000);
return this.logs.filter(log => log.level === level &&
new Date(log.timestamp) >= cutoff);
}
/**
* Get logs by context
*/
getLogsByContext(contextFilter, hours = 1) {
const cutoff = new Date(Date.now() - hours * 60 * 60 * 1000);
return this.logs.filter(log => {
if (new Date(log.timestamp) < cutoff)
return false;
return Object.entries(contextFilter).every(([key, value]) => log.context[key] === value);
});
}
/**
* Get error statistics
*/
getErrorStats(hours = 1) {
const cutoff = new Date(Date.now() - hours * 60 * 60 * 1000);
const errorLogs = this.logs.filter(log => (log.level === 'ERROR' || log.level === 'FATAL') &&
new Date(log.timestamp) >= cutoff);
const errorsByType = {};
const errorsByProvider = {};
const errorsByStage = {};
errorLogs.forEach(log => {
// Count by error type
if (log.context.errorType) {
errorsByType[log.context.errorType] = (errorsByType[log.context.errorType] || 0) + 1;
}
// Count by provider
if (log.context.provider) {
errorsByProvider[log.context.provider] = (errorsByProvider[log.context.provider] || 0) + 1;
}
// Count by stage
if (log.context.stage) {
errorsByStage[log.context.stage] = (errorsByStage[log.context.stage] || 0) + 1;
}
});
return {
totalErrors: errorLogs.length,
errorsByType,
errorsByProvider,
errorsByStage
};
}
/**
* Clear logs
*/
clearLogs() {
this.logs = [];
}
/**
* Export logs as JSON
*/
exportLogs() {
return JSON.stringify(this.logs, null, 2);
}
}
/**
* Determine appropriate log level based on environment
*/
function getEnvironmentLogLevel() {
// Check for explicit debug environment variables
if (process.env.HIVE_DEBUG === 'true' ||
process.env.DEBUG === 'true' ||
process.env.HIVE_VERBOSE === 'true') {
return 'DEBUG';
}
// Check for numeric log level
const logLevel = process.env.HIVE_LOG_LEVEL || process.env.LOG_LEVEL;
if (logLevel) {
const numLevel = parseInt(logLevel);
switch (numLevel) {
case 0: return 'DEBUG';
case 1: return 'INFO';
case 2: return 'WARN';
case 3: return 'ERROR';
case 4: return 'FATAL';
}
}
// Development vs Production detection
if (process.env.NODE_ENV === 'development') {
return 'DEBUG';
}
// Default for production users - hide debug noise
return 'WARN';
}
/**
* Enhanced StructuredLogger with debug patterns
*/
class EnhancedStructuredLogger extends StructuredLogger {
/**
* Cost calculation debug logging (💰 prefix)
*/
costDebug(message, context = {}) {
this.debug(`💰 ${message}`, { ...context, category: 'cost' });
}
/**
* Consensus pipeline debug logging (📚 prefix)
*/
consensusDebug(message, context = {}) {
this.debug(`📚 ${message}`, { ...context, category: 'consensus' });
}
/**
* Pipeline enhancement debug logging (🔄 prefix)
*/
pipelineDebug(message, context = {}) {
this.debug(`🔄 ${message}`, { ...context, category: 'pipeline' });
}
/**
* Toggle debug mode on/off
*/
enableDebugMode(enabled = true) {
if (enabled) {
this.setLevel('DEBUG');
console.log('🐛 Debug mode enabled - detailed logs will be shown');
}
else {
this.setLevel('WARN');
console.log('📱 Production mode - debug logs hidden');
}
}
/**
* Check if debug mode is active
*/
isDebugMode() {
return this.currentLevel === 'DEBUG';
}
}
// Global structured logger instance with environment-based configuration
export const structuredLogger = new EnhancedStructuredLogger({
level: getEnvironmentLogLevel(),
enableConsole: true
});
//# sourceMappingURL=structured-logger.js.map