123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367 |
- // 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 (
- "encoding/json"
- "fmt"
- "log"
- "math"
- "net/http"
- "os"
- "runtime"
- "sort"
- "strconv"
- "strings"
- "sync"
- "sync/atomic"
- )
- // Var is an abstract type for all exported variables.
- type Var interface {
- // String returns a valid JSON value for the variable.
- // Types with String methods that do not return valid JSON
- // (such as time.Time) must not be used as a Var.
- String() string
- }
- // Int is a 64-bit integer variable that satisfies the Var interface.
- type Int struct {
- i int64
- }
- func (v *Int) Value() int64 {
- return atomic.LoadInt64(&v.i)
- }
- func (v *Int) String() string {
- return strconv.FormatInt(atomic.LoadInt64(&v.i), 10)
- }
- func (v *Int) Add(delta int64) {
- atomic.AddInt64(&v.i, delta)
- }
- func (v *Int) Set(value int64) {
- atomic.StoreInt64(&v.i, value)
- }
- // Float is a 64-bit float variable that satisfies the Var interface.
- type Float struct {
- f uint64
- }
- func (v *Float) Value() float64 {
- return math.Float64frombits(atomic.LoadUint64(&v.f))
- }
- func (v *Float) String() string {
- return strconv.FormatFloat(
- math.Float64frombits(atomic.LoadUint64(&v.f)), 'g', -1, 64)
- }
- // Add adds delta to v.
- func (v *Float) Add(delta float64) {
- for {
- cur := atomic.LoadUint64(&v.f)
- curVal := math.Float64frombits(cur)
- nxtVal := curVal + delta
- nxt := math.Float64bits(nxtVal)
- if atomic.CompareAndSwapUint64(&v.f, cur, nxt) {
- return
- }
- }
- }
- // Set sets v to value.
- func (v *Float) Set(value float64) {
- atomic.StoreUint64(&v.f, math.Float64bits(value))
- }
- // Map is a string-to-Var map variable that satisfies the Var interface.
- type Map struct {
- m sync.Map // map[string]Var
- keysMu sync.RWMutex
- keys []string // sorted
- }
- // KeyValue represents a single entry in a Map.
- type KeyValue struct {
- Key string
- Value Var
- }
- func (v *Map) String() string {
- var b strings.Builder
- fmt.Fprintf(&b, "{")
- first := true
- v.Do(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()
- }
- // Init removes all keys from the map.
- func (v *Map) Init() *Map {
- v.keysMu.Lock()
- defer v.keysMu.Unlock()
- v.keys = v.keys[:0]
- v.m.Range(func(k, _ any) bool {
- v.m.Delete(k)
- return true
- })
- return v
- }
- // addKey updates the sorted list of keys in v.keys.
- func (v *Map) addKey(key string) {
- v.keysMu.Lock()
- defer v.keysMu.Unlock()
- // Using insertion sort to place key into the already-sorted v.keys.
- if i := sort.SearchStrings(v.keys, key); i >= len(v.keys) {
- v.keys = append(v.keys, key)
- } else if v.keys[i] != key {
- v.keys = append(v.keys, "")
- copy(v.keys[i+1:], v.keys[i:])
- v.keys[i] = key
- }
- }
- func (v *Map) Get(key string) Var {
- i, _ := v.m.Load(key)
- av, _ := i.(Var)
- return av
- }
- func (v *Map) Set(key string, av Var) {
- // Before we store the value, check to see whether the key is new. Try a Load
- // before LoadOrStore: LoadOrStore causes the key interface to escape even on
- // the Load path.
- if _, ok := v.m.Load(key); !ok {
- if _, dup := v.m.LoadOrStore(key, av); !dup {
- v.addKey(key)
- return
- }
- }
- v.m.Store(key, av)
- }
- // Add adds delta to the *Int value stored under the given map key.
- func (v *Map) Add(key string, delta int64) {
- i, ok := v.m.Load(key)
- if !ok {
- var dup bool
- i, dup = v.m.LoadOrStore(key, new(Int))
- if !dup {
- v.addKey(key)
- }
- }
- // Add to Int; ignore otherwise.
- if iv, ok := i.(*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) {
- i, ok := v.m.Load(key)
- if !ok {
- var dup bool
- i, dup = v.m.LoadOrStore(key, new(Float))
- if !dup {
- v.addKey(key)
- }
- }
- // Add to Float; ignore otherwise.
- if iv, ok := i.(*Float); ok {
- iv.Add(delta)
- }
- }
- // Delete deletes the given key from the map.
- func (v *Map) Delete(key string) {
- v.keysMu.Lock()
- defer v.keysMu.Unlock()
- i := sort.SearchStrings(v.keys, key)
- if i < len(v.keys) && key == v.keys[i] {
- v.keys = append(v.keys[:i], v.keys[i+1:]...)
- v.m.Delete(key)
- }
- }
- // 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.keysMu.RLock()
- defer v.keysMu.RUnlock()
- for _, k := range v.keys {
- i, _ := v.m.Load(k)
- f(KeyValue{k, i.(Var)})
- }
- }
- // String is a string variable, and satisfies the Var interface.
- type String struct {
- s atomic.Value // string
- }
- func (v *String) Value() string {
- p, _ := v.s.Load().(string)
- return p
- }
- // String implements the Var interface. To get the unquoted string
- // use Value.
- func (v *String) String() string {
- s := v.Value()
- b, _ := json.Marshal(s)
- return string(b)
- }
- func (v *String) Set(value string) {
- v.s.Store(value)
- }
- // Func implements Var by calling the function
- // and formatting the returned value using JSON.
- type Func func() any
- func (f Func) Value() any {
- return f()
- }
- func (f Func) String() string {
- v, _ := json.Marshal(f())
- return string(v)
- }
- // All published variables.
- var (
- vars sync.Map // map[string]Var
- varKeysMu sync.RWMutex
- 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) {
- if _, dup := vars.LoadOrStore(name, v); dup {
- log.Panicln("Reuse of exported var name:", name)
- }
- varKeysMu.Lock()
- defer varKeysMu.Unlock()
- varKeys = append(varKeys, name)
- sort.Strings(varKeys)
- }
- // Get retrieves a named exported variable. It returns nil if the name has
- // not been registered.
- func Get(name string) Var {
- i, _ := vars.Load(name)
- v, _ := i.(Var)
- return v
- }
- // 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)) {
- varKeysMu.RLock()
- defer varKeysMu.RUnlock()
- for _, k := range varKeys {
- val, _ := vars.Load(k)
- f(KeyValue{k, val.(Var)})
- }
- }
- 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")
- }
- // Handler returns the expvar HTTP Handler.
- //
- // This is only needed to install the handler in a non-standard location.
- func Handler() http.Handler {
- return http.HandlerFunc(expvarHandler)
- }
- func cmdline() any {
- return os.Args
- }
- func memstats() any {
- stats := new(runtime.MemStats)
- runtime.ReadMemStats(stats)
- return *stats
- }
- func init() {
- http.HandleFunc("/debug/vars", expvarHandler)
- Publish("cmdline", Func(cmdline))
- Publish("memstats", Func(memstats))
- }
|