123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337 |
- // Copyright 2009 The Go Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style
- // license that can be found in the LICENSE file.
- // Package expvar provides a standardized interface to public variables, such
- // as operation counters in servers. It exposes these variables via HTTP at
- // /debug/vars in JSON format.
- //
- // Operations to set or modify these public variables are atomic.
- //
- // In addition to adding the HTTP handler, this package registers the
- // following variables:
- //
- // cmdline os.Args
- // memstats runtime.Memstats
- //
- // The package is sometimes only imported for the side effect of
- // registering its HTTP handler and the above variables. To use it
- // this way, link this package into your program:
- // import _ "expvar"
- //
- package expvar
- import (
- "bytes"
- "encoding/json"
- "fmt"
- "log"
- "net/http"
- "os"
- "runtime"
- "sort"
- "strconv"
- "sync"
- )
- // Var is an abstract type for all exported variables.
- type Var interface {
- String() string
- }
- // Int is a 64-bit integer variable that satisfies the Var interface.
- type Int struct {
- mu sync.RWMutex
- i int64
- }
- func (v *Int) String() string {
- v.mu.RLock()
- defer v.mu.RUnlock()
- return strconv.FormatInt(v.i, 10)
- }
- func (v *Int) Add(delta int64) {
- v.mu.Lock()
- defer v.mu.Unlock()
- v.i += delta
- }
- func (v *Int) Set(value int64) {
- v.mu.Lock()
- defer v.mu.Unlock()
- v.i = value
- }
- // Float is a 64-bit float variable that satisfies the Var interface.
- type Float struct {
- mu sync.RWMutex
- f float64
- }
- func (v *Float) String() string {
- v.mu.RLock()
- defer v.mu.RUnlock()
- return strconv.FormatFloat(v.f, 'g', -1, 64)
- }
- // Add adds delta to v.
- func (v *Float) Add(delta float64) {
- v.mu.Lock()
- defer v.mu.Unlock()
- v.f += delta
- }
- // Set sets v to value.
- func (v *Float) Set(value float64) {
- v.mu.Lock()
- defer v.mu.Unlock()
- v.f = value
- }
- // Map is a string-to-Var map variable that satisfies the Var interface.
- type Map struct {
- mu sync.RWMutex
- m map[string]Var
- keys []string // sorted
- }
- // KeyValue represents a single entry in a Map.
- type KeyValue struct {
- Key string
- Value Var
- }
- func (v *Map) String() string {
- v.mu.RLock()
- defer v.mu.RUnlock()
- var b bytes.Buffer
- fmt.Fprintf(&b, "{")
- first := true
- v.doLocked(func(kv KeyValue) {
- if !first {
- fmt.Fprintf(&b, ", ")
- }
- fmt.Fprintf(&b, "%q: %v", kv.Key, kv.Value)
- first = false
- })
- fmt.Fprintf(&b, "}")
- return b.String()
- }
- func (v *Map) Init() *Map {
- v.m = make(map[string]Var)
- return v
- }
- // updateKeys updates the sorted list of keys in v.keys.
- // must be called with v.mu held.
- func (v *Map) updateKeys() {
- if len(v.m) == len(v.keys) {
- // No new key.
- return
- }
- v.keys = v.keys[:0]
- for k := range v.m {
- v.keys = append(v.keys, k)
- }
- sort.Strings(v.keys)
- }
- func (v *Map) Get(key string) Var {
- v.mu.RLock()
- defer v.mu.RUnlock()
- return v.m[key]
- }
- func (v *Map) Set(key string, av Var) {
- v.mu.Lock()
- defer v.mu.Unlock()
- v.m[key] = av
- v.updateKeys()
- }
- func (v *Map) Add(key string, delta int64) {
- v.mu.RLock()
- av, ok := v.m[key]
- v.mu.RUnlock()
- if !ok {
- // check again under the write lock
- v.mu.Lock()
- av, ok = v.m[key]
- if !ok {
- av = new(Int)
- v.m[key] = av
- v.updateKeys()
- }
- v.mu.Unlock()
- }
- // Add to Int; ignore otherwise.
- if iv, ok := av.(*Int); ok {
- iv.Add(delta)
- }
- }
- // AddFloat adds delta to the *Float value stored under the given map key.
- func (v *Map) AddFloat(key string, delta float64) {
- v.mu.RLock()
- av, ok := v.m[key]
- v.mu.RUnlock()
- if !ok {
- // check again under the write lock
- v.mu.Lock()
- av, ok = v.m[key]
- if !ok {
- av = new(Float)
- v.m[key] = av
- v.updateKeys()
- }
- v.mu.Unlock()
- }
- // Add to Float; ignore otherwise.
- if iv, ok := av.(*Float); ok {
- iv.Add(delta)
- }
- }
- // Do calls f for each entry in the map.
- // The map is locked during the iteration,
- // but existing entries may be concurrently updated.
- func (v *Map) Do(f func(KeyValue)) {
- v.mu.RLock()
- defer v.mu.RUnlock()
- v.doLocked(f)
- }
- // doLocked calls f for each entry in the map.
- // v.mu must be held for reads.
- func (v *Map) doLocked(f func(KeyValue)) {
- for _, k := range v.keys {
- f(KeyValue{k, v.m[k]})
- }
- }
- // String is a string variable, and satisfies the Var interface.
- type String struct {
- mu sync.RWMutex
- s string
- }
- func (v *String) String() string {
- v.mu.RLock()
- defer v.mu.RUnlock()
- return strconv.Quote(v.s)
- }
- func (v *String) Set(value string) {
- v.mu.Lock()
- defer v.mu.Unlock()
- v.s = value
- }
- // Func implements Var by calling the function
- // and formatting the returned value using JSON.
- type Func func() interface{}
- func (f Func) String() string {
- v, _ := json.Marshal(f())
- return string(v)
- }
- // All published variables.
- var (
- mutex sync.RWMutex
- vars = make(map[string]Var)
- varKeys []string // sorted
- )
- // Publish declares a named exported variable. This should be called from a
- // package's init function when it creates its Vars. If the name is already
- // registered then this will log.Panic.
- func Publish(name string, v Var) {
- mutex.Lock()
- defer mutex.Unlock()
- if _, existing := vars[name]; existing {
- log.Panicln("Reuse of exported var name:", name)
- }
- vars[name] = v
- varKeys = append(varKeys, name)
- sort.Strings(varKeys)
- }
- // Get retrieves a named exported variable.
- func Get(name string) Var {
- mutex.RLock()
- defer mutex.RUnlock()
- return vars[name]
- }
- // Convenience functions for creating new exported variables.
- func NewInt(name string) *Int {
- v := new(Int)
- Publish(name, v)
- return v
- }
- func NewFloat(name string) *Float {
- v := new(Float)
- Publish(name, v)
- return v
- }
- func NewMap(name string) *Map {
- v := new(Map).Init()
- Publish(name, v)
- return v
- }
- func NewString(name string) *String {
- v := new(String)
- Publish(name, v)
- return v
- }
- // Do calls f for each exported variable.
- // The global variable map is locked during the iteration,
- // but existing entries may be concurrently updated.
- func Do(f func(KeyValue)) {
- mutex.RLock()
- defer mutex.RUnlock()
- for _, k := range varKeys {
- f(KeyValue{k, vars[k]})
- }
- }
- func expvarHandler(w http.ResponseWriter, r *http.Request) {
- w.Header().Set("Content-Type", "application/json; charset=utf-8")
- fmt.Fprintf(w, "{\n")
- first := true
- Do(func(kv KeyValue) {
- if !first {
- fmt.Fprintf(w, ",\n")
- }
- first = false
- fmt.Fprintf(w, "%q: %s", kv.Key, kv.Value)
- })
- fmt.Fprintf(w, "\n}\n")
- }
- func cmdline() interface{} {
- return os.Args
- }
- func memstats() interface{} {
- stats := new(runtime.MemStats)
- runtime.ReadMemStats(stats)
- return *stats
- }
- func init() {
- http.HandleFunc("/debug/vars", expvarHandler)
- Publish("cmdline", Func(cmdline))
- Publish("memstats", Func(memstats))
- }
|