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