/** * Fleet Prometheus Metrics * - /metrics 엔드포인트에 Prometheus text format으로 노출 * - 디바이스/커맨드/배포/알림 통계 */ import { query, queryOne } from "../database/db"; export class FleetMetricsService { /** * Prometheus text format 메트릭 생성 */ static async generate(): Promise { const lines: string[] = []; // 디바이스 상태 const devices = await query( `SELECT is_online, company_code, COUNT(*) as n FROM fleet_devices GROUP BY is_online, company_code`, ); lines.push("# HELP fleet_devices_total 디바이스 총 개수"); lines.push("# TYPE fleet_devices_total gauge"); for (const d of devices) { const online = d.is_online ? "true" : "false"; lines.push( `fleet_devices_total{online="${online}",company_code="${d.company_code || "unknown"}"} ${d.n}`, ); } // 최근 1시간 heartbeat const hbStats = await queryOne( `SELECT AVG(cpu_percent) as avg_cpu, AVG(memory_percent) as avg_memory, AVG(disk_percent) as avg_disk, COUNT(*) as hb_count FROM fleet_heartbeats WHERE received_at > NOW() - INTERVAL '1 hour'`, ); lines.push("# HELP fleet_cpu_percent_avg 최근 1시간 평균 CPU (%)"); lines.push("# TYPE fleet_cpu_percent_avg gauge"); lines.push(`fleet_cpu_percent_avg ${hbStats?.avg_cpu || 0}`); lines.push("# HELP fleet_memory_percent_avg 최근 1시간 평균 메모리 (%)"); lines.push("# TYPE fleet_memory_percent_avg gauge"); lines.push(`fleet_memory_percent_avg ${hbStats?.avg_memory || 0}`); lines.push("# HELP fleet_heartbeat_count 최근 1시간 heartbeat 수"); lines.push("# TYPE fleet_heartbeat_count counter"); lines.push(`fleet_heartbeat_count ${hbStats?.hb_count || 0}`); // 커맨드 const cmds = await query( `SELECT status, COUNT(*) as n FROM fleet_commands WHERE issued_at > NOW() - INTERVAL '24 hours' GROUP BY status`, ); lines.push("# HELP fleet_commands_total 최근 24시간 커맨드 (상태별)"); lines.push("# TYPE fleet_commands_total counter"); for (const c of cmds) { lines.push(`fleet_commands_total{status="${c.status}"} ${c.n}`); } // 알림 const alerts = await query( `SELECT severity, status, COUNT(*) as n FROM fleet_alerts GROUP BY severity, status`, ); lines.push("# HELP fleet_alerts_total 알림 (심각도/상태별)"); lines.push("# TYPE fleet_alerts_total gauge"); for (const a of alerts) { lines.push( `fleet_alerts_total{severity="${a.severity}",status="${a.status}"} ${a.n}`, ); } // 배포 const deploys = await query( `SELECT status, COUNT(*) as n FROM fleet_deployments WHERE created_at > NOW() - INTERVAL '7 days' GROUP BY status`, ); lines.push("# HELP fleet_deployments_total 최근 7일 배포 (상태별)"); lines.push("# TYPE fleet_deployments_total counter"); for (const d of deploys) { lines.push(`fleet_deployments_total{status="${d.status}"} ${d.n}`); } // PLC 연결 상태 const plcs = await query( `SELECT status, COUNT(*) as n FROM fleet_plc_connections GROUP BY status`, ); lines.push("# HELP fleet_plc_connections_total PLC 연결 (상태별)"); lines.push("# TYPE fleet_plc_connections_total gauge"); for (const p of plcs) { lines.push(`fleet_plc_connections_total{status="${p.status}"} ${p.n}`); } // 실시간 데이터 수집 const edgeData = await queryOne( `SELECT COUNT(*) as records_1h, COUNT(DISTINCT device_id) as active_devices, COUNT(DISTINCT tag_name) as unique_tags FROM fleet_edge_raw_data WHERE time > NOW() - INTERVAL '1 hour'`, ); lines.push("# HELP fleet_edge_records_1h 최근 1시간 수집 레코드"); lines.push("# TYPE fleet_edge_records_1h counter"); lines.push(`fleet_edge_records_1h ${edgeData?.records_1h || 0}`); lines.push("# HELP fleet_edge_active_devices 최근 1시간 활성 디바이스"); lines.push("# TYPE fleet_edge_active_devices gauge"); lines.push(`fleet_edge_active_devices ${edgeData?.active_devices || 0}`); return lines.join("\n") + "\n"; } }