110 lines
3.3 KiB
TypeScript
110 lines
3.3 KiB
TypeScript
import { query } from '../../utils/db'
|
|
|
|
export default defineEventHandler(async (event) => {
|
|
const THRESHOLD = 30 // 30% 이상 변화 시 이상 감지
|
|
|
|
// 활성 서버 목록
|
|
const servers = await query<any>(`
|
|
SELECT target_id, server_name
|
|
FROM server_targets
|
|
WHERE is_active = 1
|
|
ORDER BY server_name
|
|
`)
|
|
|
|
const anomalies: any[] = []
|
|
const serverResults: any[] = []
|
|
|
|
for (const server of servers) {
|
|
const snapshots = await query<any>(`
|
|
SELECT cpu_percent, memory_percent, collected_at
|
|
FROM server_snapshots
|
|
WHERE target_id = $1
|
|
ORDER BY collected_at DESC
|
|
LIMIT 20
|
|
`, [server.target_id])
|
|
|
|
if (snapshots.length < 4) {
|
|
serverResults.push({
|
|
target_id: server.target_id,
|
|
server_name: server.server_name,
|
|
cpu_change: null,
|
|
mem_change: null,
|
|
status: 'normal'
|
|
})
|
|
continue
|
|
}
|
|
|
|
const half = Math.floor(snapshots.length / 2)
|
|
const currSnapshots = snapshots.slice(0, half)
|
|
const prevSnapshots = snapshots.slice(half)
|
|
|
|
const currCpuAvg = currSnapshots.reduce((sum: number, s: any) => sum + (Number(s.cpu_percent) || 0), 0) / currSnapshots.length
|
|
const prevCpuAvg = prevSnapshots.reduce((sum: number, s: any) => sum + (Number(s.cpu_percent) || 0), 0) / prevSnapshots.length
|
|
const currMemAvg = currSnapshots.reduce((sum: number, s: any) => sum + (Number(s.memory_percent) || 0), 0) / currSnapshots.length
|
|
const prevMemAvg = prevSnapshots.reduce((sum: number, s: any) => sum + (Number(s.memory_percent) || 0), 0) / prevSnapshots.length
|
|
|
|
let cpuChange: number | null = null
|
|
let memChange: number | null = null
|
|
|
|
if (prevCpuAvg > 1) {
|
|
cpuChange = ((currCpuAvg - prevCpuAvg) / prevCpuAvg) * 100
|
|
} else {
|
|
cpuChange = currCpuAvg - prevCpuAvg
|
|
}
|
|
|
|
if (prevMemAvg > 1) {
|
|
memChange = ((currMemAvg - prevMemAvg) / prevMemAvg) * 100
|
|
} else {
|
|
memChange = currMemAvg - prevMemAvg
|
|
}
|
|
|
|
let status = 'normal'
|
|
const maxChange = Math.max(Math.abs(cpuChange || 0), Math.abs(memChange || 0))
|
|
if (maxChange >= 100) status = 'danger'
|
|
else if (maxChange >= THRESHOLD) status = 'warning'
|
|
|
|
serverResults.push({
|
|
target_id: server.target_id,
|
|
server_name: server.server_name,
|
|
cpu_change: cpuChange,
|
|
mem_change: memChange,
|
|
status
|
|
})
|
|
|
|
// CPU 이상 감지
|
|
if (cpuChange !== null && Math.abs(cpuChange) >= THRESHOLD) {
|
|
anomalies.push({
|
|
target_id: server.target_id,
|
|
server_name: server.server_name,
|
|
metric: 'CPU',
|
|
prev_avg: prevCpuAvg,
|
|
curr_avg: currCpuAvg,
|
|
change_rate: cpuChange,
|
|
direction: cpuChange >= 0 ? 'up' : 'down'
|
|
})
|
|
}
|
|
|
|
// Memory 이상 감지
|
|
if (memChange !== null && Math.abs(memChange) >= THRESHOLD) {
|
|
anomalies.push({
|
|
target_id: server.target_id,
|
|
server_name: server.server_name,
|
|
metric: 'Memory',
|
|
prev_avg: prevMemAvg,
|
|
curr_avg: currMemAvg,
|
|
change_rate: memChange,
|
|
direction: memChange >= 0 ? 'up' : 'down'
|
|
})
|
|
}
|
|
}
|
|
|
|
anomalies.sort((a, b) => Math.abs(b.change_rate) - Math.abs(a.change_rate))
|
|
|
|
return {
|
|
anomalies,
|
|
servers: serverResults,
|
|
threshold: THRESHOLD,
|
|
timestamp: new Date().toISOString()
|
|
}
|
|
})
|