src/pkg/expvar/expvar.go - The Go Programming Language

Golang

Source file src/pkg/expvar/expvar.go

     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		"net/http"
    30		"os"
    31		"runtime"
    32		"strconv"
    33		"sync"
    34	)
    35	
    36	// Var is an abstract type for all exported variables.
    37	type Var interface {
    38		String() string
    39	}
    40	
    41	// Int is a 64-bit integer variable that satisfies the Var interface.
    42	type Int struct {
    43		i  int64
    44		mu sync.RWMutex
    45	}
    46	
    47	func (v *Int) String() string {
    48		v.mu.RLock()
    49		defer v.mu.RUnlock()
    50		return strconv.FormatInt(v.i, 10)
    51	}
    52	
    53	func (v *Int) Add(delta int64) {
    54		v.mu.Lock()
    55		defer v.mu.Unlock()
    56		v.i += delta
    57	}
    58	
    59	func (v *Int) Set(value int64) {
    60		v.mu.Lock()
    61		defer v.mu.Unlock()
    62		v.i = value
    63	}
    64	
    65	// Float is a 64-bit float variable that satisfies the Var interface.
    66	type Float struct {
    67		f  float64
    68		mu sync.RWMutex
    69	}
    70	
    71	func (v *Float) String() string {
    72		v.mu.RLock()
    73		defer v.mu.RUnlock()
    74		return strconv.FormatFloat(v.f, 'g', -1, 64)
    75	}
    76	
    77	// Add adds delta to v.
    78	func (v *Float) Add(delta float64) {
    79		v.mu.Lock()
    80		defer v.mu.Unlock()
    81		v.f += delta
    82	}
    83	
    84	// Set sets v to value.
    85	func (v *Float) Set(value float64) {
    86		v.mu.Lock()
    87		defer v.mu.Unlock()
    88		v.f = value
    89	}
    90	
    91	// Map is a string-to-Var map variable that satisfies the Var interface.
    92	type Map struct {
    93		m  map[string]Var
    94		mu sync.RWMutex
    95	}
    96	
    97	// KeyValue represents a single entry in a Map.
    98	type KeyValue struct {
    99		Key   string
   100		Value Var
   101	}
   102	
   103	func (v *Map) String() string {
   104		v.mu.RLock()
   105		defer v.mu.RUnlock()
   106		var b bytes.Buffer
   107		fmt.Fprintf(&b, "{")
   108		first := true
   109		for key, val := range v.m {
   110			if !first {
   111				fmt.Fprintf(&b, ", ")
   112			}
   113			fmt.Fprintf(&b, "\"%s\": %v", key, val)
   114			first = false
   115		}
   116		fmt.Fprintf(&b, "}")
   117		return b.String()
   118	}
   119	
   120	func (v *Map) Init() *Map {
   121		v.m = make(map[string]Var)
   122		return v
   123	}
   124	
   125	func (v *Map) Get(key string) Var {
   126		v.mu.RLock()
   127		defer v.mu.RUnlock()
   128		return v.m[key]
   129	}
   130	
   131	func (v *Map) Set(key string, av Var) {
   132		v.mu.Lock()
   133		defer v.mu.Unlock()
   134		v.m[key] = av
   135	}
   136	
   137	func (v *Map) Add(key string, delta int64) {
   138		v.mu.RLock()
   139		av, ok := v.m[key]
   140		v.mu.RUnlock()
   141		if !ok {
   142			// check again under the write lock
   143			v.mu.Lock()
   144			if _, ok = v.m[key]; !ok {
   145				av = new(Int)
   146				v.m[key] = av
   147			}
   148			v.mu.Unlock()
   149		}
   150	
   151		// Add to Int; ignore otherwise.
   152		if iv, ok := av.(*Int); ok {
   153			iv.Add(delta)
   154		}
   155	}
   156	
   157	// AddFloat adds delta to the *Float value stored under the given map key.
   158	func (v *Map) AddFloat(key string, delta float64) {
   159		v.mu.RLock()
   160		av, ok := v.m[key]
   161		v.mu.RUnlock()
   162		if !ok {
   163			// check again under the write lock
   164			v.mu.Lock()
   165			if _, ok = v.m[key]; !ok {
   166				av = new(Float)
   167				v.m[key] = av
   168			}
   169			v.mu.Unlock()
   170		}
   171	
   172		// Add to Float; ignore otherwise.
   173		if iv, ok := av.(*Float); ok {
   174			iv.Add(delta)
   175		}
   176	}
   177	
   178	// Do calls f for each entry in the map.
   179	// The map is locked during the iteration,
   180	// but existing entries may be concurrently updated.
   181	func (v *Map) Do(f func(KeyValue)) {
   182		v.mu.RLock()
   183		defer v.mu.RUnlock()
   184		for k, v := range v.m {
   185			f(KeyValue{k, v})
   186		}
   187	}
   188	
   189	// String is a string variable, and satisfies the Var interface.
   190	type String struct {
   191		s  string
   192		mu sync.RWMutex
   193	}
   194	
   195	func (v *String) String() string {
   196		v.mu.RLock()
   197		defer v.mu.RUnlock()
   198		return strconv.Quote(v.s)
   199	}
   200	
   201	func (v *String) Set(value string) {
   202		v.mu.Lock()
   203		defer v.mu.Unlock()
   204		v.s = value
   205	}
   206	
   207	// Func implements Var by calling the function
   208	// and formatting the returned value using JSON.
   209	type Func func() interface{}
   210	
   211	func (f Func) String() string {
   212		v, _ := json.Marshal(f())
   213		return string(v)
   214	}
   215	
   216	// All published variables.
   217	var (
   218		mutex sync.RWMutex
   219		vars  map[string]Var = make(map[string]Var)
   220	)
   221	
   222	// Publish declares a named exported variable. This should be called from a
   223	// package's init function when it creates its Vars. If the name is already
   224	// registered then this will log.Panic.
   225	func Publish(name string, v Var) {
   226		mutex.Lock()
   227		defer mutex.Unlock()
   228		if _, existing := vars[name]; existing {
   229			log.Panicln("Reuse of exported var name:", name)
   230		}
   231		vars[name] = v
   232	}
   233	
   234	// Get retrieves a named exported variable.
   235	func Get(name string) Var {
   236		mutex.RLock()
   237		defer mutex.RUnlock()
   238		return vars[name]
   239	}
   240	
   241	// Convenience functions for creating new exported variables.
   242	
   243	func NewInt(name string) *Int {
   244		v := new(Int)
   245		Publish(name, v)
   246		return v
   247	}
   248	
   249	func NewFloat(name string) *Float {
   250		v := new(Float)
   251		Publish(name, v)
   252		return v
   253	}
   254	
   255	func NewMap(name string) *Map {
   256		v := new(Map).Init()
   257		Publish(name, v)
   258		return v
   259	}
   260	
   261	func NewString(name string) *String {
   262		v := new(String)
   263		Publish(name, v)
   264		return v
   265	}
   266	
   267	// Do calls f for each exported variable.
   268	// The global variable map is locked during the iteration,
   269	// but existing entries may be concurrently updated.
   270	func Do(f func(KeyValue)) {
   271		mutex.RLock()
   272		defer mutex.RUnlock()
   273		for k, v := range vars {
   274			f(KeyValue{k, v})
   275		}
   276	}
   277	
   278	func expvarHandler(w http.ResponseWriter, r *http.Request) {
   279		w.Header().Set("Content-Type", "application/json; charset=utf-8")
   280		fmt.Fprintf(w, "{\n")
   281		first := true
   282		Do(func(kv KeyValue) {
   283			if !first {
   284				fmt.Fprintf(w, ",\n")
   285			}
   286			first = false
   287			fmt.Fprintf(w, "%q: %s", kv.Key, kv.Value)
   288		})
   289		fmt.Fprintf(w, "\n}\n")
   290	}
   291	
   292	func cmdline() interface{} {
   293		return os.Args
   294	}
   295	
   296	func memstats() interface{} {
   297		stats := new(runtime.MemStats)
   298		runtime.ReadMemStats(stats)
   299		return *stats
   300	}
   301	
   302	func init() {
   303		http.HandleFunc("/debug/vars", expvarHandler)
   304		Publish("cmdline", Func(cmdline))
   305		Publish("memstats", Func(memstats))
   306	}