101 lines
2.4 KiB
Go
101 lines
2.4 KiB
Go
package main
|
|
|
|
import (
|
|
"flag"
|
|
"fmt"
|
|
"os"
|
|
"regexp"
|
|
"time"
|
|
|
|
"github.com/TheCreeper/go-notify"
|
|
"github.com/shirou/gopsutil/process"
|
|
)
|
|
|
|
var (
|
|
processRegex string
|
|
threshold float64
|
|
interval int
|
|
gracePeriod int
|
|
lastNotified time.Time
|
|
|
|
prevCPUTimes = make(map[int32]float64)
|
|
currentCPUTimes = make(map[int32]float64)
|
|
)
|
|
|
|
func init() {
|
|
flag.StringVar(&processRegex, "regex", "", "Regular expression to match process name")
|
|
flag.Float64Var(&threshold, "threshold", 66, "CPU usage threshold")
|
|
flag.IntVar(&interval, "interval", 30, "Interval in seconds to check the processes")
|
|
flag.IntVar(&gracePeriod, "grace", 300, "Grace period in seconds after a notification is sent")
|
|
flag.Parse()
|
|
}
|
|
|
|
func main() {
|
|
if processRegex == "" {
|
|
fmt.Fprintln(os.Stderr, "Error: regex is required")
|
|
return
|
|
}
|
|
|
|
re, err := regexp.Compile(processRegex)
|
|
if err != nil {
|
|
fmt.Fprintln(os.Stderr, "Error compiling regex:", err)
|
|
return
|
|
}
|
|
|
|
for {
|
|
processes, err := process.Processes()
|
|
if err != nil {
|
|
fmt.Fprintln(os.Stderr, "Error fetching processes:", err)
|
|
return
|
|
}
|
|
|
|
for _, p := range processes {
|
|
pid := p.Pid
|
|
name, err := p.Name()
|
|
if err != nil {
|
|
continue
|
|
}
|
|
|
|
if re.MatchString(name) {
|
|
cpuTime, err := p.Times()
|
|
if err != nil {
|
|
continue
|
|
}
|
|
|
|
currentTotalTime := cpuTime.User + cpuTime.System
|
|
if prevTime, exists := prevCPUTimes[pid]; exists {
|
|
cpuPercent := (currentTotalTime - prevTime) / float64(interval) * 100.0
|
|
if cpuPercent > threshold && time.Since(lastNotified) > time.Duration(gracePeriod)*time.Second {
|
|
sendNotification(name, int(pid), cpuPercent)
|
|
lastNotified = time.Now()
|
|
}
|
|
}
|
|
|
|
currentCPUTimes[pid] = currentTotalTime
|
|
}
|
|
}
|
|
|
|
// Swap maps and clear the old currentCPUTimes
|
|
prevCPUTimes, currentCPUTimes = currentCPUTimes, prevCPUTimes
|
|
for k := range currentCPUTimes {
|
|
delete(currentCPUTimes, k)
|
|
}
|
|
|
|
time.Sleep(time.Duration(interval) * time.Second)
|
|
}
|
|
}
|
|
|
|
func sendNotification(processName string, pid int, cpuUsage float64) {
|
|
title := "High CPU Usage"
|
|
message := fmt.Sprintf("Process with PID %d (%s) is using %.2f%% CPU.", pid, processName, cpuUsage)
|
|
fmt.Fprintf(os.Stderr, "%s\n", message)
|
|
|
|
notification := notify.Notification{
|
|
Summary: title,
|
|
Body: message,
|
|
}
|
|
_, err := notification.Show()
|
|
if err != nil {
|
|
fmt.Fprintln(os.Stderr, "Failed to send notification:", err)
|
|
}
|
|
}
|