MCP GitHub Issue Server
by sammcj
- src
- logging
import { Logger } from './index.js';
import { LoggerHealthStatus } from '../types/logging.js';
/**
* Monitors logger health and handles recovery
*/
export class LoggerHealthMonitor {
private lastHealthCheck = 0;
private consecutiveFailures = 0;
private readonly MAX_CONSECUTIVE_FAILURES = 3;
private readonly HEALTH_CHECK_INTERVAL = 5000; // 5 seconds
constructor(
private readonly logger: Logger,
private readonly onUnhealthy: (status: LoggerHealthStatus) => void
) {}
/**
* Performs a comprehensive health check of the logger
*/
async checkHealth(): Promise<LoggerHealthStatus> {
const now = Date.now();
// Only check every 5 seconds to reduce overhead
if (now - this.lastHealthCheck < this.HEALTH_CHECK_INTERVAL) {
return { healthy: true };
}
try {
// Test file descriptors (if using file transport)
await this.checkFileDescriptors();
// Test write capability
await this.testLogWrite();
// Test error handling
await this.testErrorLogging();
// Reset failure count on success
this.consecutiveFailures = 0;
this.lastHealthCheck = now;
return { healthy: true };
} catch (error) {
this.consecutiveFailures++;
const status: LoggerHealthStatus = {
healthy: false,
error: error instanceof Error ? error.message : String(error),
consecutiveFailures: this.consecutiveFailures,
lastCheckTime: now,
};
// Notify if max failures exceeded
if (this.consecutiveFailures >= this.MAX_CONSECUTIVE_FAILURES) {
this.onUnhealthy(status);
}
return status;
}
}
/**
* Checks if file descriptors are valid and writable
*/
private async checkFileDescriptors(): Promise<void> {
// Implementation depends on logger transport type
// For now, we'll just check if the logger instance exists
if (!this.logger) {
throw new Error('Logger instance is null or undefined');
}
}
/**
* Tests basic log writing capability
*/
private async testLogWrite(): Promise<void> {
return new Promise<void>((resolve, reject) => {
try {
this.logger.debug('Logger health check - write test');
resolve();
} catch (error) {
reject(error);
}
});
}
/**
* Tests error logging capability
*/
private async testErrorLogging(): Promise<void> {
return new Promise<void>((resolve, reject) => {
try {
const testError = new Error('Logger health check - error test');
this.logger.error('Test error logging', testError);
resolve();
} catch (error) {
reject(error);
}
});
}
/**
* Attempts to recover logger functionality
*/
async attemptRecovery(): Promise<boolean> {
try {
// Basic recovery attempts:
// 1. Flush any pending writes
await this.flushLogs();
// 2. Check file permissions and attempt to fix
await this.checkAndFixPermissions();
// 3. Attempt to recreate transports
await this.recreateTransports();
// Verify recovery was successful
const status = await this.checkHealth();
return status.healthy;
} catch (error) {
this.logger.error('Logger recovery failed', error);
return false;
}
}
/**
* Flushes any pending log writes
*/
private async flushLogs(): Promise<void> {
// Implementation depends on logger transport type
// For now, just a placeholder
return Promise.resolve();
}
/**
* Checks and attempts to fix log file permissions
*/
private async checkAndFixPermissions(): Promise<void> {
// Implementation depends on logger transport type
// For now, just a placeholder
return Promise.resolve();
}
/**
* Recreates logger transports
*/
private async recreateTransports(): Promise<void> {
// Implementation depends on logger transport type
// For now, just a placeholder
return Promise.resolve();
}
}