github.com/roboticscm/goman@v0.0.0-20210203095141-87c07b4a0a55/src/expvar/expvar.go (about) 1 // Copyright 2009 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 // Package expvar provides a standardized interface to public variables, such 6 // as operation counters in servers. It exposes these variables via HTTP at 7 // /debug/vars in JSON format. 8 // 9 // Operations to set or modify these public variables are atomic. 10 // 11 // In addition to adding the HTTP handler, this package registers the 12 // following variables: 13 // 14 // cmdline os.Args 15 // memstats runtime.Memstats 16 // 17 // The package is sometimes only imported for the side effect of 18 // registering its HTTP handler and the above variables. To use it 19 // this way, link this package into your program: 20 // import _ "expvar" 21 // 22 package expvar 23 24 import ( 25 "bytes" 26 "encoding/json" 27 "fmt" 28 "log" 29 "net/http" 30 "os" 31 "runtime" 32 "sort" 33 "strconv" 34 "sync" 35 ) 36 37 // Var is an abstract type for all exported variables. 38 type Var interface { 39 String() string 40 } 41 42 // Int is a 64-bit integer variable that satisfies the Var interface. 43 type Int struct { 44 mu sync.RWMutex 45 i int64 46 } 47 48 func (v *Int) String() string { 49 v.mu.RLock() 50 defer v.mu.RUnlock() 51 return strconv.FormatInt(v.i, 10) 52 } 53 54 func (v *Int) Add(delta int64) { 55 v.mu.Lock() 56 defer v.mu.Unlock() 57 v.i += delta 58 } 59 60 func (v *Int) Set(value int64) { 61 v.mu.Lock() 62 defer v.mu.Unlock() 63 v.i = value 64 } 65 66 // Float is a 64-bit float variable that satisfies the Var interface. 67 type Float struct { 68 mu sync.RWMutex 69 f float64 70 } 71 72 func (v *Float) String() string { 73 v.mu.RLock() 74 defer v.mu.RUnlock() 75 return strconv.FormatFloat(v.f, 'g', -1, 64) 76 } 77 78 // Add adds delta to v. 79 func (v *Float) Add(delta float64) { 80 v.mu.Lock() 81 defer v.mu.Unlock() 82 v.f += delta 83 } 84 85 // Set sets v to value. 86 func (v *Float) Set(value float64) { 87 v.mu.Lock() 88 defer v.mu.Unlock() 89 v.f = value 90 } 91 92 // Map is a string-to-Var map variable that satisfies the Var interface. 93 type Map struct { 94 mu sync.RWMutex 95 m map[string]Var 96 keys []string // sorted 97 } 98 99 // KeyValue represents a single entry in a Map. 100 type KeyValue struct { 101 Key string 102 Value Var 103 } 104 105 func (v *Map) String() string { 106 v.mu.RLock() 107 defer v.mu.RUnlock() 108 var b bytes.Buffer 109 fmt.Fprintf(&b, "{") 110 first := true 111 v.doLocked(func(kv KeyValue) { 112 if !first { 113 fmt.Fprintf(&b, ", ") 114 } 115 fmt.Fprintf(&b, "%q: %v", kv.Key, kv.Value) 116 first = false 117 }) 118 fmt.Fprintf(&b, "}") 119 return b.String() 120 } 121 122 func (v *Map) Init() *Map { 123 v.m = make(map[string]Var) 124 return v 125 } 126 127 // updateKeys updates the sorted list of keys in v.keys. 128 // must be called with v.mu held. 129 func (v *Map) updateKeys() { 130 if len(v.m) == len(v.keys) { 131 // No new key. 132 return 133 } 134 v.keys = v.keys[:0] 135 for k := range v.m { 136 v.keys = append(v.keys, k) 137 } 138 sort.Strings(v.keys) 139 } 140 141 func (v *Map) Get(key string) Var { 142 v.mu.RLock() 143 defer v.mu.RUnlock() 144 return v.m[key] 145 } 146 147 func (v *Map) Set(key string, av Var) { 148 v.mu.Lock() 149 defer v.mu.Unlock() 150 v.m[key] = av 151 v.updateKeys() 152 } 153 154 func (v *Map) Add(key string, delta int64) { 155 v.mu.RLock() 156 av, ok := v.m[key] 157 v.mu.RUnlock() 158 if !ok { 159 // check again under the write lock 160 v.mu.Lock() 161 av, ok = v.m[key] 162 if !ok { 163 av = new(Int) 164 v.m[key] = av 165 v.updateKeys() 166 } 167 v.mu.Unlock() 168 } 169 170 // Add to Int; ignore otherwise. 171 if iv, ok := av.(*Int); ok { 172 iv.Add(delta) 173 } 174 } 175 176 // AddFloat adds delta to the *Float value stored under the given map key. 177 func (v *Map) AddFloat(key string, delta float64) { 178 v.mu.RLock() 179 av, ok := v.m[key] 180 v.mu.RUnlock() 181 if !ok { 182 // check again under the write lock 183 v.mu.Lock() 184 av, ok = v.m[key] 185 if !ok { 186 av = new(Float) 187 v.m[key] = av 188 v.updateKeys() 189 } 190 v.mu.Unlock() 191 } 192 193 // Add to Float; ignore otherwise. 194 if iv, ok := av.(*Float); ok { 195 iv.Add(delta) 196 } 197 } 198 199 // Do calls f for each entry in the map. 200 // The map is locked during the iteration, 201 // but existing entries may be concurrently updated. 202 func (v *Map) Do(f func(KeyValue)) { 203 v.mu.RLock() 204 defer v.mu.RUnlock() 205 v.doLocked(f) 206 } 207 208 // doLocked calls f for each entry in the map. 209 // v.mu must be held for reads. 210 func (v *Map) doLocked(f func(KeyValue)) { 211 for _, k := range v.keys { 212 f(KeyValue{k, v.m[k]}) 213 } 214 } 215 216 // String is a string variable, and satisfies the Var interface. 217 type String struct { 218 mu sync.RWMutex 219 s string 220 } 221 222 func (v *String) String() string { 223 v.mu.RLock() 224 defer v.mu.RUnlock() 225 return strconv.Quote(v.s) 226 } 227 228 func (v *String) Set(value string) { 229 v.mu.Lock() 230 defer v.mu.Unlock() 231 v.s = value 232 } 233 234 // Func implements Var by calling the function 235 // and formatting the returned value using JSON. 236 type Func func() interface{} 237 238 func (f Func) String() string { 239 v, _ := json.Marshal(f()) 240 return string(v) 241 } 242 243 // All published variables. 244 var ( 245 mutex sync.RWMutex 246 vars = make(map[string]Var) 247 varKeys []string // sorted 248 ) 249 250 // Publish declares a named exported variable. This should be called from a 251 // package's init function when it creates its Vars. If the name is already 252 // registered then this will log.Panic. 253 func Publish(name string, v Var) { 254 mutex.Lock() 255 defer mutex.Unlock() 256 if _, existing := vars[name]; existing { 257 log.Panicln("Reuse of exported var name:", name) 258 } 259 vars[name] = v 260 varKeys = append(varKeys, name) 261 sort.Strings(varKeys) 262 } 263 264 // Get retrieves a named exported variable. 265 func Get(name string) Var { 266 mutex.RLock() 267 defer mutex.RUnlock() 268 return vars[name] 269 } 270 271 // Convenience functions for creating new exported variables. 272 273 func NewInt(name string) *Int { 274 v := new(Int) 275 Publish(name, v) 276 return v 277 } 278 279 func NewFloat(name string) *Float { 280 v := new(Float) 281 Publish(name, v) 282 return v 283 } 284 285 func NewMap(name string) *Map { 286 v := new(Map).Init() 287 Publish(name, v) 288 return v 289 } 290 291 func NewString(name string) *String { 292 v := new(String) 293 Publish(name, v) 294 return v 295 } 296 297 // Do calls f for each exported variable. 298 // The global variable map is locked during the iteration, 299 // but existing entries may be concurrently updated. 300 func Do(f func(KeyValue)) { 301 mutex.RLock() 302 defer mutex.RUnlock() 303 for _, k := range varKeys { 304 f(KeyValue{k, vars[k]}) 305 } 306 } 307 308 func expvarHandler(w http.ResponseWriter, r *http.Request) { 309 w.Header().Set("Content-Type", "application/json; charset=utf-8") 310 fmt.Fprintf(w, "{\n") 311 first := true 312 Do(func(kv KeyValue) { 313 if !first { 314 fmt.Fprintf(w, ",\n") 315 } 316 first = false 317 fmt.Fprintf(w, "%q: %s", kv.Key, kv.Value) 318 }) 319 fmt.Fprintf(w, "\n}\n") 320 } 321 322 func cmdline() interface{} { 323 return os.Args 324 } 325 326 func memstats() interface{} { 327 stats := new(runtime.MemStats) 328 runtime.ReadMemStats(stats) 329 return *stats 330 } 331 332 func init() { 333 http.HandleFunc("/debug/vars", expvarHandler) 334 Publish("cmdline", Func(cmdline)) 335 Publish("memstats", Func(memstats)) 336 }