Source file
src/expvar/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 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.Range(func(k, _ any) bool {
173 v.m.Delete(k)
174 return true
175 })
176 return v
177 }
178
179
180 func (v *Map) addKey(key string) {
181 v.keysMu.Lock()
182 defer v.keysMu.Unlock()
183
184 i, found := slices.BinarySearch(v.keys, key)
185 if found {
186 return
187 }
188 v.keys = slices.Insert(v.keys, i, key)
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, found := slices.BinarySearch(v.keys, key)
250 if found {
251 v.keys = slices.Delete(v.keys, i, 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 slices.Sort(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 if godebug.New("httpmuxgo121").Value() == "1" {
385 http.HandleFunc("/debug/vars", expvarHandler)
386 } else {
387 http.HandleFunc("GET /debug/vars", expvarHandler)
388 }
389 Publish("cmdline", Func(cmdline))
390 Publish("memstats", Func(memstats))
391 }
392
393
394 func appendJSONQuote(b []byte, s string) []byte {
395 const hex = "0123456789abcdef"
396 b = append(b, '"')
397 for _, r := range s {
398 switch {
399 case r < ' ' || r == '\\' || r == '"' || r == '<' || r == '>' || r == '&' || r == '\u2028' || r == '\u2029':
400 switch r {
401 case '\\', '"':
402 b = append(b, '\\', byte(r))
403 case '\n':
404 b = append(b, '\\', 'n')
405 case '\r':
406 b = append(b, '\\', 'r')
407 case '\t':
408 b = append(b, '\\', 't')
409 default:
410 b = append(b, '\\', 'u', hex[(r>>12)&0xf], hex[(r>>8)&0xf], hex[(r>>4)&0xf], hex[(r>>0)&0xf])
411 }
412 case r < utf8.RuneSelf:
413 b = append(b, byte(r))
414 default:
415 b = utf8.AppendRune(b, r)
416 }
417 }
418 b = append(b, '"')
419 return b
420 }
421
View as plain text