Browse Source

Update github.com/rcrowley/go-metrics

(Nov 19 2014 to Mar 12 2016; 465b4c348..eeba7bd0d)
Ask Bjørn Hansen 9 years ago
parent
commit
030d68e713

+ 1 - 1
Godeps/Godeps.json

@@ -24,7 +24,7 @@
 		},
 		},
 		{
 		{
 			"ImportPath": "github.com/rcrowley/go-metrics",
 			"ImportPath": "github.com/rcrowley/go-metrics",
-			"Rev": "1ce93efbc8f9c568886b2ef85ce305b2217b3de3"
+			"Rev": "eeba7bd0dd01ace6e690fa833b3f22aaec29af43"
 		},
 		},
 		{
 		{
 			"ImportPath": "github.com/stathat/go",
 			"ImportPath": "github.com/stathat/go",

+ 1 - 0
Godeps/_workspace/src/github.com/rcrowley/go-metrics/.travis.yml

@@ -4,6 +4,7 @@ go:
     - 1.2
     - 1.2
     - 1.3
     - 1.3
     - 1.4
     - 1.4
+    - 1.5
 
 
 script:
 script:
     - ./validate.sh
     - ./validate.sh

+ 14 - 1
Godeps/_workspace/src/github.com/rcrowley/go-metrics/README.md

@@ -39,7 +39,7 @@ t.Update(47)
 Periodically log every metric in human-readable form to standard error:
 Periodically log every metric in human-readable form to standard error:
 
 
 ```go
 ```go
-go metrics.Log(metrics.DefaultRegistry, 60e9, log.New(os.Stderr, "metrics: ", log.Lmicroseconds))
+go metrics.Log(metrics.DefaultRegistry, 5 * time.Second, log.New(os.Stderr, "metrics: ", log.Lmicroseconds))
 ```
 ```
 
 
 Periodically log every metric in slightly-more-parseable form to syslog:
 Periodically log every metric in slightly-more-parseable form to syslog:
@@ -103,6 +103,19 @@ import "github.com/rcrowley/go-metrics/stathat"
 go stathat.Stathat(metrics.DefaultRegistry, 10e9, "[email protected]")
 go stathat.Stathat(metrics.DefaultRegistry, 10e9, "[email protected]")
 ```
 ```
 
 
+Maintain all metrics along with expvars at `/debug/metrics`:
+
+This uses the same mechanism as [the official expvar](http://golang.org/pkg/expvar/)
+but exposed under `/debug/metrics`, which shows a json representation of all your usual expvars
+as well as all your go-metrics.
+
+
+```go
+import "github.com/rcrowley/go-metrics/exp"
+
+exp.Exp(metrics.DefaultRegistry)
+```
+
 Installation
 Installation
 ------------
 ------------
 
 

+ 148 - 0
Godeps/_workspace/src/github.com/rcrowley/go-metrics/exp/exp.go

@@ -0,0 +1,148 @@
+// Hook go-metrics into expvar
+// on any /debug/metrics request, load all vars from the registry into expvar, and execute regular expvar handler
+package exp
+
+import (
+	"expvar"
+	"fmt"
+	"github.com/abh/geodns/Godeps/_workspace/src/github.com/rcrowley/go-metrics"
+	"net/http"
+	"sync"
+)
+
+type exp struct {
+	expvarLock sync.Mutex // expvar panics if you try to register the same var twice, so we must probe it safely
+	registry   metrics.Registry
+}
+
+func (exp *exp) expHandler(w http.ResponseWriter, r *http.Request) {
+	// load our variables into expvar
+	exp.syncToExpvar()
+
+	// now just run the official expvar handler code (which is not publicly callable, so pasted inline)
+	w.Header().Set("Content-Type", "application/json; charset=utf-8")
+	fmt.Fprintf(w, "{\n")
+	first := true
+	expvar.Do(func(kv expvar.KeyValue) {
+		if !first {
+			fmt.Fprintf(w, ",\n")
+		}
+		first = false
+		fmt.Fprintf(w, "%q: %s", kv.Key, kv.Value)
+	})
+	fmt.Fprintf(w, "\n}\n")
+}
+
+func Exp(r metrics.Registry) {
+	e := exp{sync.Mutex{}, r}
+	// this would cause a panic:
+	// panic: http: multiple registrations for /debug/vars
+	// http.HandleFunc("/debug/vars", e.expHandler)
+	// haven't found an elegant way, so just use a different endpoint
+	http.HandleFunc("/debug/metrics", e.expHandler)
+}
+
+func (exp *exp) getInt(name string) *expvar.Int {
+	var v *expvar.Int
+	exp.expvarLock.Lock()
+	p := expvar.Get(name)
+	if p != nil {
+		v = p.(*expvar.Int)
+	} else {
+		v = new(expvar.Int)
+		expvar.Publish(name, v)
+	}
+	exp.expvarLock.Unlock()
+	return v
+}
+
+func (exp *exp) getFloat(name string) *expvar.Float {
+	var v *expvar.Float
+	exp.expvarLock.Lock()
+	p := expvar.Get(name)
+	if p != nil {
+		v = p.(*expvar.Float)
+	} else {
+		v = new(expvar.Float)
+		expvar.Publish(name, v)
+	}
+	exp.expvarLock.Unlock()
+	return v
+}
+
+func (exp *exp) publishCounter(name string, metric metrics.Counter) {
+	v := exp.getInt(name)
+	v.Set(metric.Count())
+}
+
+func (exp *exp) publishGauge(name string, metric metrics.Gauge) {
+	v := exp.getInt(name)
+	v.Set(metric.Value())
+}
+func (exp *exp) publishGaugeFloat64(name string, metric metrics.GaugeFloat64) {
+	exp.getFloat(name).Set(metric.Value())
+}
+
+func (exp *exp) publishHistogram(name string, metric metrics.Histogram) {
+	h := metric.Snapshot()
+	ps := h.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999})
+	exp.getInt(name + ".count").Set(h.Count())
+	exp.getFloat(name + ".min").Set(float64(h.Min()))
+	exp.getFloat(name + ".max").Set(float64(h.Max()))
+	exp.getFloat(name + ".mean").Set(float64(h.Mean()))
+	exp.getFloat(name + ".std-dev").Set(float64(h.StdDev()))
+	exp.getFloat(name + ".50-percentile").Set(float64(ps[0]))
+	exp.getFloat(name + ".75-percentile").Set(float64(ps[1]))
+	exp.getFloat(name + ".95-percentile").Set(float64(ps[2]))
+	exp.getFloat(name + ".99-percentile").Set(float64(ps[3]))
+	exp.getFloat(name + ".999-percentile").Set(float64(ps[4]))
+}
+
+func (exp *exp) publishMeter(name string, metric metrics.Meter) {
+	m := metric.Snapshot()
+	exp.getInt(name + ".count").Set(m.Count())
+	exp.getFloat(name + ".one-minute").Set(float64(m.Rate1()))
+	exp.getFloat(name + ".five-minute").Set(float64(m.Rate5()))
+	exp.getFloat(name + ".fifteen-minute").Set(float64((m.Rate15())))
+	exp.getFloat(name + ".mean").Set(float64(m.RateMean()))
+}
+
+func (exp *exp) publishTimer(name string, metric metrics.Timer) {
+	t := metric.Snapshot()
+	ps := t.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999})
+	exp.getInt(name + ".count").Set(t.Count())
+	exp.getFloat(name + ".min").Set(float64(t.Min()))
+	exp.getFloat(name + ".max").Set(float64(t.Max()))
+	exp.getFloat(name + ".mean").Set(float64(t.Mean()))
+	exp.getFloat(name + ".std-dev").Set(float64(t.StdDev()))
+	exp.getFloat(name + ".50-percentile").Set(float64(ps[0]))
+	exp.getFloat(name + ".75-percentile").Set(float64(ps[1]))
+	exp.getFloat(name + ".95-percentile").Set(float64(ps[2]))
+	exp.getFloat(name + ".99-percentile").Set(float64(ps[3]))
+	exp.getFloat(name + ".999-percentile").Set(float64(ps[4]))
+	exp.getFloat(name + ".one-minute").Set(float64(t.Rate1()))
+	exp.getFloat(name + ".five-minute").Set(float64(t.Rate5()))
+	exp.getFloat(name + ".fifteen-minute").Set(float64((t.Rate15())))
+	exp.getFloat(name + ".mean-rate").Set(float64(t.RateMean()))
+}
+
+func (exp *exp) syncToExpvar() {
+	exp.registry.Each(func(name string, i interface{}) {
+		switch i.(type) {
+		case metrics.Counter:
+			exp.publishCounter(name, i.(metrics.Counter))
+		case metrics.Gauge:
+			exp.publishGauge(name, i.(metrics.Gauge))
+		case metrics.GaugeFloat64:
+			exp.publishGaugeFloat64(name, i.(metrics.GaugeFloat64))
+		case metrics.Histogram:
+			exp.publishHistogram(name, i.(metrics.Histogram))
+		case metrics.Meter:
+			exp.publishMeter(name, i.(metrics.Meter))
+		case metrics.Timer:
+			exp.publishTimer(name, i.(metrics.Timer))
+		default:
+			panic(fmt.Sprintf("unsupported type for '%s': %T", name, i))
+		}
+	})
+}

