Skip to main content
Glama
lis186

Taiwan Holiday MCP Server

by lis186
health-monitor.ts8.55 kB
/** * Health Monitoring System * * 系統健康狀態監控,提供健康檢查和狀態報告 */ export enum HealthStatus { HEALTHY = 'HEALTHY', DEGRADED = 'DEGRADED', UNHEALTHY = 'UNHEALTHY', UNKNOWN = 'UNKNOWN' } export interface HealthCheckResult { /** 檢查名稱 */ name: string; /** 健康狀態 */ status: HealthStatus; /** 回應時間 (ms) */ responseTime: number; /** 錯誤訊息 */ error?: string; /** 額外資訊 */ details?: Record<string, unknown>; /** 檢查時間 */ timestamp: number; } export interface SystemHealth { /** 整體狀態 */ status: HealthStatus; /** 檢查時間 */ timestamp: number; /** 系統啟動時間 */ uptime: number; /** 版本資訊 */ version: string; /** 個別檢查結果 */ checks: HealthCheckResult[]; /** 統計資訊 */ stats: { /** 記憶體使用情況 */ memory: { used: number; total: number; percentage: number; }; /** 快取統計 */ cache?: { hitRate: number; totalItems: number; memoryUsage: number; }; /** 錯誤統計 */ errors?: { circuitBreakerState: string; totalErrors: number; recentErrors: number; }; }; } export type HealthCheckFunction = () => Promise<HealthCheckResult>; /** * 健康監控器 */ export class HealthMonitor { private checks = new Map<string, HealthCheckFunction>(); private lastCheckResults = new Map<string, HealthCheckResult>(); private startTime = Date.now(); constructor(private readonly version: string = '1.0.1') {} /** * 註冊健康檢查 */ registerCheck(name: string, checkFn: HealthCheckFunction): void { this.checks.set(name, checkFn); } /** * 移除健康檢查 */ unregisterCheck(name: string): boolean { this.lastCheckResults.delete(name); return this.checks.delete(name); } /** * 執行所有健康檢查 */ async performHealthCheck(): Promise<SystemHealth> { const timestamp = Date.now(); const checkResults: HealthCheckResult[] = []; // 執行所有註冊的檢查 for (const [name, checkFn] of this.checks) { try { const result = await this.executeCheck(name, checkFn); checkResults.push(result); this.lastCheckResults.set(name, result); } catch (error) { const failedResult: HealthCheckResult = { name, status: HealthStatus.UNHEALTHY, responseTime: 0, error: error instanceof Error ? error.message : String(error), timestamp, }; checkResults.push(failedResult); this.lastCheckResults.set(name, failedResult); } } // 確定整體健康狀態 const overallStatus = this.determineOverallStatus(checkResults); return { status: overallStatus, timestamp, uptime: timestamp - this.startTime, version: this.version, checks: checkResults, stats: { memory: this.getMemoryStats(), }, }; } /** * 獲取快速健康狀態(不執行檢查) */ getQuickStatus(): { status: HealthStatus; uptime: number; timestamp: number } { const timestamp = Date.now(); const recentResults = Array.from(this.lastCheckResults.values()) .filter(result => timestamp - result.timestamp < 300000); // 5 分鐘內的結果 const status = recentResults.length > 0 ? this.determineOverallStatus(recentResults) : HealthStatus.UNKNOWN; return { status, uptime: timestamp - this.startTime, timestamp, }; } /** * 獲取特定檢查的最新結果 */ getCheckResult(name: string): HealthCheckResult | null { return this.lastCheckResults.get(name) || null; } /** * 獲取所有檢查的最新結果 */ getAllCheckResults(): HealthCheckResult[] { return Array.from(this.lastCheckResults.values()); } /** * 清除所有檢查結果 */ clearResults(): void { this.lastCheckResults.clear(); } /** * 執行單個健康檢查 */ private async executeCheck(name: string, checkFn: HealthCheckFunction): Promise<HealthCheckResult> { const startTime = Date.now(); try { const result = await Promise.race([ checkFn(), this.createTimeoutPromise(30000) // 30 秒超時 ]); return { ...result, responseTime: Date.now() - startTime, timestamp: Date.now(), }; } catch (error) { throw new Error(`Health check '${name}' failed: ${error instanceof Error ? error.message : String(error)}`); } } /** * 建立超時 Promise */ private createTimeoutPromise(timeout: number): Promise<never> { return new Promise((_, reject) => { setTimeout(() => reject(new Error('Health check timeout')), timeout); }); } /** * 確定整體健康狀態 */ private determineOverallStatus(results: HealthCheckResult[]): HealthStatus { if (results.length === 0) { return HealthStatus.UNKNOWN; } const statusCounts = { [HealthStatus.HEALTHY]: 0, [HealthStatus.DEGRADED]: 0, [HealthStatus.UNHEALTHY]: 0, [HealthStatus.UNKNOWN]: 0, }; for (const result of results) { statusCounts[result.status]++; } // 如果有任何 UNHEALTHY,整體為 UNHEALTHY if (statusCounts[HealthStatus.UNHEALTHY] > 0) { return HealthStatus.UNHEALTHY; } // 如果有 DEGRADED 但沒有 UNHEALTHY,整體為 DEGRADED if (statusCounts[HealthStatus.DEGRADED] > 0) { return HealthStatus.DEGRADED; } // 如果有 UNKNOWN,整體為 DEGRADED if (statusCounts[HealthStatus.UNKNOWN] > 0) { return HealthStatus.DEGRADED; } // 全部 HEALTHY return HealthStatus.HEALTHY; } /** * 獲取記憶體統計資訊 */ private getMemoryStats(): { used: number; total: number; percentage: number } { const usage = process.memoryUsage(); const total = usage.heapTotal; const used = usage.heapUsed; return { used, total, percentage: Math.round((used / total) * 100 * 100) / 100, }; } } /** * 預設健康檢查功能 */ export class DefaultHealthChecks { /** * 基本存活檢查 */ static aliveness(): HealthCheckFunction { return async () => ({ name: 'aliveness', status: HealthStatus.HEALTHY, responseTime: 0, details: { message: 'Service is alive' }, timestamp: Date.now(), }); } /** * 記憶體使用檢查 */ static memoryUsage(threshold: number = 90): HealthCheckFunction { return async () => { const usage = process.memoryUsage(); const percentage = (usage.heapUsed / usage.heapTotal) * 100; let status = HealthStatus.HEALTHY; if (percentage > threshold) { status = HealthStatus.UNHEALTHY; } else if (percentage > threshold * 0.8) { status = HealthStatus.DEGRADED; } return { name: 'memory', status, responseTime: 0, details: { heapUsed: usage.heapUsed, heapTotal: usage.heapTotal, percentage: Math.round(percentage * 100) / 100, threshold, }, timestamp: Date.now(), }; }; } /** * 外部 API 檢查 */ static externalApi(url: string, timeout: number = 5000): HealthCheckFunction { return async () => { const startTime = Date.now(); try { const controller = new AbortController(); const timeoutId = setTimeout(() => controller.abort(), timeout); const response = await fetch(url, { signal: controller.signal, method: 'HEAD', }); clearTimeout(timeoutId); const responseTime = Date.now() - startTime; const status = response.ok ? HealthStatus.HEALTHY : HealthStatus.DEGRADED; return { name: 'external-api', status, responseTime, details: { url, statusCode: response.status, statusText: response.statusText, }, timestamp: Date.now(), }; } catch (error) { const responseTime = Date.now() - startTime; return { name: 'external-api', status: HealthStatus.UNHEALTHY, responseTime, error: error instanceof Error ? error.message : String(error), details: { url }, timestamp: Date.now(), }; } }; } }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/lis186/taiwan-holiday-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server