-
-
Notifications
You must be signed in to change notification settings - Fork 1
/
monitor.go
133 lines (110 loc) · 3.76 KB
/
monitor.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
// +build !js
// Copyright (c) 2009-2020 Misakai Ltd. and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for details.
package stats
import (
"runtime"
"sync"
"time"
"github.com/golang/snappy"
"github.com/kelindar/binary"
"github.com/kelindar/process"
)
// Monitor represents a monitoring registry
type Monitor struct {
registry sync.Map // The registry used for keeping various metrics.
created time.Time // The start time for uptime calculation.
}
// Assert contract compliance
var _ Measurer = New()
// New creates a new monitor.
func New() *Monitor {
return &Monitor{
created: time.Now(),
}
}
// Get retrieves a metric by its name. If the metric does not exist yet, it will
// create and register the metric.
func (m *Monitor) Get(name string) *Metric {
if v, ok := m.registry.Load(name); ok {
return v.(*Metric)
}
v, _ := m.registry.LoadOrStore(name, NewMetric(name))
return v.(*Metric)
}
// Measure retrieves the metric and updates it.
func (m *Monitor) Measure(name string, value int32) {
m.Get(name).Update(value)
}
// MeasureElapsed measures elapsed time since the start
func (m *Monitor) MeasureElapsed(name string, start time.Time) {
m.Measure(name, int32(time.Since(start)/time.Microsecond))
}
// Tag updates a tag of a particular metric.
func (m *Monitor) Tag(name, tag string) {
m.Get(name).UpdateTag(tag)
}
// Snapshot encodes the metrics into a binary representation
func (m *Monitor) Snapshot() (out []byte) {
var snapshots []Snapshot
m.Range(func(metric *Metric) bool {
snapshots = append(snapshots, *metric.Snapshot())
metric.Reset()
return true
})
// Marshal and compress with snappy
if enc, err := binary.Marshal(snapshots); err == nil {
out = snappy.Encode(out, enc)
}
return
}
// Range ranges over all the metrics in the monitor. The iteration stops if the function
// returns false, similar to sync.Map
func (m *Monitor) Range(f func(*Metric) bool) {
m.registry.Range(func(k, v interface{}) bool {
return f(v.(*Metric))
})
}
// MeasureRuntime captures the runtime metrics, this is a relatively slow process
// and code is largely inspired by go-metrics.
func (m *Monitor) MeasureRuntime() {
defer recover()
// Collect stats
var memory runtime.MemStats
var memoryPriv, memoryVirtual int64
var cpu float64
runtime.ReadMemStats(&memory)
process.ProcUsage(&cpu, &memoryPriv, &memoryVirtual)
// Measure process information
m.Measure("proc.cpu", int32(cpu*10000))
m.Measure("proc.priv", toKB(uint64(memoryPriv)))
m.Measure("proc.virt", toKB(uint64(memoryVirtual)))
m.Measure("proc.uptime", int32(time.Now().Sub(m.created).Seconds()))
// Measure heap information
m.Measure("heap.alloc", toKB(memory.HeapAlloc))
m.Measure("heap.idle", toKB(memory.HeapIdle))
m.Measure("heap.inuse", toKB(memory.HeapInuse))
m.Measure("heap.objects", int32(memory.HeapObjects))
m.Measure("heap.released", toKB(memory.HeapReleased))
m.Measure("heap.sys", toKB(memory.HeapSys))
// Measure off heap memory
m.Measure("mcache.inuse", toKB(memory.MCacheInuse))
m.Measure("mcache.sys", toKB(memory.MCacheSys))
m.Measure("mspan.inuse", toKB(memory.MSpanInuse))
m.Measure("mspan.sys", toKB(memory.MSpanSys))
// Measure GC
m.Measure("gc.cpu", int32(memory.GCCPUFraction*10000))
m.Measure("gc.sys", toKB(memory.GCSys))
// Measure memory
m.Measure("stack.inuse", toKB(memory.StackInuse))
m.Measure("stack.sys", toKB(memory.StackSys))
// Measure goroutines and threads and total memory
m.Measure("go.count", int32(runtime.NumGoroutine()))
m.Measure("go.procs", int32(runtime.NumCPU()))
m.Measure("go.sys", toKB(memory.Sys))
m.Measure("go.alloc", toKB(memory.TotalAlloc))
}
// Converts the memory in bytes to KBs, otherwise it would overflow our int32
func toKB(v uint64) int32 {
return int32(v / 1024)
}