Skip to content

Commit 975a36c

Browse files
Add metrics package with expvar counters
Create a singleton metrics struct registered with expvar and context helpers: Set, AddRequests, AddGoroutines, AddErrors, AddPanics. Goroutine metric is refreshed every 100 requests.
1 parent 786ba35 commit 975a36c

1 file changed

Lines changed: 92 additions & 0 deletions

File tree

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
package metrics
2+
3+
import (
4+
"context"
5+
"expvar"
6+
"runtime"
7+
)
8+
9+
// This holds the single instance of the metrics value needed for
10+
// collecting metrics. The expvar package is already based on a singleton
11+
// for the different metrics that are registered with the package so there
12+
// isn't much choice here.
13+
var m *metrics
14+
15+
// =============================================================================
16+
17+
// metrics represents the set of metrics we gather. These fields are
18+
// safe to be accessed concurrently thanks to expvar. No extra abstraction is required.
19+
type metrics struct {
20+
goroutines *expvar.Int
21+
requests *expvar.Int
22+
errors *expvar.Int
23+
panics *expvar.Int
24+
}
25+
26+
// init constructs the metrics value that will be used to capture metrics.
27+
// The metrics value is stored in a package level variable since everything
28+
// inside of expvar is registered as a singleton. The use of once will make
29+
// sure this initialization only happens once.
30+
func init() {
31+
m = &metrics{
32+
goroutines: expvar.NewInt("goroutines"),
33+
requests: expvar.NewInt("requests"),
34+
errors: expvar.NewInt("errors"),
35+
panics: expvar.NewInt("panics"),
36+
}
37+
}
38+
39+
// =============================================================================
40+
41+
type ctxKey int
42+
43+
const key ctxKey = 1
44+
45+
// Set sets the metrics data into the context.
46+
func Set(ctx context.Context) context.Context {
47+
return context.WithValue(ctx, key, m)
48+
}
49+
50+
// AddGoroutines refreshes the goroutine metric every 100 requests.
51+
func AddGoroutines(ctx context.Context) int64 {
52+
if v, ok := ctx.Value(key).(*metrics); ok {
53+
if v.requests.Value()%100 == 0 {
54+
g := int64(runtime.NumGoroutine())
55+
v.goroutines.Set(g)
56+
return g
57+
}
58+
}
59+
60+
return 0
61+
}
62+
63+
// AddRequests increments the request metric by 1.
64+
func AddRequests(ctx context.Context) int64 {
65+
v, ok := ctx.Value(key).(*metrics)
66+
if ok {
67+
v.requests.Add(1)
68+
return v.goroutines.Value()
69+
}
70+
71+
return 0
72+
}
73+
74+
// AddErrors increments the errors metric by 1.
75+
func AddErrors(ctx context.Context) int64 {
76+
if v, ok := ctx.Value(key).(*metrics); ok {
77+
v.errors.Add(1)
78+
return v.errors.Value()
79+
}
80+
81+
return 0
82+
}
83+
84+
// AddPanics increments the panics metric by 1.
85+
func AddPanics(ctx context.Context) int64 {
86+
if v, ok := ctx.Value(key).(*metrics); ok {
87+
v.panics.Add(1)
88+
return v.panics.Value()
89+
}
90+
91+
return 0
92+
}

0 commit comments

Comments
 (0)