d132e1cfd8
1. Add logger to writeCacheFile (version-cache.ts) - Now logs warnings when file write fails - Helps debugging in production environments 2. Sanitize logging in parseGroupResult (communities.ts) - Changed from info to debug level - Removed full node/groupNode dumps (sensitive data) - Now only logs nodeTag and groupId 3. Add input validation in parseNewsletterCreateResponse (newsletter.ts) - Validates response structure before destructuring - Adds fallback values for parseInt (prevents NaN) - Adds null checks for optional fields 4. Add health-status.ts module - getHealthStatus(): Full health check with circuit breakers, cache, memory - isHealthy(): Simple boolean for liveness probes - getSimpleHealthStatus(): Returns 'ok', 'degraded', or 'error' - Useful for k8s probes and monitoring dashboards
210 lines
4.7 KiB
TypeScript
210 lines
4.7 KiB
TypeScript
/**
|
|
* Health Status Utilities
|
|
*
|
|
* Provides health check and status information for monitoring and k8s probes.
|
|
*
|
|
* @module Utils/health-status
|
|
*/
|
|
|
|
import { getVersionCacheStatus } from './version-cache.js'
|
|
import { globalCircuitRegistry } from './circuit-breaker.js'
|
|
|
|
/**
|
|
* Circuit breaker health information
|
|
*/
|
|
export interface CircuitBreakerHealth {
|
|
name: string
|
|
state: 'closed' | 'open' | 'half-open'
|
|
failures: number
|
|
successes: number
|
|
totalCalls: number
|
|
}
|
|
|
|
/**
|
|
* Cache health information
|
|
*/
|
|
export interface CacheHealth {
|
|
versionCache: {
|
|
hasCache: boolean
|
|
isExpired: boolean
|
|
ageMs: number | null
|
|
source: string | null
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Overall health status
|
|
*/
|
|
export interface HealthStatus {
|
|
status: 'healthy' | 'degraded' | 'unhealthy'
|
|
timestamp: number
|
|
uptime: number
|
|
version: string
|
|
circuitBreakers: CircuitBreakerHealth[]
|
|
cache: CacheHealth
|
|
checks: {
|
|
name: string
|
|
status: 'pass' | 'warn' | 'fail'
|
|
message?: string
|
|
}[]
|
|
}
|
|
|
|
/**
|
|
* Get the current health status of the Baileys instance.
|
|
*
|
|
* Useful for:
|
|
* - Kubernetes liveness/readiness probes
|
|
* - Load balancer health checks
|
|
* - Monitoring dashboards
|
|
*
|
|
* @returns HealthStatus object with detailed status information
|
|
*
|
|
* @example
|
|
* ```typescript
|
|
* import { getHealthStatus } from '@whiskeysockets/baileys'
|
|
*
|
|
* // Simple health check endpoint
|
|
* app.get('/health', (req, res) => {
|
|
* const health = getHealthStatus()
|
|
* const statusCode = health.status === 'healthy' ? 200 :
|
|
* health.status === 'degraded' ? 200 : 503
|
|
* res.status(statusCode).json(health)
|
|
* })
|
|
*
|
|
* // Kubernetes probe
|
|
* app.get('/healthz', (req, res) => {
|
|
* const health = getHealthStatus()
|
|
* res.status(health.status !== 'unhealthy' ? 200 : 503).send(health.status)
|
|
* })
|
|
* ```
|
|
*/
|
|
export function getHealthStatus(): HealthStatus {
|
|
const checks: HealthStatus['checks'] = []
|
|
let overallStatus: HealthStatus['status'] = 'healthy'
|
|
|
|
// 1. Check version cache
|
|
const versionCacheStatus = getVersionCacheStatus()
|
|
if (!versionCacheStatus.hasCache) {
|
|
checks.push({
|
|
name: 'version_cache',
|
|
status: 'warn',
|
|
message: 'No version cache available'
|
|
})
|
|
if (overallStatus === 'healthy') overallStatus = 'degraded'
|
|
} else if (versionCacheStatus.isExpired) {
|
|
checks.push({
|
|
name: 'version_cache',
|
|
status: 'warn',
|
|
message: 'Version cache is expired'
|
|
})
|
|
if (overallStatus === 'healthy') overallStatus = 'degraded'
|
|
} else {
|
|
checks.push({
|
|
name: 'version_cache',
|
|
status: 'pass'
|
|
})
|
|
}
|
|
|
|
// 2. Check circuit breakers
|
|
const circuitBreakers: CircuitBreakerHealth[] = []
|
|
let openCircuits = 0
|
|
|
|
for (const [name, breaker] of globalCircuitRegistry.getAll()) {
|
|
const stats = breaker.getStats()
|
|
const state = breaker.getState()
|
|
|
|
circuitBreakers.push({
|
|
name,
|
|
state,
|
|
failures: stats.totalFailures,
|
|
successes: stats.totalSuccesses,
|
|
totalCalls: stats.totalCalls
|
|
})
|
|
|
|
if (state === 'open') {
|
|
openCircuits++
|
|
}
|
|
}
|
|
|
|
if (openCircuits > 0) {
|
|
checks.push({
|
|
name: 'circuit_breakers',
|
|
status: openCircuits > 2 ? 'fail' : 'warn',
|
|
message: `${openCircuits} circuit breaker(s) are open`
|
|
})
|
|
if (openCircuits > 2) {
|
|
overallStatus = 'unhealthy'
|
|
} else if (overallStatus === 'healthy') {
|
|
overallStatus = 'degraded'
|
|
}
|
|
} else {
|
|
checks.push({
|
|
name: 'circuit_breakers',
|
|
status: 'pass'
|
|
})
|
|
}
|
|
|
|
// 3. Check memory usage (warn if > 90%)
|
|
const memUsage = process.memoryUsage()
|
|
const heapUsedPercent = (memUsage.heapUsed / memUsage.heapTotal) * 100
|
|
|
|
if (heapUsedPercent > 90) {
|
|
checks.push({
|
|
name: 'memory',
|
|
status: 'warn',
|
|
message: `Heap usage is ${heapUsedPercent.toFixed(1)}%`
|
|
})
|
|
if (overallStatus === 'healthy') overallStatus = 'degraded'
|
|
} else {
|
|
checks.push({
|
|
name: 'memory',
|
|
status: 'pass'
|
|
})
|
|
}
|
|
|
|
return {
|
|
status: overallStatus,
|
|
timestamp: Date.now(),
|
|
uptime: process.uptime(),
|
|
version: process.env.npm_package_version || 'unknown',
|
|
circuitBreakers,
|
|
cache: {
|
|
versionCache: {
|
|
hasCache: versionCacheStatus.hasCache,
|
|
isExpired: versionCacheStatus.isExpired,
|
|
ageMs: versionCacheStatus.age,
|
|
source: versionCacheStatus.source
|
|
}
|
|
},
|
|
checks
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Simple health check - returns true if system is healthy or degraded.
|
|
* Use this for basic liveness probes.
|
|
*
|
|
* @returns true if healthy or degraded, false if unhealthy
|
|
*/
|
|
export function isHealthy(): boolean {
|
|
const status = getHealthStatus()
|
|
return status.status !== 'unhealthy'
|
|
}
|
|
|
|
/**
|
|
* Get a simple status string for minimal health endpoints.
|
|
*
|
|
* @returns 'ok', 'degraded', or 'error'
|
|
*/
|
|
export function getSimpleHealthStatus(): 'ok' | 'degraded' | 'error' {
|
|
const status = getHealthStatus()
|
|
switch (status.status) {
|
|
case 'healthy':
|
|
return 'ok'
|
|
case 'degraded':
|
|
return 'degraded'
|
|
case 'unhealthy':
|
|
return 'error'
|
|
}
|
|
}
|