Skip to content

Commit b141a7a

Browse files
committed
fix: do not use new metric instances every Go memstats collection
1 parent 81df5d1 commit b141a7a

File tree

2 files changed

+69
-155
lines changed

2 files changed

+69
-155
lines changed

prometheus/go_collector.go

+67-153
Original file line numberDiff line numberDiff line change
@@ -24,176 +24,72 @@ import (
2424
// while eval closure works on runtime.MemStats, the struct from Go 1.17+ is
2525
// populated using runtime/metrics. Those are the defaults we can't alter.
2626
func goRuntimeMemStats() memStatsMetrics {
27+
const ns = "go_memstats" // see memstatNamespace(string)
2728
return memStatsMetrics{
2829
{
29-
desc: NewDesc(
30-
memstatNamespace("alloc_bytes"),
31-
"Number of bytes allocated in heap and currently in use. Equals to /memory/classes/heap/objects:bytes.",
32-
nil, nil,
33-
),
34-
eval: func(ms *runtime.MemStats) float64 { return float64(ms.Alloc) },
35-
valType: GaugeValue,
30+
eval: func(ms *runtime.MemStats) float64 { return float64(ms.Alloc) },
31+
metric: NewGauge(GaugeOpts{Namespace: ns, Name: "alloc_bytes", Help: "Number of bytes allocated in heap and currently in use. Equals to /memory/classes/heap/objects:bytes."}),
3632
}, {
37-
desc: NewDesc(
38-
memstatNamespace("alloc_bytes_total"),
39-
"Total number of bytes allocated in heap until now, even if released already. Equals to /gc/heap/allocs:bytes.",
40-
nil, nil,
41-
),
42-
eval: func(ms *runtime.MemStats) float64 { return float64(ms.TotalAlloc) },
43-
valType: CounterValue,
33+
eval: func(ms *runtime.MemStats) float64 { return float64(ms.TotalAlloc) },
34+
metric: NewCounter(CounterOpts{Namespace: ns, Name: "alloc_bytes_total", Help: "Total number of bytes allocated in heap until now, even if released already. Equals to /gc/heap/allocs:bytes."}),
4435
}, {
45-
desc: NewDesc(
46-
memstatNamespace("sys_bytes"),
47-
"Number of bytes obtained from system. Equals to /memory/classes/total:byte.",
48-
nil, nil,
49-
),
50-
eval: func(ms *runtime.MemStats) float64 { return float64(ms.Sys) },
51-
valType: GaugeValue,
36+
eval: func(ms *runtime.MemStats) float64 { return float64(ms.Sys) },
37+
metric: NewGauge(GaugeOpts{Namespace: ns, Name: "sys_bytes", Help: "Number of bytes obtained from system. Equals to /memory/classes/total:byte."}),
5238
}, {
53-
desc: NewDesc(
54-
memstatNamespace("mallocs_total"),
55-
// TODO(bwplotka): We could add go_memstats_heap_objects, probably useful for discovery. Let's gather more feedback, kind of a waste of bytes for everybody for compatibility reasons to keep both, and we can't really rename/remove useful metric.
56-
"Total number of heap objects allocated, both live and gc-ed. Semantically a counter version for go_memstats_heap_objects gauge. Equals to /gc/heap/allocs:objects + /gc/heap/tiny/allocs:objects.",
57-
nil, nil,
58-
),
59-
eval: func(ms *runtime.MemStats) float64 { return float64(ms.Mallocs) },
60-
valType: CounterValue,
39+
eval: func(ms *runtime.MemStats) float64 { return float64(ms.Mallocs) },
40+
// TODO(bwplotka): We could add go_memstats_heap_objects, probably useful for discovery. Let's gather more feedback, kind of a waste of bytes for everybody for compatibility reasons to keep both, and we can't really rename/remove useful metric.
41+
metric: NewCounter(CounterOpts{Namespace: ns, Name: "mallocs_total", Help: "Total number of heap objects allocated, both live and gc-ed. Semantically a counter version for go_memstats_heap_objects gauge. Equals to /gc/heap/allocs:objects + /gc/heap/tiny/allocs:objects."}),
6142
}, {
62-
desc: NewDesc(
63-
memstatNamespace("frees_total"),
64-
"Total number of heap objects frees. Equals to /gc/heap/frees:objects + /gc/heap/tiny/allocs:objects.",
65-
nil, nil,
66-
),
67-
eval: func(ms *runtime.MemStats) float64 { return float64(ms.Frees) },
68-
valType: CounterValue,
43+
eval: func(ms *runtime.MemStats) float64 { return float64(ms.Frees) },
44+
metric: NewCounter(CounterOpts{Namespace: ns, Name: "frees_total", Help: "Total number of heap objects frees. Equals to /gc/heap/frees:objects + /gc/heap/tiny/allocs:objects."}),
6945
}, {
70-
desc: NewDesc(
71-
memstatNamespace("heap_alloc_bytes"),
72-
"Number of heap bytes allocated and currently in use, same as go_memstats_alloc_bytes. Equals to /memory/classes/heap/objects:bytes.",
73-
nil, nil,
74-
),
75-
eval: func(ms *runtime.MemStats) float64 { return float64(ms.HeapAlloc) },
76-
valType: GaugeValue,
46+
eval: func(ms *runtime.MemStats) float64 { return float64(ms.HeapAlloc) },
47+
metric: NewGauge(GaugeOpts{Namespace: ns, Name: "heap_alloc_bytes", Help: "Number of heap bytes allocated and currently in use, same as go_memstats_alloc_bytes. Equals to /memory/classes/heap/objects:bytes."}),
7748
}, {
78-
desc: NewDesc(
79-
memstatNamespace("heap_sys_bytes"),
80-
"Number of heap bytes obtained from system. Equals to /memory/classes/heap/objects:bytes + /memory/classes/heap/unused:bytes + /memory/classes/heap/released:bytes + /memory/classes/heap/free:bytes.",
81-
nil, nil,
82-
),
83-
eval: func(ms *runtime.MemStats) float64 { return float64(ms.HeapSys) },
84-
valType: GaugeValue,
49+
eval: func(ms *runtime.MemStats) float64 { return float64(ms.HeapSys) },
50+
metric: NewGauge(GaugeOpts{Namespace: ns, Name: "heap_sys_bytes", Help: "Number of heap bytes obtained from system. Equals to /memory/classes/heap/objects:bytes + /memory/classes/heap/unused:bytes + /memory/classes/heap/released:bytes + /memory/classes/heap/free:bytes."}),
8551
}, {
86-
desc: NewDesc(
87-
memstatNamespace("heap_idle_bytes"),
88-
"Number of heap bytes waiting to be used. Equals to /memory/classes/heap/released:bytes + /memory/classes/heap/free:bytes.",
89-
nil, nil,
90-
),
91-
eval: func(ms *runtime.MemStats) float64 { return float64(ms.HeapIdle) },
92-
valType: GaugeValue,
52+
eval: func(ms *runtime.MemStats) float64 { return float64(ms.HeapIdle) },
53+
metric: NewGauge(GaugeOpts{Namespace: ns, Name: "heap_idle_bytes", Help: "Number of heap bytes waiting to be used. Equals to /memory/classes/heap/released:bytes + /memory/classes/heap/free:bytes."}),
9354
}, {
94-
desc: NewDesc(
95-
memstatNamespace("heap_inuse_bytes"),
96-
"Number of heap bytes that are in use. Equals to /memory/classes/heap/objects:bytes + /memory/classes/heap/unused:bytes",
97-
nil, nil,
98-
),
99-
eval: func(ms *runtime.MemStats) float64 { return float64(ms.HeapInuse) },
100-
valType: GaugeValue,
55+
eval: func(ms *runtime.MemStats) float64 { return float64(ms.HeapInuse) },
56+
metric: NewGauge(GaugeOpts{Namespace: ns, Name: "heap_inuse_bytes", Help: "Number of heap bytes that are in use. Equals to /memory/classes/heap/objects:bytes + /memory/classes/heap/unused:bytes"}),
10157
}, {
102-
desc: NewDesc(
103-
memstatNamespace("heap_released_bytes"),
104-
"Number of heap bytes released to OS. Equals to /memory/classes/heap/released:bytes.",
105-
nil, nil,
106-
),
107-
eval: func(ms *runtime.MemStats) float64 { return float64(ms.HeapReleased) },
108-
valType: GaugeValue,
58+
eval: func(ms *runtime.MemStats) float64 { return float64(ms.HeapReleased) },
59+
metric: NewGauge(GaugeOpts{Namespace: ns, Name: "heap_released_bytes", Help: "Number of heap bytes released to OS. Equals to /memory/classes/heap/released:bytes."}),
10960
}, {
110-
desc: NewDesc(
111-
memstatNamespace("heap_objects"),
112-
"Number of currently allocated objects. Equals to /gc/heap/objects:objects.",
113-
nil, nil,
114-
),
115-
eval: func(ms *runtime.MemStats) float64 { return float64(ms.HeapObjects) },
116-
valType: GaugeValue,
61+
eval: func(ms *runtime.MemStats) float64 { return float64(ms.HeapObjects) },
62+
metric: NewGauge(GaugeOpts{Namespace: ns, Name: "heap_objects", Help: "Number of currently allocated objects. Equals to /gc/heap/objects:objects."}),
11763
}, {
118-
desc: NewDesc(
119-
memstatNamespace("stack_inuse_bytes"),
120-
"Number of bytes obtained from system for stack allocator in non-CGO environments. Equals to /memory/classes/heap/stacks:bytes.",
121-
nil, nil,
122-
),
123-
eval: func(ms *runtime.MemStats) float64 { return float64(ms.StackInuse) },
124-
valType: GaugeValue,
64+
eval: func(ms *runtime.MemStats) float64 { return float64(ms.StackInuse) },
65+
metric: NewGauge(GaugeOpts{Namespace: ns, Name: "stack_inuse_bytes", Help: "Number of bytes obtained from system for stack allocator in non-CGO environments. Equals to /memory/classes/heap/stacks:bytes."}),
12566
}, {
126-
desc: NewDesc(
127-
memstatNamespace("stack_sys_bytes"),
128-
"Number of bytes obtained from system for stack allocator. Equals to /memory/classes/heap/stacks:bytes + /memory/classes/os-stacks:bytes.",
129-
nil, nil,
130-
),
131-
eval: func(ms *runtime.MemStats) float64 { return float64(ms.StackSys) },
132-
valType: GaugeValue,
67+
eval: func(ms *runtime.MemStats) float64 { return float64(ms.StackSys) },
68+
metric: NewGauge(GaugeOpts{Namespace: ns, Name: "stack_sys_bytes", Help: "Number of bytes obtained from system for stack allocator. Equals to /memory/classes/heap/stacks:bytes + /memory/classes/os-stacks:bytes."}),
13369
}, {
134-
desc: NewDesc(
135-
memstatNamespace("mspan_inuse_bytes"),
136-
"Number of bytes in use by mspan structures. Equals to /memory/classes/metadata/mspan/inuse:bytes.",
137-
nil, nil,
138-
),
139-
eval: func(ms *runtime.MemStats) float64 { return float64(ms.MSpanInuse) },
140-
valType: GaugeValue,
70+
eval: func(ms *runtime.MemStats) float64 { return float64(ms.MSpanInuse) },
71+
metric: NewGauge(GaugeOpts{Namespace: ns, Name: "mspan_inuse_bytes", Help: "Number of bytes in use by mspan structures. Equals to /memory/classes/metadata/mspan/inuse:bytes."}),
14172
}, {
142-
desc: NewDesc(
143-
memstatNamespace("mspan_sys_bytes"),
144-
"Number of bytes used for mspan structures obtained from system. Equals to /memory/classes/metadata/mspan/inuse:bytes + /memory/classes/metadata/mspan/free:bytes.",
145-
nil, nil,
146-
),
147-
eval: func(ms *runtime.MemStats) float64 { return float64(ms.MSpanSys) },
148-
valType: GaugeValue,
73+
eval: func(ms *runtime.MemStats) float64 { return float64(ms.MSpanSys) },
74+
metric: NewGauge(GaugeOpts{Namespace: ns, Name: "mspan_sys_bytes", Help: "Number of bytes used for mspan structures obtained from system. Equals to /memory/classes/metadata/mspan/inuse:bytes + /memory/classes/metadata/mspan/free:bytes."}),
14975
}, {
150-
desc: NewDesc(
151-
memstatNamespace("mcache_inuse_bytes"),
152-
"Number of bytes in use by mcache structures. Equals to /memory/classes/metadata/mcache/inuse:bytes.",
153-
nil, nil,
154-
),
155-
eval: func(ms *runtime.MemStats) float64 { return float64(ms.MCacheInuse) },
156-
valType: GaugeValue,
76+
eval: func(ms *runtime.MemStats) float64 { return float64(ms.MCacheInuse) },
77+
metric: NewGauge(GaugeOpts{Namespace: ns, Name: "mcache_inuse_bytes", Help: "Number of bytes in use by mcache structures. Equals to /memory/classes/metadata/mcache/inuse:bytes."}),
15778
}, {
158-
desc: NewDesc(
159-
memstatNamespace("mcache_sys_bytes"),
160-
"Number of bytes used for mcache structures obtained from system. Equals to /memory/classes/metadata/mcache/inuse:bytes + /memory/classes/metadata/mcache/free:bytes.",
161-
nil, nil,
162-
),
163-
eval: func(ms *runtime.MemStats) float64 { return float64(ms.MCacheSys) },
164-
valType: GaugeValue,
79+
eval: func(ms *runtime.MemStats) float64 { return float64(ms.MCacheSys) },
80+
metric: NewGauge(GaugeOpts{Namespace: ns, Name: "mcache_sys_bytes", Help: "Number of bytes used for mcache structures obtained from system. Equals to /memory/classes/metadata/mcache/inuse:bytes + /memory/classes/metadata/mcache/free:bytes."}),
16581
}, {
166-
desc: NewDesc(
167-
memstatNamespace("buck_hash_sys_bytes"),
168-
"Number of bytes used by the profiling bucket hash table. Equals to /memory/classes/profiling/buckets:bytes.",
169-
nil, nil,
170-
),
171-
eval: func(ms *runtime.MemStats) float64 { return float64(ms.BuckHashSys) },
172-
valType: GaugeValue,
82+
eval: func(ms *runtime.MemStats) float64 { return float64(ms.BuckHashSys) },
83+
metric: NewGauge(GaugeOpts{Namespace: ns, Name: "buck_hash_sys_bytes", Help: "Number of bytes used by the profiling bucket hash table. Equals to /memory/classes/profiling/buckets:bytes."}),
17384
}, {
174-
desc: NewDesc(
175-
memstatNamespace("gc_sys_bytes"),
176-
"Number of bytes used for garbage collection system metadata. Equals to /memory/classes/metadata/other:bytes.",
177-
nil, nil,
178-
),
179-
eval: func(ms *runtime.MemStats) float64 { return float64(ms.GCSys) },
180-
valType: GaugeValue,
85+
eval: func(ms *runtime.MemStats) float64 { return float64(ms.GCSys) },
86+
metric: NewGauge(GaugeOpts{Namespace: ns, Name: "gc_sys_bytes", Help: "Number of bytes used for garbage collection system metadata. Equals to /memory/classes/metadata/other:bytes."}),
18187
}, {
182-
desc: NewDesc(
183-
memstatNamespace("other_sys_bytes"),
184-
"Number of bytes used for other system allocations. Equals to /memory/classes/other:bytes.",
185-
nil, nil,
186-
),
187-
eval: func(ms *runtime.MemStats) float64 { return float64(ms.OtherSys) },
188-
valType: GaugeValue,
88+
eval: func(ms *runtime.MemStats) float64 { return float64(ms.OtherSys) },
89+
metric: NewGauge(GaugeOpts{Namespace: ns, Name: "other_sys_bytes", Help: "Number of bytes used for other system allocations. Equals to /memory/classes/other:bytes."}),
18990
}, {
190-
desc: NewDesc(
191-
memstatNamespace("next_gc_bytes"),
192-
"Number of heap bytes when next garbage collection will take place. Equals to /gc/heap/goal:bytes.",
193-
nil, nil,
194-
),
195-
eval: func(ms *runtime.MemStats) float64 { return float64(ms.NextGC) },
196-
valType: GaugeValue,
91+
eval: func(ms *runtime.MemStats) float64 { return float64(ms.NextGC) },
92+
metric: NewGauge(GaugeOpts{Namespace: ns, Name: "next_gc_bytes", Help: "Number of heap bytes when next garbage collection will take place. Equals to /gc/heap/goal:bytes."}),
19793
},
19894
}
19995
}
@@ -265,10 +161,28 @@ func memstatNamespace(s string) string {
265161
return "go_memstats_" + s
266162
}
267163

164+
// memStatsMetric provide description, evaluator, runtime/metrics name, and
165+
// value type for memstat metric.
166+
type memStatsMetric struct {
167+
last float64
168+
eval func(*runtime.MemStats) float64
169+
metric Metric
170+
}
171+
172+
func (m *memStatsMetric) update(memStats *runtime.MemStats) Metric {
173+
last := m.eval(memStats)
174+
switch actual := m.metric.(type) {
175+
case Counter:
176+
actual.Add(last - m.last)
177+
case Gauge:
178+
actual.Set(last)
179+
default:
180+
panic("unexpected metric type")
181+
}
182+
m.last = last
183+
return m.metric
184+
}
185+
268186
// memStatsMetrics provide description, evaluator, runtime/metrics name, and
269187
// value type for memstat metrics.
270-
type memStatsMetrics []struct {
271-
desc *Desc
272-
eval func(*runtime.MemStats) float64
273-
valType ValueType
274-
}
188+
type memStatsMetrics []memStatsMetric

prometheus/go_collector_latest.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -295,7 +295,7 @@ func attachOriginalName(desc, origName string) string {
295295
func (c *goCollector) Describe(ch chan<- *Desc) {
296296
c.base.Describe(ch)
297297
for _, i := range c.msMetrics {
298-
ch <- i.desc
298+
ch <- i.metric.Desc()
299299
}
300300
for _, m := range c.rmExposedMetrics {
301301
ch <- m.Desc()
@@ -364,7 +364,7 @@ func (c *goCollector) Collect(ch chan<- Metric) {
364364
var ms runtime.MemStats
365365
memStatsFromRM(&ms, c.sampleMap)
366366
for _, i := range c.msMetrics {
367-
ch <- MustNewConstMetric(i.desc, i.valType, i.eval(&ms))
367+
ch <- i.update(&ms)
368368
}
369369
}
370370
}

0 commit comments

Comments
 (0)