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