|
6 | 6 | package cpuscraper // import "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/hostmetricsreceiver/internal/scraper/cpuscraper" |
7 | 7 |
|
8 | 8 | import ( |
| 9 | + "context" |
| 10 | + "fmt" |
| 11 | + "time" |
| 12 | + |
9 | 13 | "github.com/prometheus/procfs" |
10 | 14 | "github.com/shirou/gopsutil/v4/cpu" |
| 15 | + "github.com/tklauser/go-sysconf" |
| 16 | + "go.opentelemetry.io/collector/featuregate" |
11 | 17 | "go.opentelemetry.io/collector/pdata/pcommon" |
12 | 18 | "go.opentelemetry.io/collector/scraper/scrapererror" |
13 | 19 |
|
| 20 | + "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/hostmetricsreceiver/internal/precision" |
| 21 | + "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/hostmetricsreceiver/internal/scraper/cpuscraper/internal/cputicks" |
14 | 22 | "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/hostmetricsreceiver/internal/scraper/cpuscraper/internal/metadata" |
15 | 23 | "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/hostmetricsreceiver/internal/scraper/cpuscraper/ucal" |
16 | 24 | ) |
17 | 25 |
|
18 | | -func (s *cpuScraper) recordCPUTimeStateDataPoints(now pcommon.Timestamp, cpuTime cpu.TimesStat) { |
19 | | - s.mb.RecordSystemCPUTimeDataPoint(now, cpuTime.User, cpuTime.CPU, metadata.AttributeStateUser) |
20 | | - s.mb.RecordSystemCPUTimeDataPoint(now, cpuTime.System, cpuTime.CPU, metadata.AttributeStateSystem) |
21 | | - s.mb.RecordSystemCPUTimeDataPoint(now, cpuTime.Idle, cpuTime.CPU, metadata.AttributeStateIdle) |
22 | | - s.mb.RecordSystemCPUTimeDataPoint(now, cpuTime.Irq, cpuTime.CPU, metadata.AttributeStateInterrupt) |
23 | | - s.mb.RecordSystemCPUTimeDataPoint(now, cpuTime.Nice, cpuTime.CPU, metadata.AttributeStateNice) |
24 | | - s.mb.RecordSystemCPUTimeDataPoint(now, cpuTime.Softirq, cpuTime.CPU, metadata.AttributeStateSoftirq) |
25 | | - s.mb.RecordSystemCPUTimeDataPoint(now, cpuTime.Steal, cpuTime.CPU, metadata.AttributeStateSteal) |
26 | | - s.mb.RecordSystemCPUTimeDataPoint(now, cpuTime.Iowait, cpuTime.CPU, metadata.AttributeStateWait) |
27 | | -} |
28 | | - |
29 | | -func (s *cpuScraper) recordCPUUtilization(now pcommon.Timestamp, cpuUtilization ucal.CPUUtilization) { |
30 | | - s.mb.RecordSystemCPUUtilizationDataPoint(now, cpuUtilization.User, cpuUtilization.CPU, metadata.AttributeStateUser) |
31 | | - s.mb.RecordSystemCPUUtilizationDataPoint(now, cpuUtilization.System, cpuUtilization.CPU, metadata.AttributeStateSystem) |
32 | | - s.mb.RecordSystemCPUUtilizationDataPoint(now, cpuUtilization.Idle, cpuUtilization.CPU, metadata.AttributeStateIdle) |
33 | | - s.mb.RecordSystemCPUUtilizationDataPoint(now, cpuUtilization.Irq, cpuUtilization.CPU, metadata.AttributeStateInterrupt) |
34 | | - s.mb.RecordSystemCPUUtilizationDataPoint(now, cpuUtilization.Nice, cpuUtilization.CPU, metadata.AttributeStateNice) |
35 | | - s.mb.RecordSystemCPUUtilizationDataPoint(now, cpuUtilization.Softirq, cpuUtilization.CPU, metadata.AttributeStateSoftirq) |
36 | | - s.mb.RecordSystemCPUUtilizationDataPoint(now, cpuUtilization.Steal, cpuUtilization.CPU, metadata.AttributeStateSteal) |
37 | | - s.mb.RecordSystemCPUUtilizationDataPoint(now, cpuUtilization.Iowait, cpuUtilization.CPU, metadata.AttributeStateWait) |
| 26 | +var useCPUTicks = featuregate.GlobalRegistry().MustRegister( |
| 27 | + "receiver.hostmetricsreceiver.UseCPUTicks", |
| 28 | + featuregate.StageAlpha, |
| 29 | + featuregate.WithRegisterDescription("Use raw uint64 CPU tick counts from /proc/stat instead of gopsutil for improved precision in CPU time and utilization metrics."), |
| 30 | + featuregate.WithRegisterReferenceURL("https://github.com/open-telemetry/opentelemetry-collector-contrib/issues/46177"), |
| 31 | + featuregate.WithRegisterFromVersion("v0.152.0"), |
| 32 | +) |
| 33 | + |
| 34 | +const defaultTicksPerSecond = 100 |
| 35 | + |
| 36 | +// tickReader reads per-CPU tick counts from the operating system. |
| 37 | +// Defined at the consumer for testability. |
| 38 | +type tickReader interface { |
| 39 | + ReadAll(ctx context.Context) ([]cputicks.Stat, error) |
| 40 | + TicksPerSecond() uint64 |
| 41 | +} |
| 42 | + |
| 43 | +func clockTicksPerSecond() uint64 { |
| 44 | + clkTck, err := sysconf.Sysconf(sysconf.SC_CLK_TCK) |
| 45 | + if err != nil || clkTck <= 0 { |
| 46 | + return defaultTicksPerSecond |
| 47 | + } |
| 48 | + return uint64(clkTck) |
| 49 | +} |
| 50 | + |
| 51 | +func newCPUEmitter(cfg *Config) func(context.Context, pcommon.Timestamp, *metadata.MetricsBuilder) error { |
| 52 | + if useCPUTicks.IsEnabled() { |
| 53 | + return newCputicksEmitter(cputicks.NewReader(cfg.rootPath, clockTicksPerSecond())) |
| 54 | + } |
| 55 | + return newGopsutilEmitter(cpu.TimesWithContext) |
| 56 | +} |
| 57 | + |
| 58 | +func newCputicksEmitter(reader tickReader) func(context.Context, pcommon.Timestamp, *metadata.MetricsBuilder) error { |
| 59 | + tickDuration := time.Second / time.Duration(reader.TicksPerSecond()) |
| 60 | + var prevTicks map[string]cputicks.Stat |
| 61 | + return func(ctx context.Context, now pcommon.Timestamp, mb *metadata.MetricsBuilder) error { |
| 62 | + ticks, err := reader.ReadAll(ctx) |
| 63 | + if err != nil { |
| 64 | + return err |
| 65 | + } |
| 66 | + |
| 67 | + recordTickTimes(now, ticks, tickDuration, mb) |
| 68 | + |
| 69 | + if prevTicks != nil { |
| 70 | + currTicks := make(map[string]cputicks.Stat, len(ticks)) |
| 71 | + for _, t := range ticks { |
| 72 | + currTicks[t.CPU] = t |
| 73 | + } |
| 74 | + for _, prev := range prevTicks { |
| 75 | + curr, ok := currTicks[prev.CPU] |
| 76 | + if !ok { |
| 77 | + return fmt.Errorf("getting ticks for cpu %s: %w", prev.CPU, ucal.ErrTimeStatNotFound) |
| 78 | + } |
| 79 | + recordTickUtilization(now, prev, curr, mb) |
| 80 | + } |
| 81 | + } |
| 82 | + prevTicks = make(map[string]cputicks.Stat, len(ticks)) |
| 83 | + for _, t := range ticks { |
| 84 | + prevTicks[t.CPU] = t |
| 85 | + } |
| 86 | + return nil |
| 87 | + } |
| 88 | +} |
| 89 | + |
| 90 | +func recordTickTimes(now pcommon.Timestamp, ticks []cputicks.Stat, tickDuration time.Duration, mb *metadata.MetricsBuilder) { |
| 91 | + for _, t := range ticks { |
| 92 | + mb.RecordSystemCPUTimeDataPoint(now, precision.Scale(t.User, tickDuration), t.CPU, metadata.AttributeStateUser) |
| 93 | + mb.RecordSystemCPUTimeDataPoint(now, precision.Scale(t.System, tickDuration), t.CPU, metadata.AttributeStateSystem) |
| 94 | + mb.RecordSystemCPUTimeDataPoint(now, precision.Scale(t.Idle, tickDuration), t.CPU, metadata.AttributeStateIdle) |
| 95 | + mb.RecordSystemCPUTimeDataPoint(now, precision.Scale(t.Irq, tickDuration), t.CPU, metadata.AttributeStateInterrupt) |
| 96 | + mb.RecordSystemCPUTimeDataPoint(now, precision.Scale(t.Nice, tickDuration), t.CPU, metadata.AttributeStateNice) |
| 97 | + mb.RecordSystemCPUTimeDataPoint(now, precision.Scale(t.Softirq, tickDuration), t.CPU, metadata.AttributeStateSoftirq) |
| 98 | + mb.RecordSystemCPUTimeDataPoint(now, precision.Scale(t.Steal, tickDuration), t.CPU, metadata.AttributeStateSteal) |
| 99 | + mb.RecordSystemCPUTimeDataPoint(now, precision.Scale(t.Iowait, tickDuration), t.CPU, metadata.AttributeStateWait) |
| 100 | + } |
| 101 | +} |
| 102 | + |
| 103 | +func recordTickUtilization(now pcommon.Timestamp, prev, curr cputicks.Stat, mb *metadata.MetricsBuilder) { |
| 104 | + deltaTotal := curr.Total() - prev.Total() |
| 105 | + if deltaTotal == 0 { |
| 106 | + recordZeroUtilization(now, curr.CPU, mb) |
| 107 | + return |
| 108 | + } |
| 109 | + mb.RecordSystemCPUUtilizationDataPoint(now, precision.Ratio(curr.User-prev.User, deltaTotal), curr.CPU, metadata.AttributeStateUser) |
| 110 | + mb.RecordSystemCPUUtilizationDataPoint(now, precision.Ratio(curr.System-prev.System, deltaTotal), curr.CPU, metadata.AttributeStateSystem) |
| 111 | + mb.RecordSystemCPUUtilizationDataPoint(now, precision.Ratio(curr.Idle-prev.Idle, deltaTotal), curr.CPU, metadata.AttributeStateIdle) |
| 112 | + mb.RecordSystemCPUUtilizationDataPoint(now, precision.Ratio(curr.Irq-prev.Irq, deltaTotal), curr.CPU, metadata.AttributeStateInterrupt) |
| 113 | + mb.RecordSystemCPUUtilizationDataPoint(now, precision.Ratio(curr.Nice-prev.Nice, deltaTotal), curr.CPU, metadata.AttributeStateNice) |
| 114 | + mb.RecordSystemCPUUtilizationDataPoint(now, precision.Ratio(curr.Softirq-prev.Softirq, deltaTotal), curr.CPU, metadata.AttributeStateSoftirq) |
| 115 | + mb.RecordSystemCPUUtilizationDataPoint(now, precision.Ratio(curr.Steal-prev.Steal, deltaTotal), curr.CPU, metadata.AttributeStateSteal) |
| 116 | + mb.RecordSystemCPUUtilizationDataPoint(now, precision.Ratio(curr.Iowait-prev.Iowait, deltaTotal), curr.CPU, metadata.AttributeStateWait) |
| 117 | +} |
| 118 | + |
| 119 | +func recordZeroUtilization(now pcommon.Timestamp, cpuName string, mb *metadata.MetricsBuilder) { |
| 120 | + for _, state := range []metadata.AttributeState{ |
| 121 | + metadata.AttributeStateUser, |
| 122 | + metadata.AttributeStateSystem, |
| 123 | + metadata.AttributeStateIdle, |
| 124 | + metadata.AttributeStateInterrupt, |
| 125 | + metadata.AttributeStateNice, |
| 126 | + metadata.AttributeStateSoftirq, |
| 127 | + metadata.AttributeStateSteal, |
| 128 | + metadata.AttributeStateWait, |
| 129 | + } { |
| 130 | + mb.RecordSystemCPUUtilizationDataPoint(now, 0, cpuName, state) |
| 131 | + } |
| 132 | +} |
| 133 | + |
| 134 | +func recordCPUTimeStateDataPoints(now pcommon.Timestamp, cpuTime cpu.TimesStat, mb *metadata.MetricsBuilder) { |
| 135 | + mb.RecordSystemCPUTimeDataPoint(now, cpuTime.User, cpuTime.CPU, metadata.AttributeStateUser) |
| 136 | + mb.RecordSystemCPUTimeDataPoint(now, cpuTime.System, cpuTime.CPU, metadata.AttributeStateSystem) |
| 137 | + mb.RecordSystemCPUTimeDataPoint(now, cpuTime.Idle, cpuTime.CPU, metadata.AttributeStateIdle) |
| 138 | + mb.RecordSystemCPUTimeDataPoint(now, cpuTime.Irq, cpuTime.CPU, metadata.AttributeStateInterrupt) |
| 139 | + mb.RecordSystemCPUTimeDataPoint(now, cpuTime.Nice, cpuTime.CPU, metadata.AttributeStateNice) |
| 140 | + mb.RecordSystemCPUTimeDataPoint(now, cpuTime.Softirq, cpuTime.CPU, metadata.AttributeStateSoftirq) |
| 141 | + mb.RecordSystemCPUTimeDataPoint(now, cpuTime.Steal, cpuTime.CPU, metadata.AttributeStateSteal) |
| 142 | + mb.RecordSystemCPUTimeDataPoint(now, cpuTime.Iowait, cpuTime.CPU, metadata.AttributeStateWait) |
| 143 | +} |
| 144 | + |
| 145 | +func recordCPUUtilization(now pcommon.Timestamp, cpuUtilization ucal.CPUUtilization, mb *metadata.MetricsBuilder) { |
| 146 | + mb.RecordSystemCPUUtilizationDataPoint(now, cpuUtilization.User, cpuUtilization.CPU, metadata.AttributeStateUser) |
| 147 | + mb.RecordSystemCPUUtilizationDataPoint(now, cpuUtilization.System, cpuUtilization.CPU, metadata.AttributeStateSystem) |
| 148 | + mb.RecordSystemCPUUtilizationDataPoint(now, cpuUtilization.Idle, cpuUtilization.CPU, metadata.AttributeStateIdle) |
| 149 | + mb.RecordSystemCPUUtilizationDataPoint(now, cpuUtilization.Irq, cpuUtilization.CPU, metadata.AttributeStateInterrupt) |
| 150 | + mb.RecordSystemCPUUtilizationDataPoint(now, cpuUtilization.Nice, cpuUtilization.CPU, metadata.AttributeStateNice) |
| 151 | + mb.RecordSystemCPUUtilizationDataPoint(now, cpuUtilization.Softirq, cpuUtilization.CPU, metadata.AttributeStateSoftirq) |
| 152 | + mb.RecordSystemCPUUtilizationDataPoint(now, cpuUtilization.Steal, cpuUtilization.CPU, metadata.AttributeStateSteal) |
| 153 | + mb.RecordSystemCPUUtilizationDataPoint(now, cpuUtilization.Iowait, cpuUtilization.CPU, metadata.AttributeStateWait) |
38 | 154 | } |
39 | 155 |
|
40 | 156 | func (*cpuScraper) getCPUInfo() ([]cpuInfo, error) { |
|
0 commit comments