forked from tinode/chat
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathstats.go
167 lines (145 loc) · 3.85 KB
/
stats.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
// Logic related to expvar handling: reporting live stats such as
// session and topic counts, memory usage etc.
// The stats updates happen in a separate go routine to avoid
// locking on main logic routines.
package main
import (
"encoding/json"
"expvar"
"net/http"
"runtime"
"sort"
"time"
"github.com/tinode/chat/server/logs"
"github.com/tinode/chat/server/store"
)
// A simple implementation of histogram expvar.Var.
// `Bounds` specifies the histogram buckets as follows (length = len(bounds)):
//
// (-inf, Bounds[i]) for i = 0
// [Bounds[i-1], Bounds[i]) for 0 < i < length
// [Bounds[i-1], +inf) for i = length
type histogram struct {
Count int64 `json:"count"`
Sum float64 `json:"sum"`
CountPerBucket []int64 `json:"count_per_bucket"`
Bounds []float64 `json:"bounds"`
}
func (h *histogram) addSample(v float64) {
h.Count++
h.Sum += v
idx := sort.SearchFloat64s(h.Bounds, v)
h.CountPerBucket[idx]++
}
func (h *histogram) String() string {
if r, err := json.Marshal(h); err == nil {
return string(r)
}
return ""
}
type varUpdate struct {
// Name of the variable to update
varname string
// Value to publish (int, float, etc.)
value interface{}
// Treat the count as an increment as opposite to the final value.
inc bool
}
// Initialize stats reporting through expvar.
func statsInit(mux *http.ServeMux, path string) {
if path == "" || path == "-" {
return
}
mux.Handle(path, expvar.Handler())
globals.statsUpdate = make(chan *varUpdate, 1024)
start := time.Now()
expvar.Publish("Uptime", expvar.Func(func() interface{} {
return time.Since(start).Seconds()
}))
expvar.Publish("NumGoroutines", expvar.Func(func() interface{} {
return runtime.NumGoroutine()
}))
go statsUpdater()
logs.Info.Printf("stats: variables exposed at '%s'", path)
}
func statsRegisterDbStats() {
if f := store.Store.DbStats(); f != nil {
expvar.Publish("DbStats", expvar.Func(f))
}
}
// Register integer variable. Don't check for initialization.
func statsRegisterInt(name string) {
expvar.Publish(name, new(expvar.Int))
}
// Register histogram variable. `bounds` specifies histogram buckets/bins
// (see comment next to the `histogram` struct definition).
func statsRegisterHistogram(name string, bounds []float64) {
numBuckets := len(bounds) + 1
expvar.Publish(name, &histogram{
CountPerBucket: make([]int64, numBuckets),
Bounds: bounds,
})
}
// Async publish int variable.
func statsSet(name string, val int64) {
if globals.statsUpdate != nil {
select {
case globals.statsUpdate <- &varUpdate{name, val, false}:
default:
}
}
}
// Async publish an increment (decrement) to int variable.
func statsInc(name string, val int) {
if globals.statsUpdate != nil {
select {
case globals.statsUpdate <- &varUpdate{name, int64(val), true}:
default:
}
}
}
// Async publish a value (add a sample) to a histogram variable.
func statsAddHistSample(name string, val float64) {
if globals.statsUpdate != nil {
select {
case globals.statsUpdate <- &varUpdate{varname: name, value: val}:
default:
}
}
}
// Stop publishing stats.
func statsShutdown() {
if globals.statsUpdate != nil {
globals.statsUpdate <- nil
}
}
// The go routine which actually publishes stats updates.
func statsUpdater() {
for upd := range globals.statsUpdate {
if upd == nil {
globals.statsUpdate = nil
// Dont' care to close the channel.
break
}
// Handle var update
if ev := expvar.Get(upd.varname); ev != nil {
switch v := ev.(type) {
case *expvar.Int:
count := upd.value.(int64)
if upd.inc {
v.Add(count)
} else {
v.Set(count)
}
case *histogram:
val := upd.value.(float64)
v.addSample(val)
default:
logs.Err.Panicf("stats: unsupported expvar type %T", ev)
}
} else {
panic("stats: update to unknown variable " + upd.varname)
}
}
logs.Info.Println("stats: shutdown")
}