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 }