github.com/alash3al/go@v0.0.0-20150827002835-d497eeb00540/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() 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  }