+ 5 - 1
Godeps/_workspace/src/github.com/rcrowley/go-metrics/librato/librato.go

@@ -23,6 +23,7 @@ func translateTimerAttributes(d time.Duration) (attrs map[string]interface{}) {
 
 
 type Reporter struct {
 type Reporter struct {
 	Email, Token    string
 	Email, Token    string
+	Namespace       string
 	Source          string
 	Source          string
 	Interval        time.Duration
 	Interval        time.Duration
 	Registry        metrics.Registry
 	Registry        metrics.Registry
@@ -32,7 +33,7 @@ type Reporter struct {
 }
 }
 
 
 func NewReporter(r metrics.Registry, d time.Duration, e string, t string, s string, p []float64, u time.Duration) *Reporter {
 func NewReporter(r metrics.Registry, d time.Duration, e string, t string, s string, p []float64, u time.Duration) *Reporter {
-	return &Reporter{e, t, s, d, r, p, translateTimerAttributes(u), int64(d / time.Second)}
+	return &Reporter{e, t, "", s, d, r, p, translateTimerAttributes(u), int64(d / time.Second)}
 }
 }
 
 
 func Librato(r metrics.Registry, d time.Duration, e string, t string, s string, p []float64, u time.Duration) {
 func Librato(r metrics.Registry, d time.Duration, e string, t string, s string, p []float64, u time.Duration) {
@@ -88,6 +89,9 @@ func (self *Reporter) BuildRequest(now time.Time, r metrics.Registry) (snapshot
 	snapshot.Counters = make([]Measurement, 0)
 	snapshot.Counters = make([]Measurement, 0)
 	histogramGaugeCount := 1 + len(self.Percentiles)
 	histogramGaugeCount := 1 + len(self.Percentiles)
 	r.Each(func(name string, metric interface{}) {
 	r.Each(func(name string, metric interface{}) {
+		if self.Namespace != "" {
+			name = fmt.Sprintf("%s.%s", self.Namespace, name)
+		}
 		measurement := Measurement{}
 		measurement := Measurement{}
 		measurement[Period] = self.Interval.Seconds()
 		measurement[Period] = self.Interval.Seconds()
 		switch m := metric.(type) {
 		switch m := metric.(type) {

+ 19 - 12
Godeps/_workspace/src/github.com/rcrowley/go-metrics/log.go

@@ -5,10 +5,17 @@ import (
 	"time"
 	"time"
 )
 )
 
 
+func Log(r Registry, freq time.Duration, l *log.Logger) {
+	LogScaled(r, freq, time.Nanosecond, l)
+}
+
 // Output each metric in the given registry periodically using the given
 // Output each metric in the given registry periodically using the given
-// logger.
-func Log(r Registry, d time.Duration, l *log.Logger) {
-	for _ = range time.Tick(d) {
+// logger. Print timings in `scale` units (eg time.Millisecond) rather than nanos.
+func LogScaled(r Registry, freq time.Duration, scale time.Duration, l *log.Logger) {
+	du := float64(scale)
+	duSuffix := scale.String()[1:]
+
+	for _ = range time.Tick(freq) {
 		r.Each(func(name string, i interface{}) {
 		r.Each(func(name string, i interface{}) {
 			switch metric := i.(type) {
 			switch metric := i.(type) {
 			case Counter:
 			case Counter:
@@ -51,15 +58,15 @@ func Log(r Registry, d time.Duration, l *log.Logger) {
 				ps := t.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999})
 				ps := t.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999})
 				l.Printf("timer %s\n", name)
 				l.Printf("timer %s\n", name)
 				l.Printf("  count:       %9d\n", t.Count())
 				l.Printf("  count:       %9d\n", t.Count())
-				l.Printf("  min:         %9d\n", t.Min())
-				l.Printf("  max:         %9d\n", t.Max())
-				l.Printf("  mean:        %12.2f\n", t.Mean())
-				l.Printf("  stddev:      %12.2f\n", t.StdDev())
-				l.Printf("  median:      %12.2f\n", ps[0])
-				l.Printf("  75%%:         %12.2f\n", ps[1])
-				l.Printf("  95%%:         %12.2f\n", ps[2])
-				l.Printf("  99%%:         %12.2f\n", ps[3])
-				l.Printf("  99.9%%:       %12.2f\n", ps[4])
+				l.Printf("  min:         %12.2f%s\n", float64(t.Min())/du, duSuffix)
+				l.Printf("  max:         %12.2f%s\n", float64(t.Max())/du, duSuffix)
+				l.Printf("  mean:        %12.2f%s\n", t.Mean()/du, duSuffix)
+				l.Printf("  stddev:      %12.2f%s\n", t.StdDev()/du, duSuffix)
+				l.Printf("  median:      %12.2f%s\n", ps[0]/du, duSuffix)
+				l.Printf("  75%%:         %12.2f%s\n", ps[1]/du, duSuffix)
+				l.Printf("  95%%:         %12.2f%s\n", ps[2]/du, duSuffix)
+				l.Printf("  99%%:         %12.2f%s\n", ps[3]/du, duSuffix)
+				l.Printf("  99.9%%:       %12.2f%s\n", ps[4]/du, duSuffix)
 				l.Printf("  1-min rate:  %12.2f\n", t.Rate1())
 				l.Printf("  1-min rate:  %12.2f\n", t.Rate1())
 				l.Printf("  5-min rate:  %12.2f\n", t.Rate5())
 				l.Printf("  5-min rate:  %12.2f\n", t.Rate5())
 				l.Printf("  15-min rate: %12.2f\n", t.Rate15())
 				l.Printf("  15-min rate: %12.2f\n", t.Rate15())

+ 2 - 2
Godeps/_workspace/src/github.com/rcrowley/go-metrics/meter_test.go

@@ -23,14 +23,14 @@ func TestGetOrRegisterMeter(t *testing.T) {
 
 
 func TestMeterDecay(t *testing.T) {
 func TestMeterDecay(t *testing.T) {
 	ma := meterArbiter{
 	ma := meterArbiter{
-		ticker: time.NewTicker(1),
+		ticker: time.NewTicker(time.Millisecond),
 	}
 	}
 	m := newStandardMeter()
 	m := newStandardMeter()
 	ma.meters = append(ma.meters, m)
 	ma.meters = append(ma.meters, m)
 	go ma.tick()
 	go ma.tick()
 	m.Mark(1)
 	m.Mark(1)
 	rateMean := m.RateMean()
 	rateMean := m.RateMean()
-	time.Sleep(1)
+	time.Sleep(100 * time.Millisecond)
 	if m.RateMean() >= rateMean {
 	if m.RateMean() >= rateMean {
 		t.Error("m.RateMean() didn't decrease")
 		t.Error("m.RateMean() didn't decrease")
 	}
 	}

+ 7 - 0
Godeps/_workspace/src/github.com/rcrowley/go-metrics/registry.go

@@ -157,6 +157,13 @@ func NewPrefixedRegistry(prefix string) Registry {
 	}
 	}
 }
 }
 
 
+func NewPrefixedChildRegistry(parent Registry, prefix string) Registry {
+	return &PrefixedRegistry{
+		underlying: parent,
+		prefix:     prefix,
+	}
+}
+
 // Call the given function for each registered metric.
 // Call the given function for each registered metric.
 func (r *PrefixedRegistry) Each(fn func(string, interface{})) {
 func (r *PrefixedRegistry) Each(fn func(string, interface{})) {
 	r.underlying.Each(fn)
 	r.underlying.Each(fn)

+ 13 - 0
Godeps/_workspace/src/github.com/rcrowley/go-metrics/registry_test.go

@@ -117,6 +117,19 @@ func TestRegistryGetOrRegisterWithLazyInstantiation(t *testing.T) {
 	}
 	}
 }
 }
 
 
+func TestPrefixedChildRegistryGetOrRegister(t *testing.T) {
+	r := NewRegistry()
+	pr := NewPrefixedChildRegistry(r, "prefix.")
+
+	_ = pr.GetOrRegister("foo", NewCounter)
+
+	r.Each(func(name string, m interface{}) {
+		if name != "prefix.foo" {
+			t.Fatal(name)
+		}
+	})
+}
+
 func TestPrefixedRegistryGetOrRegister(t *testing.T) {
 func TestPrefixedRegistryGetOrRegister(t *testing.T) {
 	r := NewPrefixedRegistry("prefix.")
 	r := NewPrefixedRegistry("prefix.")
 
 

+ 38 - 26
Godeps/_workspace/src/github.com/rcrowley/go-metrics/runtime.go

@@ -2,6 +2,7 @@ package metrics
 
 
 import (
 import (
 	"runtime"
 	"runtime"
+	"runtime/pprof"
 	"time"
 	"time"
 )
 )
 
 
@@ -9,35 +10,37 @@ var (
 	memStats       runtime.MemStats
 	memStats       runtime.MemStats
 	runtimeMetrics struct {
 	runtimeMetrics struct {
 		MemStats struct {
 		MemStats struct {
-			Alloc        Gauge
-			BuckHashSys  Gauge
-			DebugGC      Gauge
-			EnableGC     Gauge
-			Frees        Gauge
-			HeapAlloc    Gauge
-			HeapIdle     Gauge
-			HeapInuse    Gauge
-			HeapObjects  Gauge
-			HeapReleased Gauge
-			HeapSys      Gauge
-			LastGC       Gauge
-			Lookups      Gauge
-			Mallocs      Gauge
-			MCacheInuse  Gauge
-			MCacheSys    Gauge
-			MSpanInuse   Gauge
-			MSpanSys     Gauge
-			NextGC       Gauge
-			NumGC        Gauge
-			PauseNs      Histogram
-			PauseTotalNs Gauge
-			StackInuse   Gauge
-			StackSys     Gauge
-			Sys          Gauge
-			TotalAlloc   Gauge
+			Alloc         Gauge
+			BuckHashSys   Gauge
+			DebugGC       Gauge
+			EnableGC      Gauge
+			Frees         Gauge
+			HeapAlloc     Gauge
+			HeapIdle      Gauge
+			HeapInuse     Gauge
+			HeapObjects   Gauge
+			HeapReleased  Gauge
+			HeapSys       Gauge
+			LastGC        Gauge
+			Lookups       Gauge
+			Mallocs       Gauge
+			MCacheInuse   Gauge
+			MCacheSys     Gauge
+			MSpanInuse    Gauge
+			MSpanSys      Gauge
+			NextGC        Gauge
+			NumGC         Gauge
+			GCCPUFraction GaugeFloat64
+			PauseNs       Histogram
+			PauseTotalNs  Gauge
+			StackInuse    Gauge
+			StackSys      Gauge
+			Sys           Gauge
+			TotalAlloc    Gauge
 		}
 		}
 		NumCgoCall   Gauge
 		NumCgoCall   Gauge
 		NumGoroutine Gauge
 		NumGoroutine Gauge
+		NumThread    Gauge
 		ReadMemStats Timer
 		ReadMemStats Timer
 	}
 	}
 	frees       uint64
 	frees       uint64
@@ -45,6 +48,8 @@ var (
 	mallocs     uint64
 	mallocs     uint64
 	numGC       uint32
 	numGC       uint32
 	numCgoCalls int64
 	numCgoCalls int64
+
+	threadCreateProfile = pprof.Lookup("threadcreate")
 )
 )
 
 
 // Capture new values for the Go runtime statistics exported in
 // Capture new values for the Go runtime statistics exported in
@@ -97,6 +102,7 @@ func CaptureRuntimeMemStatsOnce(r Registry) {
 	runtimeMetrics.MemStats.MSpanSys.Update(int64(memStats.MSpanSys))
 	runtimeMetrics.MemStats.MSpanSys.Update(int64(memStats.MSpanSys))
 	runtimeMetrics.MemStats.NextGC.Update(int64(memStats.NextGC))
 	runtimeMetrics.MemStats.NextGC.Update(int64(memStats.NextGC))
 	runtimeMetrics.MemStats.NumGC.Update(int64(memStats.NumGC - numGC))
 	runtimeMetrics.MemStats.NumGC.Update(int64(memStats.NumGC - numGC))
+	runtimeMetrics.MemStats.GCCPUFraction.Update(gcCPUFraction(&memStats))
 
 
 	// <https://code.google.com/p/go/source/browse/src/pkg/runtime/mgc0.c>
 	// <https://code.google.com/p/go/source/browse/src/pkg/runtime/mgc0.c>
 	i := numGC % uint32(len(memStats.PauseNs))
 	i := numGC % uint32(len(memStats.PauseNs))
@@ -132,6 +138,8 @@ func CaptureRuntimeMemStatsOnce(r Registry) {
 	numCgoCalls = currentNumCgoCalls
 	numCgoCalls = currentNumCgoCalls
 
 
 	runtimeMetrics.NumGoroutine.Update(int64(runtime.NumGoroutine()))
 	runtimeMetrics.NumGoroutine.Update(int64(runtime.NumGoroutine()))
+
+	runtimeMetrics.NumThread.Update(int64(threadCreateProfile.Count()))
 }
 }
 
 
 // Register runtimeMetrics for the Go runtime statistics exported in runtime and
 // Register runtimeMetrics for the Go runtime statistics exported in runtime and
@@ -158,6 +166,7 @@ func RegisterRuntimeMemStats(r Registry) {
 	runtimeMetrics.MemStats.MSpanSys = NewGauge()
 	runtimeMetrics.MemStats.MSpanSys = NewGauge()
 	runtimeMetrics.MemStats.NextGC = NewGauge()
 	runtimeMetrics.MemStats.NextGC = NewGauge()
 	runtimeMetrics.MemStats.NumGC = NewGauge()
 	runtimeMetrics.MemStats.NumGC = NewGauge()
+	runtimeMetrics.MemStats.GCCPUFraction = NewGaugeFloat64()
 	runtimeMetrics.MemStats.PauseNs = NewHistogram(NewExpDecaySample(1028, 0.015))
 	runtimeMetrics.MemStats.PauseNs = NewHistogram(NewExpDecaySample(1028, 0.015))
 	runtimeMetrics.MemStats.PauseTotalNs = NewGauge()
 	runtimeMetrics.MemStats.PauseTotalNs = NewGauge()
 	runtimeMetrics.MemStats.StackInuse = NewGauge()
 	runtimeMetrics.MemStats.StackInuse = NewGauge()
@@ -166,6 +175,7 @@ func RegisterRuntimeMemStats(r Registry) {
 	runtimeMetrics.MemStats.TotalAlloc = NewGauge()
 	runtimeMetrics.MemStats.TotalAlloc = NewGauge()
 	runtimeMetrics.NumCgoCall = NewGauge()
 	runtimeMetrics.NumCgoCall = NewGauge()
 	runtimeMetrics.NumGoroutine = NewGauge()
 	runtimeMetrics.NumGoroutine = NewGauge()
+	runtimeMetrics.NumThread = NewGauge()
 	runtimeMetrics.ReadMemStats = NewTimer()
 	runtimeMetrics.ReadMemStats = NewTimer()
 
 
 	r.Register("runtime.MemStats.Alloc", runtimeMetrics.MemStats.Alloc)
 	r.Register("runtime.MemStats.Alloc", runtimeMetrics.MemStats.Alloc)
@@ -188,6 +198,7 @@ func RegisterRuntimeMemStats(r Registry) {
 	r.Register("runtime.MemStats.MSpanSys", runtimeMetrics.MemStats.MSpanSys)
 	r.Register("runtime.MemStats.MSpanSys", runtimeMetrics.MemStats.MSpanSys)
 	r.Register("runtime.MemStats.NextGC", runtimeMetrics.MemStats.NextGC)
 	r.Register("runtime.MemStats.NextGC", runtimeMetrics.MemStats.NextGC)
 	r.Register("runtime.MemStats.NumGC", runtimeMetrics.MemStats.NumGC)
 	r.Register("runtime.MemStats.NumGC", runtimeMetrics.MemStats.NumGC)
+	r.Register("runtime.MemStats.GCCPUFraction", runtimeMetrics.MemStats.GCCPUFraction)
 	r.Register("runtime.MemStats.PauseNs", runtimeMetrics.MemStats.PauseNs)
 	r.Register("runtime.MemStats.PauseNs", runtimeMetrics.MemStats.PauseNs)
 	r.Register("runtime.MemStats.PauseTotalNs", runtimeMetrics.MemStats.PauseTotalNs)
 	r.Register("runtime.MemStats.PauseTotalNs", runtimeMetrics.MemStats.PauseTotalNs)
 	r.Register("runtime.MemStats.StackInuse", runtimeMetrics.MemStats.StackInuse)
 	r.Register("runtime.MemStats.StackInuse", runtimeMetrics.MemStats.StackInuse)
@@ -196,5 +207,6 @@ func RegisterRuntimeMemStats(r Registry) {
 	r.Register("runtime.MemStats.TotalAlloc", runtimeMetrics.MemStats.TotalAlloc)
 	r.Register("runtime.MemStats.TotalAlloc", runtimeMetrics.MemStats.TotalAlloc)
 	r.Register("runtime.NumCgoCall", runtimeMetrics.NumCgoCall)
 	r.Register("runtime.NumCgoCall", runtimeMetrics.NumCgoCall)
 	r.Register("runtime.NumGoroutine", runtimeMetrics.NumGoroutine)
 	r.Register("runtime.NumGoroutine", runtimeMetrics.NumGoroutine)
+	r.Register("runtime.NumThread", runtimeMetrics.NumThread)
 	r.Register("runtime.ReadMemStats", runtimeMetrics.ReadMemStats)
 	r.Register("runtime.ReadMemStats", runtimeMetrics.ReadMemStats)
 }
 }

+ 9 - 0
Godeps/_workspace/src/github.com/rcrowley/go-metrics/runtime_gccpufraction.go

@@ -0,0 +1,9 @@
+// +build go1.5
+
+package metrics
+
+import "runtime"
+
+func gcCPUFraction(memStats *runtime.MemStats) float64 {
+	return memStats.GCCPUFraction
+}

+ 9 - 0
Godeps/_workspace/src/github.com/rcrowley/go-metrics/runtime_no_gccpufraction.go

@@ -0,0 +1,9 @@
+// +build !go1.5
+
+package metrics
+
+import "runtime"
+
+func gcCPUFraction(memStats *runtime.MemStats) float64 {
+	return 0
+}

+ 10 - 0
Godeps/_workspace/src/github.com/rcrowley/go-metrics/runtime_test.go

@@ -47,6 +47,16 @@ func TestRuntimeMemStats(t *testing.T) {
 	}
 	}
 }
 }
 
 
+func TestRuntimeMemStatsNumThread(t *testing.T) {
+	r := NewRegistry()
+	RegisterRuntimeMemStats(r)
+	CaptureRuntimeMemStatsOnce(r)
+
+	if value := runtimeMetrics.NumThread.Value(); value < 1 {
+		t.Fatalf("got NumThread: %d, wanted at least 1", value)
+	}
+}
+
 func TestRuntimeMemStatsBlocking(t *testing.T) {
 func TestRuntimeMemStatsBlocking(t *testing.T) {
 	if g := runtime.GOMAXPROCS(0); g < 2 {
 	if g := runtime.GOMAXPROCS(0); g < 2 {
 		t.Skipf("skipping TestRuntimeMemStatsBlocking with GOMAXPROCS=%d\n", g)
 		t.Skipf("skipping TestRuntimeMemStatsBlocking with GOMAXPROCS=%d\n", g)