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