-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathexpvar.go
231 lines (209 loc) · 8.51 KB
/
expvar.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
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
package expvar
import (
"context"
"runtime"
"sync"
"time"
stat "github.com/asecurityteam/component-stat"
"github.com/asecurityteam/settings/v2"
)
const (
statMemstatsAlloc = "go_expvar.memstats.alloc"
statMemstatsFrees = "go_expvar.memstats.frees"
statMemstatsHeapAlloc = "go_expvar.memstats.heap_alloc"
statMemstatsHeapIdle = "go_expvar.memstats.heap_idle"
statMemstatsHeapInuse = "go_expvar.memstats.heap_inuse"
statMemstatsHeapObjects = "go_expvar.memstats.heap_objects"
statMemstatsHeapReleased = "go_expvar.memstats.heap_released"
statMemstatsHeapSys = "go_expvar.memstats.heap_sys"
statMemstatsLookups = "go_expvar.memstats.lookups"
statMemstatsMallocs = "go_expvar.memstats.mallocs"
statMemstatsNumGC = "go_expvar.memstats.num_gc"
statMemstatsPauseNS = "go_expvar.memstats.pause_ns"
statMemstatsPauseTotalNS = "go_expvar.memstats.pause_total_ns"
statMemstatsTotalAlloc = "go_expvar.memstats.total_alloc"
statGoroutinesExists = "go_expvar.goroutines.exists"
expvarInterval = 5 * time.Second
)
// Expvar tracks the memory usage of the Go runtime and collects metrics instrumented from Go’s expvar package
type Expvar struct {
Stat stat.Stat
MemstatsAllocName string
MemstatsFreesName string
MemstatsHeapAllocName string
MemstatsHeapIdleName string
MemstatsHeapInuseName string
MemstatsHeapObjectsName string
MemstatsHeapReleasedName string
MemstatsHeapSysName string
MemstatsLookupsName string
MemstatsMallocsName string
MemstatsNumGCName string
MemstatsPauseNSName string
MemstatsPauseTotalNSName string
MemstatsTotalAllocName string
GoroutinesExistsName string
Interval time.Duration
reportMut *sync.Mutex
stop chan interface{}
lastNumGC uint32
readMemStats func(*runtime.MemStats)
numGoroutine func() int
}
// Report loops on a time interval and pushes a set of gauge metrics.
func (e *Expvar) Report() {
ticker := time.NewTicker(e.Interval)
defer ticker.Stop()
for {
select {
case <-ticker.C:
e.report()
case <-e.stop:
return
}
}
}
// Close the reporting loop.
func (e *Expvar) Close() error {
close(e.stop)
return nil
}
func (e *Expvar) report() {
memstats := new(runtime.MemStats)
e.readMemStats(memstats)
numGoroutines := e.numGoroutine()
e.reportMut.Lock()
defer e.reportMut.Unlock()
e.Stat.Gauge(e.MemstatsAllocName, float64(memstats.Alloc))
e.Stat.Gauge(e.MemstatsFreesName, float64(memstats.Frees))
e.Stat.Gauge(e.MemstatsHeapAllocName, float64(memstats.HeapAlloc))
e.Stat.Gauge(e.MemstatsHeapIdleName, float64(memstats.HeapIdle))
e.Stat.Gauge(e.MemstatsHeapInuseName, float64(memstats.HeapInuse))
e.Stat.Gauge(e.MemstatsHeapObjectsName, float64(memstats.HeapObjects))
e.Stat.Gauge(e.MemstatsHeapReleasedName, float64(memstats.HeapReleased))
e.Stat.Gauge(e.MemstatsHeapSysName, float64(memstats.HeapSys))
e.Stat.Gauge(e.MemstatsLookupsName, float64(memstats.Lookups))
e.Stat.Gauge(e.MemstatsMallocsName, float64(memstats.Mallocs))
e.Stat.Gauge(e.MemstatsNumGCName, float64(memstats.NumGC))
e.Stat.Gauge(e.MemstatsPauseTotalNSName, float64(memstats.PauseTotalNs))
e.Stat.Gauge(e.MemstatsTotalAllocName, float64(memstats.TotalAlloc))
e.Stat.Gauge(e.GoroutinesExistsName, float64(numGoroutines))
// PauseNs is a circular buffer of recent GC stop-the-world
// pause times in nanoseconds.
//
// The most recent pause is at PauseNs[(NumGC+255)%256]. In
// general, PauseNs[N%256] records the time paused in the most
// recent N%256th GC cycle. There may be multiple pauses per
// GC cycle; this is the sum of all pauses during a cycle.
pauseNS := memstats.PauseNs
if e.lastNumGC == memstats.NumGC { // no GC cycles have run
return
}
start := e.lastNumGC % 256
end := (memstats.NumGC+255)%256 + 1
var values []uint64
if start < end {
values = pauseNS[start:end]
} else {
values = append(pauseNS[start:], pauseNS[:end]...)
}
e.lastNumGC = memstats.NumGC
for _, val := range values {
e.Stat.Histogram(e.MemstatsPauseNSName, float64(val))
}
}
// Config is a container for internal expvar metrics settings.
type Config struct {
Alloc string `description:"Name of the metric tracking allocated bytes"`
Frees string `description:"Name of the metric tracking number of frees"`
HeapAlloc string `description:"Name of the metric tracking allocated bytes"`
HeapIdle string `description:"Name of the metric tracking bytes in unused spans"`
HeapInuse string `description:"Name of the metric tracking bytes in in-use spans"`
HeapObjects string `description:"Name of the metric tracking total number of object allocated"`
HeapReleased string `description:"Name of the metric tracking bytes realeased to the OS"`
HeapSys string `description:"Name of the metric tracking bytes obtained from the system"`
Lookups string `description:"Name of the metric tracking number of pointer lookups"`
Mallocs string `description:"Name of the metric tracking number of mallocs"`
NumGC string `description:"Name of the metric tracking number of garbage collections"`
PauseNS string `description:"Name of the metric tracking duration of GC pauses"`
PauseTotalNS string `description:"Name of the metric tracking total GC pause duration over lifetime process"`
TotalAlloc string `description:"Name of the metric tracking allocated bytes (even if freed)"`
GoroutinesExists string `description:"Name of the metric tracking number of active go routines"`
ReportInterval time.Duration `description:"Interval on which metrics are reported."`
}
// Name of the configuration root.
func (*Config) Name() string {
return "expvar"
}
// Description returns the help information for the configuration root.
func (*Config) Description() string {
return "Expvar metric names"
}
// Component implements the settings.Component interface for expvar metrics.
type Component struct {
Stat stat.Stat
}
// NewComponent generates a blank component instance.
func NewComponent() *Component {
return &Component{}
}
// WithStat returns a copy of the component bound to the given Stat instance.
func (c *Component) WithStat(s stat.Stat) *Component {
return &Component{Stat: s}
}
// Settings returns a configuration with all defaults set.
func (*Component) Settings() *Config {
return &Config{
Alloc: statMemstatsAlloc,
Frees: statMemstatsFrees,
HeapAlloc: statMemstatsHeapAlloc,
HeapIdle: statMemstatsHeapIdle,
HeapInuse: statMemstatsHeapInuse,
HeapObjects: statMemstatsHeapObjects,
HeapReleased: statMemstatsHeapReleased,
HeapSys: statMemstatsHeapSys,
Lookups: statMemstatsLookups,
Mallocs: statMemstatsMallocs,
NumGC: statMemstatsNumGC,
PauseNS: statMemstatsPauseNS,
PauseTotalNS: statMemstatsPauseTotalNS,
TotalAlloc: statMemstatsTotalAlloc,
GoroutinesExists: statGoroutinesExists,
ReportInterval: expvarInterval,
}
}
// New produces a ServerFn bound to the given configuration.
func (c *Component) New(_ context.Context, conf *Config) (*Expvar, error) {
return &Expvar{
Stat: c.Stat,
MemstatsAllocName: conf.Alloc,
MemstatsFreesName: conf.Frees,
MemstatsHeapAllocName: conf.HeapAlloc,
MemstatsHeapIdleName: conf.HeapIdle,
MemstatsHeapInuseName: conf.HeapInuse,
MemstatsHeapObjectsName: conf.HeapObjects,
MemstatsHeapReleasedName: conf.HeapReleased,
MemstatsHeapSysName: conf.HeapSys,
MemstatsLookupsName: conf.Lookups,
MemstatsMallocsName: conf.Mallocs,
MemstatsNumGCName: conf.NumGC,
MemstatsPauseNSName: conf.PauseNS,
MemstatsPauseTotalNSName: conf.PauseTotalNS,
MemstatsTotalAllocName: conf.TotalAlloc,
GoroutinesExistsName: conf.GoroutinesExists,
Interval: conf.ReportInterval,
reportMut: &sync.Mutex{},
stop: make(chan interface{}),
readMemStats: runtime.ReadMemStats,
numGoroutine: runtime.NumGoroutine,
}, nil
}
// Load is a convenience method for binding the source to the component.
func Load(ctx context.Context, source settings.Source, c *Component) (*Expvar, error) {
dst := new(Expvar)
err := settings.NewComponent(ctx, source, c, dst)
if err != nil {
return nil, err
}
return dst, nil
}