package health import ( "context" "os" "strings" "time" agentpb "git.ipao.vip/rogee/go-sip/gen/agent" "github.com/shirou/gopsutil/v4/cpu" "github.com/shirou/gopsutil/v4/disk" "github.com/shirou/gopsutil/v4/mem" "github.com/shirou/gopsutil/v4/process" ) // Sampler uses gopsutil for host/process observations. Unsupported or // unconfigured dimensions stay explicitly unknown instead of being reported as // zero capacity. type Sampler struct { Now func() time.Time } func (s Sampler) Sample(ctx context.Context, spoolRoot string) *agentpb.ResourceSample { now := time.Now if s.Now != nil { now = s.Now } sample := &agentpb.ResourceSample{ ObservedAtUnixMs: now().UnixMilli(), SampleFresh: true, } var missing []string if values, err := cpu.PercentWithContext(ctx, 0, false); err != nil || len(values) == 0 { missing = append(missing, "cpu") } else { sample.CpuUsedRatio = values[0] / 100 } if value, err := mem.VirtualMemoryWithContext(ctx); err != nil { missing = append(missing, "memory") } else { sample.MemoryAvailableBytes = int64(value.Available) } if spoolRoot == "" { missing = append(missing, "spool") } else if value, err := disk.UsageWithContext(ctx, spoolRoot); err != nil { missing = append(missing, "spool") } else { sample.SpoolUsedBytes = int64(value.Used) sample.SpoolCapacityBytes = int64(value.Total) } proc, err := process.NewProcess(int32(os.Getpid())) if err != nil { missing = append(missing, "fd") } else if limits, limitErr := proc.RlimitUsageWithContext(ctx, true); limitErr != nil { missing = append(missing, "fd") } else { found := false for _, limit := range limits { if limit.Resource == process.RLIMIT_NOFILE { sample.FdUsed = int64(limit.Used) sample.FdLimit = int64(limit.Soft) found = true break } } if !found { missing = append(missing, "fd") } } // Media-port and AI-provider quotas are owned by the Cell/Dispatcher, not // this host sampler. Keep them unknown until those sources are connected. missing = append(missing, "media_ports", "ai_quota") if len(missing) != 0 { sample.SampleFresh = false sample.MissingReason = strings.Join(missing, ",") } return sample }