David Symonds | 3cc702b | 2009-04-19 21:17:27 -0700 | [diff] [blame] | 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 | |
Nigel Tao | 6a186d3 | 2011-04-20 09:57:05 +1000 | [diff] [blame] | 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. |
Russ Cox | a709876 | 2010-03-29 13:06:26 -0700 | [diff] [blame] | 8 | // |
Sam Thorogood | 3b9c9d2 | 2010-10-11 13:14:07 -0400 | [diff] [blame] | 9 | // Operations to set or modify these public variables are atomic. |
| 10 | // |
Russ Cox | a709876 | 2010-03-29 13:06:26 -0700 | [diff] [blame] | 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 |
Russ Cox | ae7497b | 2012-02-06 13:34:35 -0500 | [diff] [blame] | 19 | // this way, link this package into your program: |
Russ Cox | a709876 | 2010-03-29 13:06:26 -0700 | [diff] [blame] | 20 | // import _ "expvar" |
| 21 | // |
Rob Pike | 0632bb4 | 2009-09-30 13:11:33 -0700 | [diff] [blame] | 22 | package expvar |
David Symonds | 3cc702b | 2009-04-19 21:17:27 -0700 | [diff] [blame] | 23 | |
| 24 | import ( |
Robert Griesemer | 1c72959 | 2009-12-15 15:27:16 -0800 | [diff] [blame] | 25 | "bytes" |
Rob Pike | 30aa701 | 2011-11-08 15:40:58 -0800 | [diff] [blame] | 26 | "encoding/json" |
Robert Griesemer | 1c72959 | 2009-12-15 15:27:16 -0800 | [diff] [blame] | 27 | "fmt" |
Robert Griesemer | 1c72959 | 2009-12-15 15:27:16 -0800 | [diff] [blame] | 28 | "log" |
Matt T. Proud | 926616d | 2015-02-02 00:33:44 -0800 | [diff] [blame] | 29 | "math" |
Rob Pike | 30aa701 | 2011-11-08 15:40:58 -0800 | [diff] [blame] | 30 | "net/http" |
Russ Cox | a709876 | 2010-03-29 13:06:26 -0700 | [diff] [blame] | 31 | "os" |
| 32 | "runtime" |
Brad Fitzpatrick | cfb9cf0 | 2014-01-20 09:59:23 -0800 | [diff] [blame] | 33 | "sort" |
Robert Griesemer | 1c72959 | 2009-12-15 15:27:16 -0800 | [diff] [blame] | 34 | "strconv" |
| 35 | "sync" |
Evan Phoenix | bd043d8 | 2015-01-28 09:16:44 -0800 | [diff] [blame] | 36 | "sync/atomic" |
David Symonds | 3cc702b | 2009-04-19 21:17:27 -0700 | [diff] [blame] | 37 | ) |
| 38 | |
David Symonds | 2f28494 | 2009-05-04 15:14:22 -0700 | [diff] [blame] | 39 | // Var is an abstract type for all exported variables. |
| 40 | type Var interface { |
Robert Griesemer | 1c72959 | 2009-12-15 15:27:16 -0800 | [diff] [blame] | 41 | String() string |
David Symonds | de489fb | 2009-04-20 22:38:14 -0700 | [diff] [blame] | 42 | } |
| 43 | |
Jos Visser | 25f762c | 2011-01-18 11:24:43 -0500 | [diff] [blame] | 44 | // Int is a 64-bit integer variable that satisfies the Var interface. |
David Symonds | 2f28494 | 2009-05-04 15:14:22 -0700 | [diff] [blame] | 45 | type Int struct { |
Evan Phoenix | bd043d8 | 2015-01-28 09:16:44 -0800 | [diff] [blame] | 46 | i int64 |
David Symonds | de489fb | 2009-04-20 22:38:14 -0700 | [diff] [blame] | 47 | } |
| 48 | |
Brad Fitzpatrick | 1042d7d | 2012-03-05 11:09:50 -0800 | [diff] [blame] | 49 | func (v *Int) String() string { |
Evan Phoenix | bd043d8 | 2015-01-28 09:16:44 -0800 | [diff] [blame] | 50 | return strconv.FormatInt(atomic.LoadInt64(&v.i), 10) |
Brad Fitzpatrick | 1042d7d | 2012-03-05 11:09:50 -0800 | [diff] [blame] | 51 | } |
David Symonds | de489fb | 2009-04-20 22:38:14 -0700 | [diff] [blame] | 52 | |
David Symonds | 2f28494 | 2009-05-04 15:14:22 -0700 | [diff] [blame] | 53 | func (v *Int) Add(delta int64) { |
Evan Phoenix | bd043d8 | 2015-01-28 09:16:44 -0800 | [diff] [blame] | 54 | atomic.AddInt64(&v.i, delta) |
David Symonds | 2f28494 | 2009-05-04 15:14:22 -0700 | [diff] [blame] | 55 | } |
| 56 | |
Sam Thorogood | 3b9c9d2 | 2010-10-11 13:14:07 -0400 | [diff] [blame] | 57 | func (v *Int) Set(value int64) { |
Evan Phoenix | bd043d8 | 2015-01-28 09:16:44 -0800 | [diff] [blame] | 58 | atomic.StoreInt64(&v.i, value) |
Sam Thorogood | 3b9c9d2 | 2010-10-11 13:14:07 -0400 | [diff] [blame] | 59 | } |
| 60 | |
Jos Visser | 25f762c | 2011-01-18 11:24:43 -0500 | [diff] [blame] | 61 | // Float is a 64-bit float variable that satisfies the Var interface. |
| 62 | type Float struct { |
Matt T. Proud | 926616d | 2015-02-02 00:33:44 -0800 | [diff] [blame] | 63 | f uint64 |
Jos Visser | 25f762c | 2011-01-18 11:24:43 -0500 | [diff] [blame] | 64 | } |
| 65 | |
Brad Fitzpatrick | 1042d7d | 2012-03-05 11:09:50 -0800 | [diff] [blame] | 66 | func (v *Float) String() string { |
Matt T. Proud | 926616d | 2015-02-02 00:33:44 -0800 | [diff] [blame] | 67 | return strconv.FormatFloat( |
| 68 | math.Float64frombits(atomic.LoadUint64(&v.f)), 'g', -1, 64) |
Brad Fitzpatrick | 1042d7d | 2012-03-05 11:09:50 -0800 | [diff] [blame] | 69 | } |
Jos Visser | 25f762c | 2011-01-18 11:24:43 -0500 | [diff] [blame] | 70 | |
| 71 | // Add adds delta to v. |
| 72 | func (v *Float) Add(delta float64) { |
Matt T. Proud | 926616d | 2015-02-02 00:33:44 -0800 | [diff] [blame] | 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 | } |
Jos Visser | 25f762c | 2011-01-18 11:24:43 -0500 | [diff] [blame] | 82 | } |
| 83 | |
| 84 | // Set sets v to value. |
| 85 | func (v *Float) Set(value float64) { |
Matt T. Proud | 926616d | 2015-02-02 00:33:44 -0800 | [diff] [blame] | 86 | atomic.StoreUint64(&v.f, math.Float64bits(value)) |
Jos Visser | 25f762c | 2011-01-18 11:24:43 -0500 | [diff] [blame] | 87 | } |
| 88 | |
| 89 | // Map is a string-to-Var map variable that satisfies the Var interface. |
David Symonds | 2f28494 | 2009-05-04 15:14:22 -0700 | [diff] [blame] | 90 | type Map struct { |
Brad Fitzpatrick | cfb9cf0 | 2014-01-20 09:59:23 -0800 | [diff] [blame] | 91 | mu sync.RWMutex |
| 92 | m map[string]Var |
| 93 | keys []string // sorted |
David Symonds | 2f28494 | 2009-05-04 15:14:22 -0700 | [diff] [blame] | 94 | } |
| 95 | |
| 96 | // KeyValue represents a single entry in a Map. |
| 97 | type KeyValue struct { |
Robert Griesemer | 1c72959 | 2009-12-15 15:27:16 -0800 | [diff] [blame] | 98 | Key string |
| 99 | Value Var |
David Symonds | 2f28494 | 2009-05-04 15:14:22 -0700 | [diff] [blame] | 100 | } |
| 101 | |
| 102 | func (v *Map) String() string { |
David Symonds | 715588f | 2012-02-04 14:32:05 +1100 | [diff] [blame] | 103 | v.mu.RLock() |
| 104 | defer v.mu.RUnlock() |
Brad Fitzpatrick | 1042d7d | 2012-03-05 11:09:50 -0800 | [diff] [blame] | 105 | var b bytes.Buffer |
| 106 | fmt.Fprintf(&b, "{") |
Robert Griesemer | 1c72959 | 2009-12-15 15:27:16 -0800 | [diff] [blame] | 107 | first := true |
Brad Fitzpatrick | 666f5b4 | 2014-03-18 11:38:39 -0700 | [diff] [blame] | 108 | v.doLocked(func(kv KeyValue) { |
David Symonds | 2f28494 | 2009-05-04 15:14:22 -0700 | [diff] [blame] | 109 | if !first { |
Brad Fitzpatrick | 1042d7d | 2012-03-05 11:09:50 -0800 | [diff] [blame] | 110 | fmt.Fprintf(&b, ", ") |
David Symonds | 2f28494 | 2009-05-04 15:14:22 -0700 | [diff] [blame] | 111 | } |
Rui Ueyama | 74c6b84 | 2014-04-10 21:14:04 -0700 | [diff] [blame] | 112 | fmt.Fprintf(&b, "%q: %v", kv.Key, kv.Value) |
Robert Griesemer | 1c72959 | 2009-12-15 15:27:16 -0800 | [diff] [blame] | 113 | first = false |
Brad Fitzpatrick | cfb9cf0 | 2014-01-20 09:59:23 -0800 | [diff] [blame] | 114 | }) |
Brad Fitzpatrick | 1042d7d | 2012-03-05 11:09:50 -0800 | [diff] [blame] | 115 | fmt.Fprintf(&b, "}") |
Robert Griesemer | 1c72959 | 2009-12-15 15:27:16 -0800 | [diff] [blame] | 116 | return b.String() |
David Symonds | de489fb | 2009-04-20 22:38:14 -0700 | [diff] [blame] | 117 | } |
| 118 | |
Brendan O'Dea | 89df071 | 2009-05-18 15:42:09 -0700 | [diff] [blame] | 119 | func (v *Map) Init() *Map { |
Robert Griesemer | 1c72959 | 2009-12-15 15:27:16 -0800 | [diff] [blame] | 120 | v.m = make(map[string]Var) |
| 121 | return v |
Brendan O'Dea | 89df071 | 2009-05-18 15:42:09 -0700 | [diff] [blame] | 122 | } |
| 123 | |
Brad Fitzpatrick | cfb9cf0 | 2014-01-20 09:59:23 -0800 | [diff] [blame] | 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 | |
David Symonds | 2f28494 | 2009-05-04 15:14:22 -0700 | [diff] [blame] | 138 | func (v *Map) Get(key string) Var { |
David Symonds | 715588f | 2012-02-04 14:32:05 +1100 | [diff] [blame] | 139 | v.mu.RLock() |
| 140 | defer v.mu.RUnlock() |
Russ Cox | c7122a3 | 2010-03-30 10:51:11 -0700 | [diff] [blame] | 141 | return v.m[key] |
David Symonds | de489fb | 2009-04-20 22:38:14 -0700 | [diff] [blame] | 142 | } |
| 143 | |
David Symonds | 2f28494 | 2009-05-04 15:14:22 -0700 | [diff] [blame] | 144 | func (v *Map) Set(key string, av Var) { |
Robert Griesemer | 1c72959 | 2009-12-15 15:27:16 -0800 | [diff] [blame] | 145 | v.mu.Lock() |
| 146 | defer v.mu.Unlock() |
| 147 | v.m[key] = av |
Brad Fitzpatrick | cfb9cf0 | 2014-01-20 09:59:23 -0800 | [diff] [blame] | 148 | v.updateKeys() |
David Symonds | 2f28494 | 2009-05-04 15:14:22 -0700 | [diff] [blame] | 149 | } |
| 150 | |
| 151 | func (v *Map) Add(key string, delta int64) { |
David Symonds | 715588f | 2012-02-04 14:32:05 +1100 | [diff] [blame] | 152 | v.mu.RLock() |
Robert Griesemer | 1c72959 | 2009-12-15 15:27:16 -0800 | [diff] [blame] | 153 | av, ok := v.m[key] |
David Symonds | 715588f | 2012-02-04 14:32:05 +1100 | [diff] [blame] | 154 | v.mu.RUnlock() |
David Symonds | 2f28494 | 2009-05-04 15:14:22 -0700 | [diff] [blame] | 155 | if !ok { |
David Symonds | 715588f | 2012-02-04 14:32:05 +1100 | [diff] [blame] | 156 | // check again under the write lock |
| 157 | v.mu.Lock() |
Brad Fitzpatrick | cfb9cf0 | 2014-01-20 09:59:23 -0800 | [diff] [blame] | 158 | av, ok = v.m[key] |
| 159 | if !ok { |
David Symonds | 715588f | 2012-02-04 14:32:05 +1100 | [diff] [blame] | 160 | av = new(Int) |
| 161 | v.m[key] = av |
Brad Fitzpatrick | cfb9cf0 | 2014-01-20 09:59:23 -0800 | [diff] [blame] | 162 | v.updateKeys() |
David Symonds | 715588f | 2012-02-04 14:32:05 +1100 | [diff] [blame] | 163 | } |
| 164 | v.mu.Unlock() |
David Symonds | de489fb | 2009-04-20 22:38:14 -0700 | [diff] [blame] | 165 | } |
David Symonds | de489fb | 2009-04-20 22:38:14 -0700 | [diff] [blame] | 166 | |
David Symonds | 2f28494 | 2009-05-04 15:14:22 -0700 | [diff] [blame] | 167 | // Add to Int; ignore otherwise. |
| 168 | if iv, ok := av.(*Int); ok { |
Robert Griesemer | 40621d5 | 2009-11-09 12:07:39 -0800 | [diff] [blame] | 169 | iv.Add(delta) |
David Symonds | f4b92c8 | 2009-04-26 20:57:01 -0700 | [diff] [blame] | 170 | } |
David Symonds | f4b92c8 | 2009-04-26 20:57:01 -0700 | [diff] [blame] | 171 | } |
| 172 | |
Jos Visser | 25f762c | 2011-01-18 11:24:43 -0500 | [diff] [blame] | 173 | // AddFloat adds delta to the *Float value stored under the given map key. |
| 174 | func (v *Map) AddFloat(key string, delta float64) { |
David Symonds | 715588f | 2012-02-04 14:32:05 +1100 | [diff] [blame] | 175 | v.mu.RLock() |
Jos Visser | 25f762c | 2011-01-18 11:24:43 -0500 | [diff] [blame] | 176 | av, ok := v.m[key] |
David Symonds | 715588f | 2012-02-04 14:32:05 +1100 | [diff] [blame] | 177 | v.mu.RUnlock() |
Jos Visser | 25f762c | 2011-01-18 11:24:43 -0500 | [diff] [blame] | 178 | if !ok { |
David Symonds | 715588f | 2012-02-04 14:32:05 +1100 | [diff] [blame] | 179 | // check again under the write lock |
| 180 | v.mu.Lock() |
Brad Fitzpatrick | cfb9cf0 | 2014-01-20 09:59:23 -0800 | [diff] [blame] | 181 | av, ok = v.m[key] |
| 182 | if !ok { |
David Symonds | 715588f | 2012-02-04 14:32:05 +1100 | [diff] [blame] | 183 | av = new(Float) |
| 184 | v.m[key] = av |
Brad Fitzpatrick | cfb9cf0 | 2014-01-20 09:59:23 -0800 | [diff] [blame] | 185 | v.updateKeys() |
David Symonds | 715588f | 2012-02-04 14:32:05 +1100 | [diff] [blame] | 186 | } |
| 187 | v.mu.Unlock() |
Jos Visser | 25f762c | 2011-01-18 11:24:43 -0500 | [diff] [blame] | 188 | } |
| 189 | |
| 190 | // Add to Float; ignore otherwise. |
| 191 | if iv, ok := av.(*Float); ok { |
| 192 | iv.Add(delta) |
| 193 | } |
| 194 | } |
| 195 | |
David Symonds | 715588f | 2012-02-04 14:32:05 +1100 | [diff] [blame] | 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() |
Brad Fitzpatrick | 666f5b4 | 2014-03-18 11:38:39 -0700 | [diff] [blame] | 202 | v.doLocked(f) |
| 203 | } |
| 204 | |
Robert Hencke | f999e14 | 2014-04-29 12:44:40 -0400 | [diff] [blame] | 205 | // doLocked calls f for each entry in the map. |
Brad Fitzpatrick | 666f5b4 | 2014-03-18 11:38:39 -0700 | [diff] [blame] | 206 | // v.mu must be held for reads. |
| 207 | func (v *Map) doLocked(f func(KeyValue)) { |
Brad Fitzpatrick | cfb9cf0 | 2014-01-20 09:59:23 -0800 | [diff] [blame] | 208 | for _, k := range v.keys { |
| 209 | f(KeyValue{k, v.m[k]}) |
David Symonds | 2f28494 | 2009-05-04 15:14:22 -0700 | [diff] [blame] | 210 | } |
David Symonds | 2f28494 | 2009-05-04 15:14:22 -0700 | [diff] [blame] | 211 | } |
| 212 | |
| 213 | // String is a string variable, and satisfies the Var interface. |
| 214 | type String struct { |
David Symonds | 63e383c | 2012-03-06 09:13:26 +1100 | [diff] [blame] | 215 | mu sync.RWMutex |
Brad Fitzpatrick | cfb9cf0 | 2014-01-20 09:59:23 -0800 | [diff] [blame] | 216 | s string |
David Symonds | 2f28494 | 2009-05-04 15:14:22 -0700 | [diff] [blame] | 217 | } |
| 218 | |
David Symonds | 63e383c | 2012-03-06 09:13:26 +1100 | [diff] [blame] | 219 | func (v *String) String() string { |
| 220 | v.mu.RLock() |
| 221 | defer v.mu.RUnlock() |
| 222 | return strconv.Quote(v.s) |
| 223 | } |
David Symonds | 2f28494 | 2009-05-04 15:14:22 -0700 | [diff] [blame] | 224 | |
David Symonds | 63e383c | 2012-03-06 09:13:26 +1100 | [diff] [blame] | 225 | func (v *String) Set(value string) { |
| 226 | v.mu.Lock() |
| 227 | defer v.mu.Unlock() |
| 228 | v.s = value |
| 229 | } |
David Symonds | 2f28494 | 2009-05-04 15:14:22 -0700 | [diff] [blame] | 230 | |
David Symonds | 57d0c26 | 2011-04-15 01:21:18 -0700 | [diff] [blame] | 231 | // Func implements Var by calling the function |
| 232 | // and formatting the returned value using JSON. |
| 233 | type Func func() interface{} |
David Symonds | 5a12b18 | 2009-05-24 15:04:43 -0700 | [diff] [blame] | 234 | |
David Symonds | 57d0c26 | 2011-04-15 01:21:18 -0700 | [diff] [blame] | 235 | func (f Func) String() string { |
| 236 | v, _ := json.Marshal(f()) |
| 237 | return string(v) |
| 238 | } |
Russ Cox | a709876 | 2010-03-29 13:06:26 -0700 | [diff] [blame] | 239 | |
David Symonds | 2f28494 | 2009-05-04 15:14:22 -0700 | [diff] [blame] | 240 | // All published variables. |
David Symonds | 715588f | 2012-02-04 14:32:05 +1100 | [diff] [blame] | 241 | var ( |
Brad Fitzpatrick | cfb9cf0 | 2014-01-20 09:59:23 -0800 | [diff] [blame] | 242 | mutex sync.RWMutex |
| 243 | vars = make(map[string]Var) |
| 244 | varKeys []string // sorted |
David Symonds | 715588f | 2012-02-04 14:32:05 +1100 | [diff] [blame] | 245 | ) |
David Symonds | 2f28494 | 2009-05-04 15:14:22 -0700 | [diff] [blame] | 246 | |
Rob Pike | 0244bae | 2011-12-09 14:24:51 -0800 | [diff] [blame] | 247 | // Publish declares a named exported variable. This should be called from a |
David Symonds | 2f28494 | 2009-05-04 15:14:22 -0700 | [diff] [blame] | 248 | // package's init function when it creates its Vars. If the name is already |
Rob Pike | 12da5a9 | 2010-10-12 12:59:18 -0700 | [diff] [blame] | 249 | // registered then this will log.Panic. |
David Symonds | 2f28494 | 2009-05-04 15:14:22 -0700 | [diff] [blame] | 250 | func Publish(name string, v Var) { |
Robert Griesemer | 1c72959 | 2009-12-15 15:27:16 -0800 | [diff] [blame] | 251 | mutex.Lock() |
| 252 | defer mutex.Unlock() |
David Symonds | 2f28494 | 2009-05-04 15:14:22 -0700 | [diff] [blame] | 253 | if _, existing := vars[name]; existing { |
Rob Pike | 12da5a9 | 2010-10-12 12:59:18 -0700 | [diff] [blame] | 254 | log.Panicln("Reuse of exported var name:", name) |
David Symonds | 2f28494 | 2009-05-04 15:14:22 -0700 | [diff] [blame] | 255 | } |
Robert Griesemer | 1c72959 | 2009-12-15 15:27:16 -0800 | [diff] [blame] | 256 | vars[name] = v |
Brad Fitzpatrick | cfb9cf0 | 2014-01-20 09:59:23 -0800 | [diff] [blame] | 257 | varKeys = append(varKeys, name) |
| 258 | sort.Strings(varKeys) |
David Symonds | 2f28494 | 2009-05-04 15:14:22 -0700 | [diff] [blame] | 259 | } |
| 260 | |
| 261 | // Get retrieves a named exported variable. |
| 262 | func Get(name string) Var { |
David Symonds | 715588f | 2012-02-04 14:32:05 +1100 | [diff] [blame] | 263 | mutex.RLock() |
| 264 | defer mutex.RUnlock() |
Russ Cox | c7122a3 | 2010-03-30 10:51:11 -0700 | [diff] [blame] | 265 | return vars[name] |
David Symonds | 2f28494 | 2009-05-04 15:14:22 -0700 | [diff] [blame] | 266 | } |
| 267 | |
| 268 | // Convenience functions for creating new exported variables. |
| 269 | |
| 270 | func NewInt(name string) *Int { |
Robert Griesemer | 1c72959 | 2009-12-15 15:27:16 -0800 | [diff] [blame] | 271 | v := new(Int) |
| 272 | Publish(name, v) |
| 273 | return v |
David Symonds | 2f28494 | 2009-05-04 15:14:22 -0700 | [diff] [blame] | 274 | } |
| 275 | |
Jos Visser | 25f762c | 2011-01-18 11:24:43 -0500 | [diff] [blame] | 276 | func NewFloat(name string) *Float { |
| 277 | v := new(Float) |
| 278 | Publish(name, v) |
| 279 | return v |
| 280 | } |
| 281 | |
David Symonds | 2f28494 | 2009-05-04 15:14:22 -0700 | [diff] [blame] | 282 | func NewMap(name string) *Map { |
Robert Griesemer | 1c72959 | 2009-12-15 15:27:16 -0800 | [diff] [blame] | 283 | v := new(Map).Init() |
| 284 | Publish(name, v) |
| 285 | return v |
David Symonds | 2f28494 | 2009-05-04 15:14:22 -0700 | [diff] [blame] | 286 | } |
| 287 | |
| 288 | func NewString(name string) *String { |
Robert Griesemer | 1c72959 | 2009-12-15 15:27:16 -0800 | [diff] [blame] | 289 | v := new(String) |
| 290 | Publish(name, v) |
| 291 | return v |
David Symonds | 2f28494 | 2009-05-04 15:14:22 -0700 | [diff] [blame] | 292 | } |
| 293 | |
David Symonds | 715588f | 2012-02-04 14:32:05 +1100 | [diff] [blame] | 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() |
Brad Fitzpatrick | cfb9cf0 | 2014-01-20 09:59:23 -0800 | [diff] [blame] | 300 | for _, k := range varKeys { |
| 301 | f(KeyValue{k, vars[k]}) |
David Symonds | 2f28494 | 2009-05-04 15:14:22 -0700 | [diff] [blame] | 302 | } |
David Symonds | 2f28494 | 2009-05-04 15:14:22 -0700 | [diff] [blame] | 303 | } |
| 304 | |
Stephen Ma | fd9a5d2 | 2010-09-29 14:30:12 +1000 | [diff] [blame] | 305 | func expvarHandler(w http.ResponseWriter, r *http.Request) { |
Brad Fitzpatrick | 2c420ec | 2011-03-09 09:41:01 -0800 | [diff] [blame] | 306 | w.Header().Set("Content-Type", "application/json; charset=utf-8") |
Stephen Ma | fd9a5d2 | 2010-09-29 14:30:12 +1000 | [diff] [blame] | 307 | fmt.Fprintf(w, "{\n") |
Robert Griesemer | 1c72959 | 2009-12-15 15:27:16 -0800 | [diff] [blame] | 308 | first := true |
David Symonds | 715588f | 2012-02-04 14:32:05 +1100 | [diff] [blame] | 309 | Do(func(kv KeyValue) { |
David Symonds | 2f28494 | 2009-05-04 15:14:22 -0700 | [diff] [blame] | 310 | if !first { |
Stephen Ma | fd9a5d2 | 2010-09-29 14:30:12 +1000 | [diff] [blame] | 311 | fmt.Fprintf(w, ",\n") |
David Symonds | 5cb6843 | 2009-04-21 16:50:09 -0700 | [diff] [blame] | 312 | } |
Robert Griesemer | 1c72959 | 2009-12-15 15:27:16 -0800 | [diff] [blame] | 313 | first = false |
David Symonds | 715588f | 2012-02-04 14:32:05 +1100 | [diff] [blame] | 314 | fmt.Fprintf(w, "%q: %s", kv.Key, kv.Value) |
| 315 | }) |
Stephen Ma | fd9a5d2 | 2010-09-29 14:30:12 +1000 | [diff] [blame] | 316 | fmt.Fprintf(w, "\n}\n") |
David Symonds | 3cc702b | 2009-04-19 21:17:27 -0700 | [diff] [blame] | 317 | } |
| 318 | |
David Symonds | 57d0c26 | 2011-04-15 01:21:18 -0700 | [diff] [blame] | 319 | func cmdline() interface{} { |
| 320 | return os.Args |
Russ Cox | a709876 | 2010-03-29 13:06:26 -0700 | [diff] [blame] | 321 | } |
| 322 | |
David Symonds | 57d0c26 | 2011-04-15 01:21:18 -0700 | [diff] [blame] | 323 | func memstats() interface{} { |
Rémy Oudompheng | 842c906 | 2012-02-06 19:16:26 +0100 | [diff] [blame] | 324 | stats := new(runtime.MemStats) |
| 325 | runtime.ReadMemStats(stats) |
| 326 | return *stats |
Russ Cox | a709876 | 2010-03-29 13:06:26 -0700 | [diff] [blame] | 327 | } |
| 328 | |
| 329 | func init() { |
David Symonds | 715588f | 2012-02-04 14:32:05 +1100 | [diff] [blame] | 330 | http.HandleFunc("/debug/vars", expvarHandler) |
David Symonds | 57d0c26 | 2011-04-15 01:21:18 -0700 | [diff] [blame] | 331 | Publish("cmdline", Func(cmdline)) |
| 332 | Publish("memstats", Func(memstats)) |
Russ Cox | a709876 | 2010-03-29 13:06:26 -0700 | [diff] [blame] | 333 | } |