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() string 42 } 43 44 // Int is a 64-bit integer variable that satisfies the Var interface. 45 type Int struct { 46 i int64 47 } 48 49 func (v *Int) String() string { 50 return strconv.FormatInt(atomic.LoadInt64(&v.i), 10) 51 } 52 53 func (v *Int) Add(delta int64) { 54 atomic.AddInt64(&v.i, delta) 55 } 56 57 func (v *Int) Set(value int64) { 58 atomic.StoreInt64(&v.i, value) 59 } 60 61 // Float is a 64-bit float variable that satisfies the Var interface. 62 type Float struct { 63 f uint64 64 } 65 66 func (v *Float) String() string { 67 return strconv.FormatFloat( 68 math.Float64frombits(atomic.LoadUint64(&v.f)), 'g', -1, 64) 69 } 70 71 // Add adds delta to v. 72 func (v *Float) Add(delta float64) { 73 for { 74 cur := atomic.LoadUint64(&v.f) 75 curVal := math.Float64frombits(cur) 76 nxtVal := curVal + delta 77 nxt := math.Float64bits(nxtVal) 78 if atomic.CompareAndSwapUint64(&v.f, cur, nxt) { 79 return 80 } 81 } 82 } 83 84 // Set sets v to value. 85 func (v *Float) Set(value float64) { 86 atomic.StoreUint64(&v.f, math.Float64bits(value)) 87 } 88 89 // Map is a string-to-Var map variable that satisfies the Var interface. 90 type Map struct { 91 mu sync.RWMutex 92 m map[string]Var 93 keys []string // sorted 94 } 95 96 // KeyValue represents a single entry in a Map. 97 type KeyValue struct { 98 Key string 99 Value Var 100 } 101 102 func (v *Map) String() string { 103 v.mu.RLock() 104 defer v.mu.RUnlock() 105 var b bytes.Buffer 106 fmt.Fprintf(&b, "{") 107 first := true 108 v.doLocked(func(kv KeyValue) { 109 if !first { 110 fmt.Fprintf(&b, ", ") 111 } 112 fmt.Fprintf(&b, "%q: %v", kv.Key, kv.Value) 113 first = false 114 }) 115 fmt.Fprintf(&b, "}") 116 return b.String() 117 } 118 119 func (v *Map) Init() *Map { 120 v.m = make(map[string]Var) 121 return v 122 } 123 124 // updateKeys updates the sorted list of keys in v.keys. 125 // must be called with v.mu held. 126 func (v *Map) updateKeys() { 127 if len(v.m) == len(v.keys) { 128 // No new key. 129 return 130 } 131 v.keys = v.keys[:0] 132 for k := range v.m { 133 v.keys = append(v.keys, k) 134 } 135 sort.Strings(v.keys) 136 } 137 138 func (v *Map) Get(key string) Var { 139 v.mu.RLock() 140 defer v.mu.RUnlock() 141 return v.m[key] 142 } 143 144 func (v *Map) Set(key string, av Var) { 145 v.mu.Lock() 146 defer v.mu.Unlock() 147 v.m[key] = av 148 v.updateKeys() 149 } 150 151 func (v *Map) Add(key string, delta int64) { 152 v.mu.RLock() 153 av, ok := v.m[key] 154 v.mu.RUnlock() 155 if !ok { 156 // check again under the write lock 157 v.mu.Lock() 158 av, ok = v.m[key] 159 if !ok { 160 av = new(Int) 161 v.m[key] = av 162 v.updateKeys() 163 } 164 v.mu.Unlock() 165 } 166 167 // Add to Int; ignore otherwise. 168 if iv, ok := av.(*Int); ok { 169 iv.Add(delta) 170 } 171 } 172 173 // AddFloat adds delta to the *Float value stored under the given map key. 174 func (v *Map) AddFloat(key string, delta float64) { 175 v.mu.RLock() 176 av, ok := v.m[key] 177 v.mu.RUnlock() 178 if !ok { 179 // check again under the write lock 180 v.mu.Lock() 181 av, ok = v.m[key] 182 if !ok { 183 av = new(Float) 184 v.m[key] = av 185 v.updateKeys() 186 } 187 v.mu.Unlock() 188 } 189 190 // Add to Float; ignore otherwise. 191 if iv, ok := av.(*Float); ok { 192 iv.Add(delta) 193 } 194 } 195 196 // Do calls f for each entry in the map. 197 // The map is locked during the iteration, 198 // but existing entries may be concurrently updated. 199 func (v *Map) Do(f func(KeyValue)) { 200 v.mu.RLock() 201 defer v.mu.RUnlock() 202 v.doLocked(f) 203 } 204 205 // doLocked calls f for each entry in the map. 206 // v.mu must be held for reads. 207 func (v *Map) doLocked(f func(KeyValue)) { 208 for _, k := range v.keys { 209 f(KeyValue{k, v.m[k]}) 210 } 211 } 212 213 // String is a string variable, and satisfies the Var interface. 214 type String struct { 215 mu sync.RWMutex 216 s string 217 } 218 219 func (v *String) String() string { 220 v.mu.RLock() 221 defer v.mu.RUnlock() 222 return strconv.Quote(v.s) 223 } 224 225 func (v *String) Set(value string) { 226 v.mu.Lock() 227 defer v.mu.Unlock() 228 v.s = value 229 } 230 231 // Func implements Var by calling the function 232 // and formatting the returned value using JSON. 233 type Func func() interface{} 234 235 func (f Func) String() string { 236 v, _ := json.Marshal(f()) 237 return string(v) 238 } 239 240 // All published variables. 241 var ( 242 mutex sync.RWMutex 243 vars = make(map[string]Var) 244 varKeys []string // sorted 245 ) 246 247 // Publish declares a named exported variable. This should be called from a 248 // package's init function when it creates its Vars. If the name is already 249 // registered then this will log.Panic. 250 func Publish(name string, v Var) { 251 mutex.Lock() 252 defer mutex.Unlock() 253 if _, existing := vars[name]; existing { 254 log.Panicln("Reuse of exported var name:", name) 255 } 256 vars[name] = v 257 varKeys = append(varKeys, name) 258 sort.Strings(varKeys) 259 } 260 261 // Get retrieves a named exported variable. 262 func Get(name string) Var { 263 mutex.RLock() 264 defer mutex.RUnlock() 265 return vars[name] 266 } 267 268 // Convenience functions for creating new exported variables. 269 270 func NewInt(name string) *Int { 271 v := new(Int) 272 Publish(name, v) 273 return v 274 } 275 276 func NewFloat(name string) *Float { 277 v := new(Float) 278 Publish(name, v) 279 return v 280 } 281 282 func NewMap(name string) *Map { 283 v := new(Map).Init() 284 Publish(name, v) 285 return v 286 } 287 288 func NewString(name string) *String { 289 v := new(String) 290 Publish(name, v) 291 return v 292 } 293 294 // Do calls f for each exported variable. 295 // The global variable map is locked during the iteration, 296 // but existing entries may be concurrently updated. 297 func Do(f func(KeyValue)) { 298 mutex.RLock() 299 defer mutex.RUnlock() 300 for _, k := range varKeys { 301 f(KeyValue{k, vars[k]}) 302 } 303 } 304 305 func expvarHandler(w http.ResponseWriter, r *http.Request) { 306 w.Header().Set("Content-Type", "application/json; charset=utf-8") 307 fmt.Fprintf(w, "{\n") 308 first := true 309 Do(func(kv KeyValue) { 310 if !first { 311 fmt.Fprintf(w, ",\n") 312 } 313 first = false 314 fmt.Fprintf(w, "%q: %s", kv.Key, kv.Value) 315 }) 316 fmt.Fprintf(w, "\n}\n") 317 } 318 319 func cmdline() interface{} { 320 return os.Args 321 } 322 323 func memstats() interface{} { 324 stats := new(runtime.MemStats) 325 runtime.ReadMemStats(stats) 326 return *stats 327 } 328 329 func init() { 330 http.HandleFunc("/debug/vars", expvarHandler) 331 Publish("cmdline", Func(cmdline)) 332 Publish("memstats", Func(memstats)) 333 } 334