github.com/euank/go@v0.0.0-20160829210321-495514729181/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 "math" 30 "net/http" 31 "os" 32 "runtime" 33 "sort" 34 "strconv" 35 "sync" 36 "sync/atomic" 37 ) 38 39 // Var is an abstract type for all exported variables. 40 type Var interface { 41 // String returns a valid JSON value for the variable. 42 // Types with String methods that do not return valid JSON 43 // (such as time.Time) must not be used as a Var. 44 String() string 45 } 46 47 // Int is a 64-bit integer variable that satisfies the Var interface. 48 type Int struct { 49 i int64 50 } 51 52 func (v *Int) String() string { 53 return strconv.FormatInt(atomic.LoadInt64(&v.i), 10) 54 } 55 56 func (v *Int) Add(delta int64) { 57 atomic.AddInt64(&v.i, delta) 58 } 59 60 func (v *Int) Set(value int64) { 61 atomic.StoreInt64(&v.i, value) 62 } 63 64 // Float is a 64-bit float variable that satisfies the Var interface. 65 type Float struct { 66 f uint64 67 } 68 69 func (v *Float) String() string { 70 return strconv.FormatFloat( 71 math.Float64frombits(atomic.LoadUint64(&v.f)), 'g', -1, 64) 72 } 73 74 // Add adds delta to v. 75 func (v *Float) Add(delta float64) { 76 for { 77 cur := atomic.LoadUint64(&v.f) 78 curVal := math.Float64frombits(cur) 79 nxtVal := curVal + delta 80 nxt := math.Float64bits(nxtVal) 81 if atomic.CompareAndSwapUint64(&v.f, cur, nxt) { 82 return 83 } 84 } 85 } 86 87 // Set sets v to value. 88 func (v *Float) Set(value float64) { 89 atomic.StoreUint64(&v.f, math.Float64bits(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 s := v.s 225 v.mu.RUnlock() 226 b, _ := json.Marshal(s) 227 return string(b) 228 } 229 230 func (v *String) Set(value string) { 231 v.mu.Lock() 232 defer v.mu.Unlock() 233 v.s = value 234 } 235 236 // Func implements Var by calling the function 237 // and formatting the returned value using JSON. 238 type Func func() interface{} 239 240 func (f Func) String() string { 241 v, _ := json.Marshal(f()) 242 return string(v) 243 } 244 245 // All published variables. 246 var ( 247 mutex sync.RWMutex 248 vars = make(map[string]Var) 249 varKeys []string // sorted 250 ) 251 252 // Publish declares a named exported variable. This should be called from a 253 // package's init function when it creates its Vars. If the name is already 254 // registered then this will log.Panic. 255 func Publish(name string, v Var) { 256 mutex.Lock() 257 defer mutex.Unlock() 258 if _, existing := vars[name]; existing { 259 log.Panicln("Reuse of exported var name:", name) 260 } 261 vars[name] = v 262 varKeys = append(varKeys, name) 263 sort.Strings(varKeys) 264 } 265 266 // Get retrieves a named exported variable. It returns nil if the name has 267 // not been registered. 268 func Get(name string) Var { 269 mutex.RLock() 270 defer mutex.RUnlock() 271 return vars[name] 272 } 273 274 // Convenience functions for creating new exported variables. 275 276 func NewInt(name string) *Int { 277 v := new(Int) 278 Publish(name, v) 279 return v 280 } 281 282 func NewFloat(name string) *Float { 283 v := new(Float) 284 Publish(name, v) 285 return v 286 } 287 288 func NewMap(name string) *Map { 289 v := new(Map).Init() 290 Publish(name, v) 291 return v 292 } 293 294 func NewString(name string) *String { 295 v := new(String) 296 Publish(name, v) 297 return v 298 } 299 300 // Do calls f for each exported variable. 301 // The global variable map is locked during the iteration, 302 // but existing entries may be concurrently updated. 303 func Do(f func(KeyValue)) { 304 mutex.RLock() 305 defer mutex.RUnlock() 306 for _, k := range varKeys { 307 f(KeyValue{k, vars[k]}) 308 } 309 } 310 311 func expvarHandler(w http.ResponseWriter, r *http.Request) { 312 w.Header().Set("Content-Type", "application/json; charset=utf-8") 313 fmt.Fprintf(w, "{\n") 314 first := true 315 Do(func(kv KeyValue) { 316 if !first { 317 fmt.Fprintf(w, ",\n") 318 } 319 first = false 320 fmt.Fprintf(w, "%q: %s", kv.Key, kv.Value) 321 }) 322 fmt.Fprintf(w, "\n}\n") 323 } 324 325 func cmdline() interface{} { 326 return os.Args 327 } 328 329 func memstats() interface{} { 330 stats := new(runtime.MemStats) 331 runtime.ReadMemStats(stats) 332 return *stats 333 } 334 335 func init() { 336 http.HandleFunc("/debug/vars", expvarHandler) 337 Publish("cmdline", Func(cmdline)) 338 Publish("memstats", Func(memstats)) 339 }