1 package runmetrics
2
3 import (
4 "errors"
5 "runtime"
6 "sync"
7 "time"
8
9 "go.opencensus.io/metric"
10 "go.opencensus.io/metric/metricdata"
11 "go.opencensus.io/metric/metricproducer"
12 )
13
14 type (
15
16
17
18 producer struct {
19 options RunMetricOptions
20 reg *metric.Registry
21
22 deprecatedMemStats *deprecatedMemStats
23 memStats *memStats
24 cpuStats *cpuStats
25 }
26
27
28 RunMetricOptions struct {
29 EnableCPU bool
30 EnableMemory bool
31 Prefix string
32 UseDerivedCumulative bool
33 }
34
35 deprecatedMemStats struct {
36 memStats runtime.MemStats
37
38 memAlloc *metric.Int64GaugeEntry
39 memTotal *metric.Int64GaugeEntry
40 memSys *metric.Int64GaugeEntry
41 memLookups *metric.Int64GaugeEntry
42 memMalloc *metric.Int64GaugeEntry
43 memFrees *metric.Int64GaugeEntry
44
45 heapAlloc *metric.Int64GaugeEntry
46 heapSys *metric.Int64GaugeEntry
47 heapIdle *metric.Int64GaugeEntry
48 heapInuse *metric.Int64GaugeEntry
49 heapObjects *metric.Int64GaugeEntry
50 heapReleased *metric.Int64GaugeEntry
51
52 stackInuse *metric.Int64GaugeEntry
53 stackSys *metric.Int64GaugeEntry
54 stackMSpanInuse *metric.Int64GaugeEntry
55 stackMSpanSys *metric.Int64GaugeEntry
56 stackMCacheInuse *metric.Int64GaugeEntry
57 stackMCacheSys *metric.Int64GaugeEntry
58
59 otherSys *metric.Int64GaugeEntry
60 gcSys *metric.Int64GaugeEntry
61 numGC *metric.Int64GaugeEntry
62 numForcedGC *metric.Int64GaugeEntry
63 nextGC *metric.Int64GaugeEntry
64 lastGC *metric.Int64GaugeEntry
65 pauseTotalNs *metric.Int64GaugeEntry
66 gcCPUFraction *metric.Float64Entry
67 }
68
69 memStats struct {
70 memStats runtime.MemStats
71
72 memAlloc *metric.Int64GaugeEntry
73 memTotal *metric.Int64DerivedCumulative
74 memSys *metric.Int64GaugeEntry
75 memLookups *metric.Int64DerivedCumulative
76 memMalloc *metric.Int64DerivedCumulative
77 memFrees *metric.Int64DerivedCumulative
78
79 heapAlloc *metric.Int64GaugeEntry
80 heapSys *metric.Int64GaugeEntry
81 heapIdle *metric.Int64GaugeEntry
82 heapInuse *metric.Int64GaugeEntry
83 heapObjects *metric.Int64GaugeEntry
84 heapReleased *metric.Int64DerivedCumulative
85
86 stackInuse *metric.Int64GaugeEntry
87 stackSys *metric.Int64GaugeEntry
88 stackMSpanInuse *metric.Int64GaugeEntry
89 stackMSpanSys *metric.Int64GaugeEntry
90 stackMCacheInuse *metric.Int64GaugeEntry
91 stackMCacheSys *metric.Int64GaugeEntry
92
93 otherSys *metric.Int64GaugeEntry
94 gcSys *metric.Int64GaugeEntry
95 numGC *metric.Int64DerivedCumulative
96 numForcedGC *metric.Int64DerivedCumulative
97 nextGC *metric.Int64GaugeEntry
98 lastGC *metric.Int64GaugeEntry
99 pauseTotalNs *metric.Int64DerivedCumulative
100 gcCPUFraction *metric.Float64Entry
101 }
102
103 cpuStats struct {
104 numGoroutines *metric.Int64GaugeEntry
105 numCgoCalls *metric.Int64GaugeEntry
106 }
107 )
108
109 var (
110 _ metricproducer.Producer = (*producer)(nil)
111 enableMutex sync.Mutex
112 enabledProducer *producer
113 )
114
115
116
117
118
119
120
121 func Enable(options RunMetricOptions) error {
122 producer := &producer{options: options, reg: metric.NewRegistry()}
123 var err error
124
125 if options.EnableMemory {
126 switch options.UseDerivedCumulative {
127 case true:
128 producer.memStats, err = newMemStats(producer)
129 if err != nil {
130 return err
131 }
132 default:
133 producer.deprecatedMemStats, err = newDeprecatedMemStats(producer)
134 if err != nil {
135 return err
136 }
137 }
138 }
139
140 if options.EnableCPU {
141 producer.cpuStats, err = newCPUStats(producer)
142 if err != nil {
143 return err
144 }
145 }
146
147 enableMutex.Lock()
148 defer enableMutex.Unlock()
149
150 metricproducer.GlobalManager().DeleteProducer(enabledProducer)
151 metricproducer.GlobalManager().AddProducer(producer)
152 enabledProducer = producer
153
154 return nil
155 }
156
157
158 func Disable() {
159 enableMutex.Lock()
160 defer enableMutex.Unlock()
161
162 metricproducer.GlobalManager().DeleteProducer(enabledProducer)
163 enabledProducer = nil
164 }
165
166
167 func (p *producer) Read() []*metricdata.Metric {
168 if p.memStats != nil {
169 p.memStats.read()
170 }
171
172 if p.cpuStats != nil {
173 p.cpuStats.read()
174 }
175
176 return p.reg.Read()
177 }
178
179 func newDeprecatedMemStats(producer *producer) (*deprecatedMemStats, error) {
180 var err error
181 memStats := &deprecatedMemStats{}
182
183
184 memStats.memAlloc, err = producer.createInt64GaugeEntry("process/memory_alloc", "Number of bytes currently allocated in use", metricdata.UnitBytes)
185 if err != nil {
186 return nil, err
187 }
188
189 memStats.memTotal, err = producer.createInt64GaugeEntry("process/total_memory_alloc", "Number of allocations in total", metricdata.UnitBytes)
190 if err != nil {
191 return nil, err
192 }
193
194 memStats.memSys, err = producer.createInt64GaugeEntry("process/sys_memory_alloc", "Number of bytes given to the process to use in total", metricdata.UnitBytes)
195 if err != nil {
196 return nil, err
197 }
198
199 memStats.memLookups, err = producer.createInt64GaugeEntry("process/memory_lookups", "Cumulative number of pointer lookups performed by the runtime", metricdata.UnitDimensionless)
200 if err != nil {
201 return nil, err
202 }
203
204 memStats.memMalloc, err = producer.createInt64GaugeEntry("process/memory_malloc", "Cumulative count of heap objects allocated", metricdata.UnitDimensionless)
205 if err != nil {
206 return nil, err
207 }
208
209 memStats.memFrees, err = producer.createInt64GaugeEntry("process/memory_frees", "Cumulative count of heap objects freed", metricdata.UnitDimensionless)
210 if err != nil {
211 return nil, err
212 }
213
214
215 memStats.heapAlloc, err = producer.createInt64GaugeEntry("process/heap_alloc", "Process heap allocation", metricdata.UnitBytes)
216 if err != nil {
217 return nil, err
218 }
219
220 memStats.heapSys, err = producer.createInt64GaugeEntry("process/sys_heap", "Bytes of heap memory obtained from the OS", metricdata.UnitBytes)
221 if err != nil {
222 return nil, err
223 }
224
225 memStats.heapIdle, err = producer.createInt64GaugeEntry("process/heap_idle", "Bytes in idle (unused) spans", metricdata.UnitBytes)
226 if err != nil {
227 return nil, err
228 }
229
230 memStats.heapInuse, err = producer.createInt64GaugeEntry("process/heap_inuse", "Bytes in in-use spans", metricdata.UnitBytes)
231 if err != nil {
232 return nil, err
233 }
234
235 memStats.heapObjects, err = producer.createInt64GaugeEntry("process/heap_objects", "The number of objects allocated on the heap", metricdata.UnitDimensionless)
236 if err != nil {
237 return nil, err
238 }
239
240 memStats.heapReleased, err = producer.createInt64GaugeEntry("process/heap_release", "The cumulative number of objects released from the heap", metricdata.UnitBytes)
241 if err != nil {
242 return nil, err
243 }
244
245
246 memStats.stackInuse, err = producer.createInt64GaugeEntry("process/stack_inuse", "Bytes in stack spans", metricdata.UnitBytes)
247 if err != nil {
248 return nil, err
249 }
250
251 memStats.stackSys, err = producer.createInt64GaugeEntry("process/sys_stack", "The memory used by stack spans and OS thread stacks", metricdata.UnitBytes)
252 if err != nil {
253 return nil, err
254 }
255
256 memStats.stackMSpanInuse, err = producer.createInt64GaugeEntry("process/stack_mspan_inuse", "Bytes of allocated mspan structures", metricdata.UnitBytes)
257 if err != nil {
258 return nil, err
259 }
260
261 memStats.stackMSpanSys, err = producer.createInt64GaugeEntry("process/sys_stack_mspan", "Bytes of memory obtained from the OS for mspan structures", metricdata.UnitBytes)
262 if err != nil {
263 return nil, err
264 }
265
266 memStats.stackMCacheInuse, err = producer.createInt64GaugeEntry("process/stack_mcache_inuse", "Bytes of allocated mcache structures", metricdata.UnitBytes)
267 if err != nil {
268 return nil, err
269 }
270
271 memStats.stackMCacheSys, err = producer.createInt64GaugeEntry("process/sys_stack_mcache", "Bytes of memory obtained from the OS for mcache structures", metricdata.UnitBytes)
272 if err != nil {
273 return nil, err
274 }
275
276
277 memStats.gcSys, err = producer.createInt64GaugeEntry("process/gc_sys", "Bytes of memory in garbage collection metadatas", metricdata.UnitBytes)
278 if err != nil {
279 return nil, err
280 }
281
282 memStats.otherSys, err = producer.createInt64GaugeEntry("process/other_sys", "Bytes of memory in miscellaneous off-heap runtime allocations", metricdata.UnitBytes)
283 if err != nil {
284 return nil, err
285 }
286
287 memStats.numGC, err = producer.createInt64GaugeEntry("process/num_gc", "Cumulative count of completed GC cycles", metricdata.UnitDimensionless)
288 if err != nil {
289 return nil, err
290 }
291
292 memStats.numForcedGC, err = producer.createInt64GaugeEntry("process/num_forced_gc", "Cumulative count of GC cycles forced by the application", metricdata.UnitDimensionless)
293 if err != nil {
294 return nil, err
295 }
296
297 memStats.nextGC, err = producer.createInt64GaugeEntry("process/next_gc_heap_size", "Target heap size of the next GC cycle in bytes", metricdata.UnitBytes)
298 if err != nil {
299 return nil, err
300 }
301
302 memStats.lastGC, err = producer.createInt64GaugeEntry("process/last_gc_finished_timestamp", "Time the last garbage collection finished, as milliseconds since 1970 (the UNIX epoch)", metricdata.UnitMilliseconds)
303 if err != nil {
304 return nil, err
305 }
306
307 memStats.pauseTotalNs, err = producer.createInt64GaugeEntry("process/pause_total", "Cumulative milliseconds spent in GC stop-the-world pauses", metricdata.UnitMilliseconds)
308 if err != nil {
309 return nil, err
310 }
311
312 memStats.gcCPUFraction, err = producer.createFloat64GaugeEntry("process/gc_cpu_fraction", "Fraction of this program's available CPU time used by the GC since the program started", metricdata.UnitDimensionless)
313 if err != nil {
314 return nil, err
315 }
316
317 return memStats, nil
318 }
319
320 func (m *deprecatedMemStats) read() {
321 runtime.ReadMemStats(&m.memStats)
322
323 m.memAlloc.Set(int64(m.memStats.Alloc))
324 m.memTotal.Set(int64(m.memStats.TotalAlloc))
325 m.memSys.Set(int64(m.memStats.Sys))
326 m.memLookups.Set(int64(m.memStats.Lookups))
327 m.memMalloc.Set(int64(m.memStats.Mallocs))
328 m.memFrees.Set(int64(m.memStats.Frees))
329
330 m.heapAlloc.Set(int64(m.memStats.HeapAlloc))
331 m.heapSys.Set(int64(m.memStats.HeapSys))
332 m.heapIdle.Set(int64(m.memStats.HeapIdle))
333 m.heapInuse.Set(int64(m.memStats.HeapInuse))
334 m.heapReleased.Set(int64(m.memStats.HeapReleased))
335 m.heapObjects.Set(int64(m.memStats.HeapObjects))
336
337 m.stackInuse.Set(int64(m.memStats.StackInuse))
338 m.stackSys.Set(int64(m.memStats.StackSys))
339 m.stackMSpanInuse.Set(int64(m.memStats.MSpanInuse))
340 m.stackMSpanSys.Set(int64(m.memStats.MSpanSys))
341 m.stackMCacheInuse.Set(int64(m.memStats.MCacheInuse))
342 m.stackMCacheSys.Set(int64(m.memStats.MCacheSys))
343
344 m.gcSys.Set(int64(m.memStats.GCSys))
345 m.otherSys.Set(int64(m.memStats.OtherSys))
346 m.numGC.Set(int64(m.memStats.NumGC))
347 m.numForcedGC.Set(int64(m.memStats.NumForcedGC))
348 m.nextGC.Set(int64(m.memStats.NextGC))
349 m.lastGC.Set(int64(m.memStats.LastGC) / int64(time.Millisecond))
350 m.pauseTotalNs.Set(int64(m.memStats.PauseTotalNs) / int64(time.Millisecond))
351 m.gcCPUFraction.Set(m.memStats.GCCPUFraction)
352 }
353
354 func newMemStats(producer *producer) (*memStats, error) {
355 var err error
356 memStats := &memStats{}
357
358
359 memStats.memAlloc, err = producer.createInt64GaugeEntry("process/memory_alloc", "Number of bytes currently allocated in use", metricdata.UnitBytes)
360 if err != nil {
361 return nil, err
362 }
363
364 memStats.memTotal, err = producer.createInt64DerivedCumulative("process/total_memory_alloc", "Number of allocations in total", metricdata.UnitBytes)
365 if err != nil {
366 return nil, err
367 }
368
369 memStats.memSys, err = producer.createInt64GaugeEntry("process/sys_memory_alloc", "Number of bytes given to the process to use in total", metricdata.UnitBytes)
370 if err != nil {
371 return nil, err
372 }
373
374 memStats.memLookups, err = producer.createInt64DerivedCumulative("process/memory_lookups", "Cumulative number of pointer lookups performed by the runtime", metricdata.UnitDimensionless)
375 if err != nil {
376 return nil, err
377 }
378
379 memStats.memMalloc, err = producer.createInt64DerivedCumulative("process/memory_malloc", "Cumulative count of heap objects allocated", metricdata.UnitDimensionless)
380 if err != nil {
381 return nil, err
382 }
383
384 memStats.memFrees, err = producer.createInt64DerivedCumulative("process/memory_frees", "Cumulative count of heap objects freed", metricdata.UnitDimensionless)
385 if err != nil {
386 return nil, err
387 }
388
389
390 memStats.heapAlloc, err = producer.createInt64GaugeEntry("process/heap_alloc", "Process heap allocation", metricdata.UnitBytes)
391 if err != nil {
392 return nil, err
393 }
394
395 memStats.heapSys, err = producer.createInt64GaugeEntry("process/sys_heap", "Bytes of heap memory obtained from the OS", metricdata.UnitBytes)
396 if err != nil {
397 return nil, err
398 }
399
400 memStats.heapIdle, err = producer.createInt64GaugeEntry("process/heap_idle", "Bytes in idle (unused) spans", metricdata.UnitBytes)
401 if err != nil {
402 return nil, err
403 }
404
405 memStats.heapInuse, err = producer.createInt64GaugeEntry("process/heap_inuse", "Bytes in in-use spans", metricdata.UnitBytes)
406 if err != nil {
407 return nil, err
408 }
409
410 memStats.heapObjects, err = producer.createInt64GaugeEntry("process/heap_objects", "The number of objects allocated on the heap", metricdata.UnitDimensionless)
411 if err != nil {
412 return nil, err
413 }
414
415 memStats.heapReleased, err = producer.createInt64DerivedCumulative("process/heap_release", "The cumulative number of objects released from the heap", metricdata.UnitBytes)
416 if err != nil {
417 return nil, err
418 }
419
420
421 memStats.stackInuse, err = producer.createInt64GaugeEntry("process/stack_inuse", "Bytes in stack spans", metricdata.UnitBytes)
422 if err != nil {
423 return nil, err
424 }
425
426 memStats.stackSys, err = producer.createInt64GaugeEntry("process/sys_stack", "The memory used by stack spans and OS thread stacks", metricdata.UnitBytes)
427 if err != nil {
428 return nil, err
429 }
430
431 memStats.stackMSpanInuse, err = producer.createInt64GaugeEntry("process/stack_mspan_inuse", "Bytes of allocated mspan structures", metricdata.UnitBytes)
432 if err != nil {
433 return nil, err
434 }
435
436 memStats.stackMSpanSys, err = producer.createInt64GaugeEntry("process/sys_stack_mspan", "Bytes of memory obtained from the OS for mspan structures", metricdata.UnitBytes)
437 if err != nil {
438 return nil, err
439 }
440
441 memStats.stackMCacheInuse, err = producer.createInt64GaugeEntry("process/stack_mcache_inuse", "Bytes of allocated mcache structures", metricdata.UnitBytes)
442 if err != nil {
443 return nil, err
444 }
445
446 memStats.stackMCacheSys, err = producer.createInt64GaugeEntry("process/sys_stack_mcache", "Bytes of memory obtained from the OS for mcache structures", metricdata.UnitBytes)
447 if err != nil {
448 return nil, err
449 }
450
451
452 memStats.gcSys, err = producer.createInt64GaugeEntry("process/gc_sys", "Bytes of memory in garbage collection metadatas", metricdata.UnitBytes)
453 if err != nil {
454 return nil, err
455 }
456
457 memStats.otherSys, err = producer.createInt64GaugeEntry("process/other_sys", "Bytes of memory in miscellaneous off-heap runtime allocations", metricdata.UnitBytes)
458 if err != nil {
459 return nil, err
460 }
461
462 memStats.numGC, err = producer.createInt64DerivedCumulative("process/num_gc", "Cumulative count of completed GC cycles", metricdata.UnitDimensionless)
463 if err != nil {
464 return nil, err
465 }
466
467 memStats.numForcedGC, err = producer.createInt64DerivedCumulative("process/num_forced_gc", "Cumulative count of GC cycles forced by the application", metricdata.UnitDimensionless)
468 if err != nil {
469 return nil, err
470 }
471
472 memStats.nextGC, err = producer.createInt64GaugeEntry("process/next_gc_heap_size", "Target heap size of the next GC cycle in bytes", metricdata.UnitBytes)
473 if err != nil {
474 return nil, err
475 }
476
477 memStats.lastGC, err = producer.createInt64GaugeEntry("process/last_gc_finished_timestamp", "Time the last garbage collection finished, as milliseconds since 1970 (the UNIX epoch)", metricdata.UnitMilliseconds)
478 if err != nil {
479 return nil, err
480 }
481
482 memStats.pauseTotalNs, err = producer.createInt64DerivedCumulative("process/pause_total", "Cumulative milliseconds spent in GC stop-the-world pauses", metricdata.UnitMilliseconds)
483 if err != nil {
484 return nil, err
485 }
486
487 memStats.gcCPUFraction, err = producer.createFloat64GaugeEntry("process/gc_cpu_fraction", "Fraction of this program's available CPU time used by the GC since the program started", metricdata.UnitDimensionless)
488 if err != nil {
489 return nil, err
490 }
491
492 return memStats, nil
493 }
494
495 func (m *memStats) read() {
496 runtime.ReadMemStats(&m.memStats)
497
498 m.memAlloc.Set(int64(m.memStats.Alloc))
499
500 _ = m.memTotal.UpsertEntry(func() int64 {
501 return int64(m.memStats.TotalAlloc)
502 })
503
504 m.memSys.Set(int64(m.memStats.Sys))
505
506 _ = m.memLookups.UpsertEntry(func() int64 {
507 return int64(m.memStats.Lookups)
508 })
509
510 _ = m.memMalloc.UpsertEntry(func() int64 {
511 return int64(m.memStats.Mallocs)
512 })
513
514 _ = m.memFrees.UpsertEntry(func() int64 {
515 return int64(m.memStats.Frees)
516 })
517
518 m.heapAlloc.Set(int64(m.memStats.HeapAlloc))
519 m.heapSys.Set(int64(m.memStats.HeapSys))
520 m.heapIdle.Set(int64(m.memStats.HeapIdle))
521 m.heapInuse.Set(int64(m.memStats.HeapInuse))
522
523 _ = m.heapReleased.UpsertEntry(func() int64 {
524 return int64(m.memStats.HeapReleased)
525 })
526
527 m.heapObjects.Set(int64(m.memStats.HeapObjects))
528
529 m.stackInuse.Set(int64(m.memStats.StackInuse))
530 m.stackSys.Set(int64(m.memStats.StackSys))
531 m.stackMSpanInuse.Set(int64(m.memStats.MSpanInuse))
532 m.stackMSpanSys.Set(int64(m.memStats.MSpanSys))
533 m.stackMCacheInuse.Set(int64(m.memStats.MCacheInuse))
534 m.stackMCacheSys.Set(int64(m.memStats.MCacheSys))
535
536 m.gcSys.Set(int64(m.memStats.GCSys))
537 m.otherSys.Set(int64(m.memStats.OtherSys))
538
539 _ = m.numGC.UpsertEntry(func() int64 {
540 return int64(m.memStats.NumGC)
541 })
542
543 _ = m.numForcedGC.UpsertEntry(func() int64 {
544 return int64(m.memStats.NumForcedGC)
545 })
546
547 m.nextGC.Set(int64(m.memStats.NextGC))
548 m.lastGC.Set(int64(m.memStats.LastGC) / int64(time.Millisecond))
549
550 _ = m.pauseTotalNs.UpsertEntry(func() int64 {
551 return int64(m.memStats.PauseTotalNs) / int64(time.Millisecond)
552 })
553
554 m.gcCPUFraction.Set(m.memStats.GCCPUFraction)
555 }
556
557 func newCPUStats(producer *producer) (*cpuStats, error) {
558 cpuStats := &cpuStats{}
559 var err error
560
561 cpuStats.numGoroutines, err = producer.createInt64GaugeEntry("process/cpu_goroutines", "Number of goroutines that currently exist", metricdata.UnitDimensionless)
562 if err != nil {
563 return nil, err
564 }
565
566 cpuStats.numCgoCalls, err = producer.createInt64GaugeEntry("process/cpu_cgo_calls", "Number of cgo calls made by the current process", metricdata.UnitDimensionless)
567 if err != nil {
568 return nil, err
569 }
570
571 return cpuStats, nil
572 }
573
574 func (c *cpuStats) read() {
575 c.numGoroutines.Set(int64(runtime.NumGoroutine()))
576 c.numCgoCalls.Set(runtime.NumCgoCall())
577 }
578
579 func (p *producer) createFloat64GaugeEntry(name string, description string, unit metricdata.Unit) (*metric.Float64Entry, error) {
580 if len(p.options.Prefix) > 0 {
581 name = p.options.Prefix + name
582 }
583
584 gauge, err := p.reg.AddFloat64Gauge(
585 name,
586 metric.WithDescription(description),
587 metric.WithUnit(unit))
588 if err != nil {
589 return nil, errors.New("error creating gauge for " + name + ": " + err.Error())
590 }
591
592 entry, err := gauge.GetEntry()
593 if err != nil {
594 return nil, errors.New("error getting gauge entry for " + name + ": " + err.Error())
595 }
596
597 return entry, nil
598 }
599
600 func (p *producer) createInt64GaugeEntry(name string, description string, unit metricdata.Unit) (*metric.Int64GaugeEntry, error) {
601 if len(p.options.Prefix) > 0 {
602 name = p.options.Prefix + name
603 }
604
605 gauge, err := p.reg.AddInt64Gauge(
606 name,
607 metric.WithDescription(description),
608 metric.WithUnit(unit))
609 if err != nil {
610 return nil, errors.New("error creating gauge for " + name + ": " + err.Error())
611 }
612
613 entry, err := gauge.GetEntry()
614 if err != nil {
615 return nil, errors.New("error getting gauge entry for " + name + ": " + err.Error())
616 }
617
618 return entry, nil
619 }
620
621 func (p *producer) createInt64DerivedCumulative(name string, description string, unit metricdata.Unit) (*metric.Int64DerivedCumulative, error) {
622 if len(p.options.Prefix) > 0 {
623 name = p.options.Prefix + name
624 }
625
626 cumulative, err := p.reg.AddInt64DerivedCumulative(
627 name,
628 metric.WithDescription(description),
629 metric.WithUnit(unit))
630 if err != nil {
631 return nil, errors.New("error creating gauge for " + name + ": " + err.Error())
632 }
633
634 return cumulative, nil
635 }
636
View as plain text