GoDoxy/internal/metrics/systeminfo/system_info.go
2025-02-15 05:44:47 +08:00

152 lines
3.8 KiB
Go

package systeminfo
import (
"context"
"encoding/json"
"errors"
"time"
"github.com/shirou/gopsutil/v4/cpu"
"github.com/shirou/gopsutil/v4/disk"
"github.com/shirou/gopsutil/v4/mem"
"github.com/shirou/gopsutil/v4/net"
"github.com/shirou/gopsutil/v4/sensors"
"github.com/yusing/go-proxy/internal/common"
"github.com/yusing/go-proxy/internal/gperr"
"github.com/yusing/go-proxy/internal/logging"
"github.com/yusing/go-proxy/internal/metrics/period"
"github.com/yusing/go-proxy/internal/utils/strutils"
)
type SystemInfo struct {
Timestamp time.Time
CPUAverage float64
Memory *mem.VirtualMemoryStat
Disk *disk.UsageStat
NetworkIO *net.IOCountersStat
NetworkUp float64
NetworkDown float64
Sensors []sensors.TemperatureStat
}
var Poller = period.NewPoller("system_info", getSystemInfo)
func init() {
Poller.Start()
}
func _() { // check if this behavior is not changed
var _ sensors.Warnings = disk.Warnings{}
}
func getSystemInfo(ctx context.Context, lastResult *SystemInfo) (*SystemInfo, error) {
errs := gperr.NewBuilder("failed to get system info")
var systemInfo SystemInfo
if !common.MetricsDisableCPU {
cpuAverage, err := cpu.PercentWithContext(ctx, 150*time.Millisecond, false)
if err != nil {
errs.Add(err)
} else {
systemInfo.CPUAverage = cpuAverage[0]
}
}
if !common.MetricsDisableMemory {
memoryInfo, err := mem.VirtualMemory()
if err != nil {
errs.Add(err)
}
systemInfo.Memory = memoryInfo
}
if !common.MetricsDisableDisk {
diskInfo, err := disk.Usage("/")
if err != nil {
errs.Add(err)
}
systemInfo.Disk = diskInfo
}
if !common.MetricsDisableNetwork {
networkIO, err := net.IOCounters(false)
if err != nil {
errs.Add(err)
} else {
networkIO := networkIO[0]
systemInfo.NetworkIO = &networkIO
var networkUp, networkDown float64
if lastResult != nil {
interval := time.Since(lastResult.Timestamp).Seconds()
networkUp = float64(networkIO.BytesSent-lastResult.NetworkIO.BytesSent) / interval
networkDown = float64(networkIO.BytesRecv-lastResult.NetworkIO.BytesRecv) / interval
}
systemInfo.NetworkUp = networkUp
systemInfo.NetworkDown = networkDown
}
}
if !common.MetricsDisableSensors {
sensorsInfo, err := sensors.SensorsTemperatures()
if err != nil {
errs.Add(err)
}
systemInfo.Sensors = sensorsInfo
}
if errs.HasError() {
allWarnings := gperr.NewBuilder("")
allErrors := gperr.NewBuilder("failed to get system info")
errs.ForEach(func(err error) {
// disk.Warnings has the same type
// all Warnings are alias of common.Warnings from "github.com/shirou/gopsutil/v4/internal/common"
// see line 37
warnings := new(sensors.Warnings)
if errors.As(err, &warnings) {
for _, warning := range warnings.List {
allWarnings.Add(warning)
}
} else {
allErrors.Add(err)
}
})
if allWarnings.HasError() {
logging.Warn().Msg(allWarnings.String())
}
if allErrors.HasError() {
return nil, allErrors.Error()
}
}
return &systemInfo, nil
}
func (s *SystemInfo) MarshalJSON() ([]byte, error) {
return json.Marshal(map[string]any{
"timestamp": s.Timestamp.Unix(),
"time": strutils.FormatTime(s.Timestamp),
"cpu_average": s.CPUAverage,
"memory": map[string]any{
"total": s.Memory.Total,
"available": s.Memory.Available,
"used": s.Memory.Used,
"used_percent": s.Memory.UsedPercent,
},
"disk": map[string]any{
"path": s.Disk.Path,
"fstype": s.Disk.Fstype,
"total": s.Disk.Total,
"used": s.Disk.Used,
"used_percent": s.Disk.UsedPercent,
"free": s.Disk.Free,
},
"network": map[string]any{
"name": s.NetworkIO.Name,
"bytes_sent": s.NetworkIO.BytesSent,
"bytes_recv": s.NetworkIO.BytesRecv,
"upload_speed": s.NetworkUp,
"download_speed": s.NetworkDown,
},
"sensors": s.Sensors,
})
}