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  }