Source file
src/expvar/expvar.go
Documentation: expvar
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 package expvar
24
25 import (
26 "encoding/json"
27 "internal/godebug"
28 "log"
29 "math"
30 "net/http"
31 "os"
32 "runtime"
33 "slices"
34 "strconv"
35 "sync"
36 "sync/atomic"
37 "unicode/utf8"
38 )
39
40
41 type Var interface {
42
43
44
45 String() string
46 }
47
48 type jsonVar interface {
49
50 appendJSON(b []byte) []byte
51 }
52
53
54 type Int struct {
55 i atomic.Int64
56 }
57
58 func (v *Int) Value() int64 {
59 return v.i.Load()
60 }
61
62 func (v *Int) String() string {
63 return string(v.appendJSON(nil))
64 }
65
66 func (v *Int) appendJSON(b []byte) []byte {
67 return strconv.AppendInt(b, v.i.Load(), 10)
68 }
69
70 func (v *Int) Add(delta int64) {
71 v.i.Add(delta)
72 }
73
74 func (v *Int) Set(value int64) {
75 v.i.Store(value)
76 }
77
78
79 type Float struct {
80 f atomic.Uint64
81 }
82
83 func (v *Float) Value() float64 {
84 return math.Float64frombits(v.f.Load())
85 }
86
87 func (v *Float) String() string {
88 return string(v.appendJSON(nil))
89 }
90
91 func (v *Float) appendJSON(b []byte) []byte {
92 return strconv.AppendFloat(b, math.Float64frombits(v.f.Load()), 'g', -1, 64)
93 }
94
95
96 func (v *Float) Add(delta float64) {
97 for {
98 cur := v.f.Load()
99 curVal := math.Float64frombits(cur)
100 nxtVal := curVal + delta
101 nxt := math.Float64bits(nxtVal)
102 if v.f.CompareAndSwap(cur, nxt) {
103 return
104 }
105 }
106 }
107
108
109 func (v *Float) Set(value float64) {
110 v.f.Store(math.Float64bits(value))
111 }
112
113
114 type Map struct {
115 m sync.Map
116 keysMu sync.RWMutex
117 keys []string
118 }
119
120
121 type KeyValue struct {
122 Key string
123 Value Var
124 }
125
126 func (v *Map) String() string {
127 return string(v.appendJSON(nil))
128 }
129
130 func (v *Map) appendJSON(b []byte) []byte {
131 return v.appendJSONMayExpand(b, false)
132 }
133
134 func (v *Map) appendJSONMayExpand(b []byte, expand bool) []byte {
135 afterCommaDelim := byte(' ')
136 mayAppendNewline := func(b []byte) []byte { return b }
137 if expand {
138 afterCommaDelim = '\n'
139 mayAppendNewline = func(b []byte) []byte { return append(b, '\n') }
140 }
141
142 b = append(b, '{')
143 b = mayAppendNewline(b)
144 first := true
145 v.Do(func(kv KeyValue) {
146 if !first {
147 b = append(b, ',', afterCommaDelim)
148 }
149 first = false
150 b = appendJSONQuote(b, kv.Key)
151 b = append(b, ':', ' ')
152 switch v := kv.Value.(type) {
153 case nil:
154 b = append(b, "null"...)
155 case jsonVar:
156 b = v.appendJSON(b)
157 default:
158 b = append(b, v.String()...)
159 }
160 })
161 b = mayAppendNewline(b)
162 b = append(b, '}')
163 b = mayAppendNewline(b)
164 return b
165 }
166
167
168 func (v *Map) Init() *Map {
169 v.keysMu.Lock()
170 defer v.keysMu.Unlock()
171 v.keys = v.keys[:0]
172 v.m.Clear()
173 return v
174 }
175
176
177 func (v *Map) addKey(key string) {
178 v.keysMu.Lock()
179 defer v.keysMu.Unlock()
180
181 i, found := slices.BinarySearch(v.keys, key)
182 if found {
183 return
184 }
185 v.keys = slices.Insert(v.keys, i, key)
186 }
187
188 func (v *Map) Get(key string) Var {
189 i, _ := v.m.Load(key)
190 av, _ := i.(Var)
191 return av
192 }
193
194 func (v *Map) Set(key string, av Var) {
195
196
197
198 if _, ok := v.m.Load(key); !ok {
199 if _, dup := v.m.LoadOrStore(key, av); !dup {
200 v.addKey(key)
201 return
202 }
203 }
204
205 v.m.Store(key, av)
206 }
207
208
209 func (v *Map) Add(key string, delta int64) {
210 i, ok := v.m.Load(key)
211 if !ok {
212 var dup bool
213 i, dup = v.m.LoadOrStore(key, new(Int))
214 if !dup {
215 v.addKey(key)
216 }
217 }
218
219
220 if iv, ok := i.(*Int); ok {
221 iv.Add(delta)
222 }
223 }
224
225
226 func (v *Map) AddFloat(key string, delta float64) {
227 i, ok := v.m.Load(key)
228 if !ok {
229 var dup bool
230 i, dup = v.m.LoadOrStore(key, new(Float))
231 if !dup {
232 v.addKey(key)
233 }
234 }
235
236
237 if iv, ok := i.(*Float); ok {
238 iv.Add(delta)
239 }
240 }
241
242
243 func (v *Map) Delete(key string) {
244 v.keysMu.Lock()
245 defer v.keysMu.Unlock()
246 i, found := slices.BinarySearch(v.keys, key)
247 if found {
248 v.keys = slices.Delete(v.keys, i, i+1)
249 v.m.Delete(key)
250 }
251 }
252
253
254
255
256 func (v *Map) Do(f func(KeyValue)) {
257 v.keysMu.RLock()
258 defer v.keysMu.RUnlock()
259 for _, k := range v.keys {
260 i, _ := v.m.Load(k)
261 val, _ := i.(Var)
262 f(KeyValue{k, val})
263 }
264 }
265
266
267 type String struct {
268 s atomic.Value
269 }
270
271 func (v *String) Value() string {
272 p, _ := v.s.Load().(string)
273 return p
274 }
275
276
277
278 func (v *String) String() string {
279 return string(v.appendJSON(nil))
280 }
281
282 func (v *String) appendJSON(b []byte) []byte {
283 return appendJSONQuote(b, v.Value())
284 }
285
286 func (v *String) Set(value string) {
287 v.s.Store(value)
288 }
289
290
291
292 type Func func() any
293
294 func (f Func) Value() any {
295 return f()
296 }
297
298 func (f Func) String() string {
299 v, _ := json.Marshal(f())
300 return string(v)
301 }
302
303
304 var vars Map
305
306
307
308
309 func Publish(name string, v Var) {
310 if _, dup := vars.m.LoadOrStore(name, v); dup {
311 log.Panicln("Reuse of exported var name:", name)
312 }
313 vars.keysMu.Lock()
314 defer vars.keysMu.Unlock()
315 vars.keys = append(vars.keys, name)
316 slices.Sort(vars.keys)
317 }
318
319
320
321 func Get(name string) Var {
322 return vars.Get(name)
323 }
324
325
326
327 func NewInt(name string) *Int {
328 v := new(Int)
329 Publish(name, v)
330 return v
331 }
332
333 func NewFloat(name string) *Float {
334 v := new(Float)
335 Publish(name, v)
336 return v
337 }
338
339 func NewMap(name string) *Map {
340 v := new(Map).Init()
341 Publish(name, v)
342 return v
343 }
344
345 func NewString(name string) *String {
346 v := new(String)
347 Publish(name, v)
348 return v
349 }
350
351
352
353
354 func Do(f func(KeyValue)) {
355 vars.Do(f)
356 }
357
358 func expvarHandler(w http.ResponseWriter, r *http.Request) {
359 w.Header().Set("Content-Type", "application/json; charset=utf-8")
360 w.Write(vars.appendJSONMayExpand(nil, true))
361 }
362
363
364
365
366 func Handler() http.Handler {
367 return http.HandlerFunc(expvarHandler)
368 }
369
370 func cmdline() any {
371 return os.Args
372 }
373
374 func memstats() any {
375 stats := new(runtime.MemStats)
376 runtime.ReadMemStats(stats)
377 return *stats
378 }
379
380 func init() {
381 if godebug.New("httpmuxgo121").Value() == "1" {
382 http.HandleFunc("/debug/vars", expvarHandler)
383 } else {
384 http.HandleFunc("GET /debug/vars", expvarHandler)
385 }
386 Publish("cmdline", Func(cmdline))
387 Publish("memstats", Func(memstats))
388 }
389
390
391 func appendJSONQuote(b []byte, s string) []byte {
392 const hex = "0123456789abcdef"
393 b = append(b, '"')
394 for _, r := range s {
395 switch {
396 case r < ' ' || r == '\\' || r == '"' || r == '<' || r == '>' || r == '&' || r == '\u2028' || r == '\u2029':
397 switch r {
398 case '\\', '"':
399 b = append(b, '\\', byte(r))
400 case '\n':
401 b = append(b, '\\', 'n')
402 case '\r':
403 b = append(b, '\\', 'r')
404 case '\t':
405 b = append(b, '\\', 't')
406 default:
407 b = append(b, '\\', 'u', hex[(r>>12)&0xf], hex[(r>>8)&0xf], hex[(r>>4)&0xf], hex[(r>>0)&0xf])
408 }
409 case r < utf8.RuneSelf:
410 b = append(b, byte(r))
411 default:
412 b = utf8.AppendRune(b, r)
413 }
414 }
415 b = append(b, '"')
416 return b
417 }
418
View as plain text