nezha/cmd/agent/monitor/monitor.go

291 lines
7.3 KiB
Go
Raw Normal View History

2019-12-07 05:14:40 -05:00
package monitor
import (
"fmt"
"os/exec"
2021-08-07 22:06:44 -04:00
"runtime"
"strconv"
2019-12-09 03:02:49 -05:00
"strings"
"syscall"
2019-12-07 05:14:40 -05:00
"time"
2023-02-02 09:44:47 -05:00
"github.com/dean2021/goss"
"github.com/shirou/gopsutil/v3/cpu"
"github.com/shirou/gopsutil/v3/disk"
"github.com/shirou/gopsutil/v3/host"
"github.com/shirou/gopsutil/v3/load"
"github.com/shirou/gopsutil/v3/mem"
"github.com/shirou/gopsutil/v3/net"
"github.com/shirou/gopsutil/v3/process"
2019-12-07 05:14:40 -05:00
2020-11-10 21:07:45 -05:00
"github.com/naiba/nezha/model"
2019-12-07 05:14:40 -05:00
)
var (
Version string
expectDiskFsTypes = []string{
"apfs", "ext4", "ext3", "ext2", "f2fs", "reiserfs", "jfs", "btrfs",
"fuseblk", "zfs", "simfs", "ntfs", "fat32", "exfat", "xfs", "fuse.rclone",
}
excludeNetInterfaces = []string{
"lo", "tun", "docker", "veth", "br-", "vmbr", "vnet", "kube",
}
)
var (
netInSpeed, netOutSpeed, netInTransfer, netOutTransfer, lastUpdateNetStats uint64
cachedBootTime time.Time
)
2021-03-19 22:05:16 -04:00
// GetHost 获取主机硬件信息
2022-03-19 05:22:15 -04:00
func GetHost(agentConfig *model.AgentConfig) *model.Host {
var ret model.Host
2021-02-27 06:24:19 -05:00
var cpuType string
hi, err := host.Info()
if err != nil {
println("host.Info error:", err)
2021-02-27 06:24:19 -05:00
} else {
if hi.VirtualizationSystem != "" {
cpuType = "Virtual"
} else {
cpuType = "Physical"
}
ret.Platform = hi.Platform
ret.PlatformVersion = hi.PlatformVersion
ret.Arch = hi.KernelArch
ret.Virtualization = hi.VirtualizationSystem
ret.BootTime = hi.BootTime
2021-02-27 06:24:19 -05:00
}
2021-02-27 06:24:19 -05:00
cpuModelCount := make(map[string]int)
ci, err := cpu.Info()
if err != nil {
println("cpu.Info error:", err)
} else {
for i := 0; i < len(ci); i++ {
cpuModelCount[ci[i].ModelName]++
}
for model, count := range cpuModelCount {
ret.CPU = append(ret.CPU, fmt.Sprintf("%s %d %s Core", model, count, cpuType))
}
2021-02-27 06:24:19 -05:00
}
ret.DiskTotal, _ = getDiskTotalAndUsed(agentConfig)
mv, err := mem.VirtualMemory()
if err != nil {
println("mem.VirtualMemory error:", err)
} else {
ret.MemTotal = mv.Total
if runtime.GOOS != "windows" {
ret.SwapTotal = mv.SwapTotal
}
2019-12-07 05:14:40 -05:00
}
2021-03-19 22:05:16 -04:00
2021-08-07 22:06:44 -04:00
if runtime.GOOS == "windows" {
ms, err := mem.SwapMemory()
if err != nil {
println("mem.SwapMemory error:", err)
} else {
ret.SwapTotal = ms.Total
}
2021-08-07 22:06:44 -04:00
}
cachedBootTime = time.Unix(int64(hi.BootTime), 0)
ret.IP = CachedIP
ret.CountryCode = strings.ToLower(cachedCountry)
ret.Version = Version
return &ret
2019-12-07 05:14:40 -05:00
}
2022-03-19 05:22:15 -04:00
func GetState(agentConfig *model.AgentConfig, skipConnectionCount bool, skipProcsCount bool) *model.HostState {
var ret model.HostState
cp, err := cpu.Percent(0, false)
if err != nil {
println("cpu.Percent error:", err)
} else {
ret.CPU = cp[0]
}
2021-08-07 22:06:44 -04:00
vm, err := mem.VirtualMemory()
if err != nil {
println("mem.VirtualMemory error:", err)
} else {
ret.MemUsed = vm.Total - vm.Available
if runtime.GOOS != "windows" {
ret.SwapUsed = vm.SwapTotal - vm.SwapFree
}
}
2021-08-07 22:06:44 -04:00
if runtime.GOOS == "windows" {
// gopsutil 在 Windows 下不能正确取 swap
ms, err := mem.SwapMemory()
if err != nil {
println("mem.SwapMemory error:", err)
} else {
ret.SwapUsed = ms.Used
}
2021-08-07 22:06:44 -04:00
}
_, ret.DiskUsed = getDiskTotalAndUsed(agentConfig)
loadStat, err := load.Avg()
if err != nil {
println("load.Avg error:", err)
} else {
ret.Load1 = loadStat.Load1
ret.Load5 = loadStat.Load5
ret.Load15 = loadStat.Load15
2019-12-09 10:45:23 -05:00
}
var procs []int32
if !skipProcsCount {
procs, err = process.Pids()
if err != nil {
println("process.Pids error:", err)
} else {
ret.ProcessCount = uint64(len(procs))
}
}
var tcpConnCount, udpConnCount uint64
2021-09-27 09:18:09 -04:00
if !skipConnectionCount {
ss_err := true
if runtime.GOOS == "linux" {
2022-10-18 11:40:01 -04:00
tcpStat, err_tcp := goss.ConnectionsWithProtocol(goss.AF_INET, syscall.IPPROTO_TCP)
udpStat, err_udp := goss.ConnectionsWithProtocol(goss.AF_INET, syscall.IPPROTO_UDP)
if err_tcp == nil && err_udp == nil {
ss_err = false
tcpConnCount = uint64(len(tcpStat))
udpConnCount = uint64(len(udpStat))
}
2022-10-18 11:40:01 -04:00
if strings.Contains(CachedIP, ":") {
tcpStat6, err_tcp := goss.ConnectionsWithProtocol(goss.AF_INET6, syscall.IPPROTO_TCP)
udpStat6, err_udp := goss.ConnectionsWithProtocol(goss.AF_INET6, syscall.IPPROTO_UDP)
if err_tcp == nil && err_udp == nil {
ss_err = false
2022-10-18 12:11:31 -04:00
tcpConnCount += uint64(len(tcpStat6))
udpConnCount += uint64(len(udpStat6))
2022-10-18 11:40:01 -04:00
}
}
}
if ss_err {
conns, _ := net.Connections("all")
for i := 0; i < len(conns); i++ {
switch conns[i].Type {
case syscall.SOCK_STREAM:
tcpConnCount++
case syscall.SOCK_DGRAM:
udpConnCount++
}
}
}
}
ret.NetInTransfer, ret.NetOutTransfer = netInTransfer, netOutTransfer
ret.NetInSpeed, ret.NetOutSpeed = netInSpeed, netOutSpeed
ret.Uptime = uint64(time.Since(cachedBootTime).Seconds())
ret.TcpConnCount, ret.UdpConnCount = tcpConnCount, udpConnCount
return &ret
2019-12-09 10:45:23 -05:00
}
// TrackNetworkSpeed NIC监控统计流量与速度
2022-03-19 05:22:15 -04:00
func TrackNetworkSpeed(agentConfig *model.AgentConfig) {
2019-12-09 10:45:23 -05:00
var innerNetInTransfer, innerNetOutTransfer uint64
nc, err := net.IOCounters(true)
2019-12-07 05:14:40 -05:00
if err == nil {
for _, v := range nc {
2022-03-19 05:22:15 -04:00
if len(agentConfig.NICAllowlist) > 0 {
if !agentConfig.NICAllowlist[v.Name] {
continue
}
} else {
if isListContainsStr(excludeNetInterfaces, v.Name) {
continue
}
}
innerNetInTransfer += v.BytesRecv
innerNetOutTransfer += v.BytesSent
}
2019-12-13 01:51:51 -05:00
now := uint64(time.Now().Unix())
diff := now - lastUpdateNetStats
2019-12-09 10:45:23 -05:00
if diff > 0 {
netInSpeed = (innerNetInTransfer - netInTransfer) / diff
netOutSpeed = (innerNetOutTransfer - netOutTransfer) / diff
2019-12-09 10:45:23 -05:00
}
netInTransfer = innerNetInTransfer
netOutTransfer = innerNetOutTransfer
lastUpdateNetStats = now
2019-12-07 05:14:40 -05:00
}
}
2022-03-19 05:22:15 -04:00
func getDiskTotalAndUsed(agentConfig *model.AgentConfig) (total uint64, used uint64) {
2021-04-26 07:13:02 -04:00
devices := make(map[string]string)
2022-03-19 05:22:15 -04:00
if len(agentConfig.HardDrivePartitionAllowlist) > 0 {
// 如果配置了白名单,使用白名单的列表
for i, v := range agentConfig.HardDrivePartitionAllowlist {
devices[strconv.Itoa(i)] = v
2021-04-26 07:13:02 -04:00
}
2022-03-19 05:22:15 -04:00
} else {
// 否则使用默认过滤规则
diskList, _ := disk.Partitions(false)
for _, d := range diskList {
fsType := strings.ToLower(d.Fstype)
// 不统计 K8s 的虚拟挂载点https://github.com/shirou/gopsutil/issues/1007
if devices[d.Device] == "" && isListContainsStr(expectDiskFsTypes, fsType) && !strings.Contains(d.Mountpoint, "/var/lib/kubelet") {
devices[d.Device] = d.Mountpoint
}
2022-03-19 05:22:15 -04:00
}
}
for _, mountPath := range devices {
diskUsageOf, err := disk.Usage(mountPath)
if err == nil {
2021-04-26 07:13:02 -04:00
total += diskUsageOf.Total
2022-03-19 05:22:15 -04:00
used += diskUsageOf.Used
}
}
// Fallback 到这个方法,仅统计根路径,适用于OpenVZ之类的.
2022-03-19 05:22:15 -04:00
if runtime.GOOS == "linux" && total == 0 && used == 0 {
cmd := exec.Command("df")
out, err := cmd.CombinedOutput()
if err == nil {
s := strings.Split(string(out), "\n")
for _, c := range s {
info := strings.Fields(c)
if len(info) == 6 {
if info[5] == "/" {
total, _ = strconv.ParseUint(info[1], 0, 64)
used, _ = strconv.ParseUint(info[2], 0, 64)
// 默认获取的是1K块为单位的.
total = total * 1024
used = used * 1024
}
}
}
}
}
return
}
2021-04-26 07:13:02 -04:00
func isListContainsStr(list []string, str string) bool {
for i := 0; i < len(list); i++ {
2021-07-16 02:49:15 -04:00
if strings.Contains(str, list[i]) {
2021-04-26 07:13:02 -04:00
return true
}
}
return false
}
func println(v ...interface{}) {
fmt.Printf("NEZHA@%s>> ", time.Now().Format("2006-01-02 15:04:05"))
fmt.Println(v...)
}