4c1dc4082e
Build and Push Images / build-and-push (push) Has been cancelled
이전 세션들에서 작업된 아래 범위를 모두 포함: 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>
120 lines
4.3 KiB
TypeScript
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";
|
|
}
|
|
}
|