blob: a96ed1cee885be85f781aa5efcd3abe7566c3b0f [file] [log] [blame]
khenaidooab1f7bd2019-11-14 14:00:27 -05001// Copyright 2018 The Prometheus Authors
2// Licensed under the Apache License, Version 2.0 (the "License");
3// you may not use this file except in compliance with the License.
4// You may obtain a copy of the License at
5//
6// http://www.apache.org/licenses/LICENSE-2.0
7//
8// Unless required by applicable law or agreed to in writing, software
9// distributed under the License is distributed on an "AS IS" BASIS,
10// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11// See the License for the specific language governing permissions and
12// limitations under the License.
13
14package prometheus
15
16import (
17 "runtime"
18 "runtime/debug"
19 "sync"
20 "time"
21)
22
23type goCollector struct {
24 goroutinesDesc *Desc
25 threadsDesc *Desc
26 gcDesc *Desc
27 goInfoDesc *Desc
28
29 // ms... are memstats related.
30 msLast *runtime.MemStats // Previously collected memstats.
31 msLastTimestamp time.Time
32 msMtx sync.Mutex // Protects msLast and msLastTimestamp.
33 msMetrics memStatsMetrics
34 msRead func(*runtime.MemStats) // For mocking in tests.
35 msMaxWait time.Duration // Wait time for fresh memstats.
36 msMaxAge time.Duration // Maximum allowed age of old memstats.
37}
38
khenaidood948f772021-08-11 17:49:24 -040039// NewGoCollector is the obsolete version of collectors.NewGoCollector.
40// See there for documentation.
khenaidooab1f7bd2019-11-14 14:00:27 -050041//
khenaidood948f772021-08-11 17:49:24 -040042// Deprecated: Use collectors.NewGoCollector instead.
khenaidooab1f7bd2019-11-14 14:00:27 -050043func NewGoCollector() Collector {
44 return &goCollector{
45 goroutinesDesc: NewDesc(
46 "go_goroutines",
47 "Number of goroutines that currently exist.",
48 nil, nil),
49 threadsDesc: NewDesc(
50 "go_threads",
51 "Number of OS threads created.",
52 nil, nil),
53 gcDesc: NewDesc(
54 "go_gc_duration_seconds",
khenaidood948f772021-08-11 17:49:24 -040055 "A summary of the pause duration of garbage collection cycles.",
khenaidooab1f7bd2019-11-14 14:00:27 -050056 nil, nil),
57 goInfoDesc: NewDesc(
58 "go_info",
59 "Information about the Go environment.",
60 nil, Labels{"version": runtime.Version()}),
61 msLast: &runtime.MemStats{},
62 msRead: runtime.ReadMemStats,
63 msMaxWait: time.Second,
64 msMaxAge: 5 * time.Minute,
65 msMetrics: memStatsMetrics{
66 {
67 desc: NewDesc(
68 memstatNamespace("alloc_bytes"),
69 "Number of bytes allocated and still in use.",
70 nil, nil,
71 ),
72 eval: func(ms *runtime.MemStats) float64 { return float64(ms.Alloc) },
73 valType: GaugeValue,
74 }, {
75 desc: NewDesc(
76 memstatNamespace("alloc_bytes_total"),
77 "Total number of bytes allocated, even if freed.",
78 nil, nil,
79 ),
80 eval: func(ms *runtime.MemStats) float64 { return float64(ms.TotalAlloc) },
81 valType: CounterValue,
82 }, {
83 desc: NewDesc(
84 memstatNamespace("sys_bytes"),
85 "Number of bytes obtained from system.",
86 nil, nil,
87 ),
88 eval: func(ms *runtime.MemStats) float64 { return float64(ms.Sys) },
89 valType: GaugeValue,
90 }, {
91 desc: NewDesc(
92 memstatNamespace("lookups_total"),
93 "Total number of pointer lookups.",
94 nil, nil,
95 ),
96 eval: func(ms *runtime.MemStats) float64 { return float64(ms.Lookups) },
97 valType: CounterValue,
98 }, {
99 desc: NewDesc(
100 memstatNamespace("mallocs_total"),
101 "Total number of mallocs.",
102 nil, nil,
103 ),
104 eval: func(ms *runtime.MemStats) float64 { return float64(ms.Mallocs) },
105 valType: CounterValue,
106 }, {
107 desc: NewDesc(
108 memstatNamespace("frees_total"),
109 "Total number of frees.",
110 nil, nil,
111 ),
112 eval: func(ms *runtime.MemStats) float64 { return float64(ms.Frees) },
113 valType: CounterValue,
114 }, {
115 desc: NewDesc(
116 memstatNamespace("heap_alloc_bytes"),
117 "Number of heap bytes allocated and still in use.",
118 nil, nil,
119 ),
120 eval: func(ms *runtime.MemStats) float64 { return float64(ms.HeapAlloc) },
121 valType: GaugeValue,
122 }, {
123 desc: NewDesc(
124 memstatNamespace("heap_sys_bytes"),
125 "Number of heap bytes obtained from system.",
126 nil, nil,
127 ),
128 eval: func(ms *runtime.MemStats) float64 { return float64(ms.HeapSys) },
129 valType: GaugeValue,
130 }, {
131 desc: NewDesc(
132 memstatNamespace("heap_idle_bytes"),
133 "Number of heap bytes waiting to be used.",
134 nil, nil,
135 ),
136 eval: func(ms *runtime.MemStats) float64 { return float64(ms.HeapIdle) },
137 valType: GaugeValue,
138 }, {
139 desc: NewDesc(
140 memstatNamespace("heap_inuse_bytes"),
141 "Number of heap bytes that are in use.",
142 nil, nil,
143 ),
144 eval: func(ms *runtime.MemStats) float64 { return float64(ms.HeapInuse) },
145 valType: GaugeValue,
146 }, {
147 desc: NewDesc(
148 memstatNamespace("heap_released_bytes"),
149 "Number of heap bytes released to OS.",
150 nil, nil,
151 ),
152 eval: func(ms *runtime.MemStats) float64 { return float64(ms.HeapReleased) },
153 valType: GaugeValue,
154 }, {
155 desc: NewDesc(
156 memstatNamespace("heap_objects"),
157 "Number of allocated objects.",
158 nil, nil,
159 ),
160 eval: func(ms *runtime.MemStats) float64 { return float64(ms.HeapObjects) },
161 valType: GaugeValue,
162 }, {
163 desc: NewDesc(
164 memstatNamespace("stack_inuse_bytes"),
165 "Number of bytes in use by the stack allocator.",
166 nil, nil,
167 ),
168 eval: func(ms *runtime.MemStats) float64 { return float64(ms.StackInuse) },
169 valType: GaugeValue,
170 }, {
171 desc: NewDesc(
172 memstatNamespace("stack_sys_bytes"),
173 "Number of bytes obtained from system for stack allocator.",
174 nil, nil,
175 ),
176 eval: func(ms *runtime.MemStats) float64 { return float64(ms.StackSys) },
177 valType: GaugeValue,
178 }, {
179 desc: NewDesc(
180 memstatNamespace("mspan_inuse_bytes"),
181 "Number of bytes in use by mspan structures.",
182 nil, nil,
183 ),
184 eval: func(ms *runtime.MemStats) float64 { return float64(ms.MSpanInuse) },
185 valType: GaugeValue,
186 }, {
187 desc: NewDesc(
188 memstatNamespace("mspan_sys_bytes"),
189 "Number of bytes used for mspan structures obtained from system.",
190 nil, nil,
191 ),
192 eval: func(ms *runtime.MemStats) float64 { return float64(ms.MSpanSys) },
193 valType: GaugeValue,
194 }, {
195 desc: NewDesc(
196 memstatNamespace("mcache_inuse_bytes"),
197 "Number of bytes in use by mcache structures.",
198 nil, nil,
199 ),
200 eval: func(ms *runtime.MemStats) float64 { return float64(ms.MCacheInuse) },
201 valType: GaugeValue,
202 }, {
203 desc: NewDesc(
204 memstatNamespace("mcache_sys_bytes"),
205 "Number of bytes used for mcache structures obtained from system.",
206 nil, nil,
207 ),
208 eval: func(ms *runtime.MemStats) float64 { return float64(ms.MCacheSys) },
209 valType: GaugeValue,
210 }, {
211 desc: NewDesc(
212 memstatNamespace("buck_hash_sys_bytes"),
213 "Number of bytes used by the profiling bucket hash table.",
214 nil, nil,
215 ),
216 eval: func(ms *runtime.MemStats) float64 { return float64(ms.BuckHashSys) },
217 valType: GaugeValue,
218 }, {
219 desc: NewDesc(
220 memstatNamespace("gc_sys_bytes"),
221 "Number of bytes used for garbage collection system metadata.",
222 nil, nil,
223 ),
224 eval: func(ms *runtime.MemStats) float64 { return float64(ms.GCSys) },
225 valType: GaugeValue,
226 }, {
227 desc: NewDesc(
228 memstatNamespace("other_sys_bytes"),
229 "Number of bytes used for other system allocations.",
230 nil, nil,
231 ),
232 eval: func(ms *runtime.MemStats) float64 { return float64(ms.OtherSys) },
233 valType: GaugeValue,
234 }, {
235 desc: NewDesc(
236 memstatNamespace("next_gc_bytes"),
237 "Number of heap bytes when next garbage collection will take place.",
238 nil, nil,
239 ),
240 eval: func(ms *runtime.MemStats) float64 { return float64(ms.NextGC) },
241 valType: GaugeValue,
242 }, {
243 desc: NewDesc(
244 memstatNamespace("last_gc_time_seconds"),
245 "Number of seconds since 1970 of last garbage collection.",
246 nil, nil,
247 ),
248 eval: func(ms *runtime.MemStats) float64 { return float64(ms.LastGC) / 1e9 },
249 valType: GaugeValue,
250 }, {
251 desc: NewDesc(
252 memstatNamespace("gc_cpu_fraction"),
253 "The fraction of this program's available CPU time used by the GC since the program started.",
254 nil, nil,
255 ),
256 eval: func(ms *runtime.MemStats) float64 { return ms.GCCPUFraction },
257 valType: GaugeValue,
258 },
259 },
260 }
261}
262
263func memstatNamespace(s string) string {
264 return "go_memstats_" + s
265}
266
267// Describe returns all descriptions of the collector.
268func (c *goCollector) Describe(ch chan<- *Desc) {
269 ch <- c.goroutinesDesc
270 ch <- c.threadsDesc
271 ch <- c.gcDesc
272 ch <- c.goInfoDesc
273 for _, i := range c.msMetrics {
274 ch <- i.desc
275 }
276}
277
278// Collect returns the current state of all metrics of the collector.
279func (c *goCollector) Collect(ch chan<- Metric) {
280 var (
281 ms = &runtime.MemStats{}
282 done = make(chan struct{})
283 )
284 // Start reading memstats first as it might take a while.
285 go func() {
286 c.msRead(ms)
287 c.msMtx.Lock()
288 c.msLast = ms
289 c.msLastTimestamp = time.Now()
290 c.msMtx.Unlock()
291 close(done)
292 }()
293
294 ch <- MustNewConstMetric(c.goroutinesDesc, GaugeValue, float64(runtime.NumGoroutine()))
295 n, _ := runtime.ThreadCreateProfile(nil)
296 ch <- MustNewConstMetric(c.threadsDesc, GaugeValue, float64(n))
297
298 var stats debug.GCStats
299 stats.PauseQuantiles = make([]time.Duration, 5)
300 debug.ReadGCStats(&stats)
301
302 quantiles := make(map[float64]float64)
303 for idx, pq := range stats.PauseQuantiles[1:] {
304 quantiles[float64(idx+1)/float64(len(stats.PauseQuantiles)-1)] = pq.Seconds()
305 }
306 quantiles[0.0] = stats.PauseQuantiles[0].Seconds()
307 ch <- MustNewConstSummary(c.gcDesc, uint64(stats.NumGC), stats.PauseTotal.Seconds(), quantiles)
308
309 ch <- MustNewConstMetric(c.goInfoDesc, GaugeValue, 1)
310
311 timer := time.NewTimer(c.msMaxWait)
312 select {
313 case <-done: // Our own ReadMemStats succeeded in time. Use it.
314 timer.Stop() // Important for high collection frequencies to not pile up timers.
315 c.msCollect(ch, ms)
316 return
317 case <-timer.C: // Time out, use last memstats if possible. Continue below.
318 }
319 c.msMtx.Lock()
320 if time.Since(c.msLastTimestamp) < c.msMaxAge {
321 // Last memstats are recent enough. Collect from them under the lock.
322 c.msCollect(ch, c.msLast)
323 c.msMtx.Unlock()
324 return
325 }
326 // If we are here, the last memstats are too old or don't exist. We have
327 // to wait until our own ReadMemStats finally completes. For that to
328 // happen, we have to release the lock.
329 c.msMtx.Unlock()
330 <-done
331 c.msCollect(ch, ms)
332}
333
334func (c *goCollector) msCollect(ch chan<- Metric, ms *runtime.MemStats) {
335 for _, i := range c.msMetrics {
336 ch <- MustNewConstMetric(i.desc, i.valType, i.eval(ms))
337 }
338}
339
340// memStatsMetrics provide description, value, and value type for memstat metrics.
341type memStatsMetrics []struct {
342 desc *Desc
343 eval func(*runtime.MemStats) float64
344 valType ValueType
345}
346
khenaidood948f772021-08-11 17:49:24 -0400347// NewBuildInfoCollector is the obsolete version of collectors.NewBuildInfoCollector.
348// See there for documentation.
khenaidooab1f7bd2019-11-14 14:00:27 -0500349//
khenaidood948f772021-08-11 17:49:24 -0400350// Deprecated: Use collectors.NewBuildInfoCollector instead.
khenaidooab1f7bd2019-11-14 14:00:27 -0500351func NewBuildInfoCollector() Collector {
khenaidood948f772021-08-11 17:49:24 -0400352 path, version, sum := "unknown", "unknown", "unknown"
353 if bi, ok := debug.ReadBuildInfo(); ok {
354 path = bi.Main.Path
355 version = bi.Main.Version
356 sum = bi.Main.Sum
357 }
khenaidooab1f7bd2019-11-14 14:00:27 -0500358 c := &selfCollector{MustNewConstMetric(
359 NewDesc(
360 "go_build_info",
361 "Build information about the main Go module.",
362 nil, Labels{"path": path, "version": version, "checksum": sum},
363 ),
364 GaugeValue, 1)}
365 c.init(c.self)
366 return c
367}