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() 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