Files
pipeline/backend-node/src/fleet/fleetMetricsService.ts
T
chpark 4c1dc4082e
Build and Push Images / build-and-push (push) Has been cancelled
feat: Fleet/Collector/엣지 배포 관련 누적 작업 일괄 커밋
이전 세션들에서 작업된 아래 범위를 모두 포함:

Fleet 서브시스템 (src/fleet/)
- fleetDeviceService / fleetCommandService / fleetDeploymentService / fleetReleaseService
- fleetMetricsService, fleetScriptService, fleetEdgeConfigService
- Edge 디바이스 관리, 커맨드 발행, 배포/릴리스, 스크립트 동기화

Collector 확장
- centralMqttForwarder / centralForwarderConfigService
- equipmentStateService, pythonHookRunner, scriptCache
- Modbus/OPC-UA/S7/XGT 프로토콜 클라이언트
- targetDbIntrospection (저장 DB 조회)

Routes / API
- automationDashboardRoutes, centralForwarderRoutes, equipmentStateRoutes

DB
- importEdgeConfig (Python cached config → Pipeline DB)
- seedDataSources (external_db_connections 초기 시드)

엣지 배포 리소스
- docker/edge/Dockerfile.backend.prod, Dockerfile.frontend.prod
- docker/edge/docker-compose.edge.yml

프론트엔드
- admin/automaticMng (centralForwarder, dashboard, equipmentState)
- admin/fleet (commands, devices, deployments, releases, scripts, alerts)
- admin/pipeline-device 개선 (저장 DB 드롭다운, 태그 매핑 등)
- ExternalDbConnectionModal, ScriptsManagerDialog 등 신규 컴포넌트
- lib/api: automationDashboard, centralForwarder, equipmentState, fleet

docs/
- EDGE_SERVER_STRUCTURE, FLEET_COMPLETE, FLEET_EDGE_INTEGRATION, FLEET_HOOK_INTEGRATION

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 20:00:06 +09:00

120 lines
4.3 KiB
TypeScript

/**
* Fleet Prometheus Metrics
* - /metrics 엔드포인트에 Prometheus text format으로 노출
* - 디바이스/커맨드/배포/알림 통계
*/
import { query, queryOne } from "../database/db";
export class FleetMetricsService {
/**
* Prometheus text format 메트릭 생성
*/
static async generate(): Promise<string> {
const lines: string[] = [];
// 디바이스 상태
const devices = await query<any>(
`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<any>(
`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<any>(
`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<any>(
`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<any>(
`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<any>(
`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<any>(
`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";
}
}