Merge pull request #113 from oliver006/add_gc_metrics

Add garbage collection stats
This commit is contained in:
Björn Rabenstein 2015-05-05 22:14:12 +02:00
commit 86bd077f30
2 changed files with 83 additions and 3 deletions

View File

@ -2,10 +2,13 @@ package prometheus
import ( import (
"runtime" "runtime"
"runtime/debug"
"time"
) )
type goCollector struct { type goCollector struct {
goroutines Gauge goroutines Gauge
gcDesc *Desc
} }
// NewGoCollector returns a collector which exports metrics about the current // NewGoCollector returns a collector which exports metrics about the current
@ -16,16 +19,32 @@ func NewGoCollector() *goCollector {
Name: "process_goroutines", Name: "process_goroutines",
Help: "Number of goroutines that currently exist.", Help: "Number of goroutines that currently exist.",
}), }),
gcDesc: NewDesc(
"go_gc_duration_seconds",
"A summary of the GC invocation durations.",
nil, nil),
} }
} }
// Describe returns all descriptions of the collector. // Describe returns all descriptions of the collector.
func (c *goCollector) Describe(ch chan<- *Desc) { func (c *goCollector) Describe(ch chan<- *Desc) {
ch <- c.goroutines.Desc() ch <- c.goroutines.Desc()
ch <- c.gcDesc
} }
// Collect returns the current state of all metrics of the collector. // Collect returns the current state of all metrics of the collector.
func (c *goCollector) Collect(ch chan<- Metric) { func (c *goCollector) Collect(ch chan<- Metric) {
c.goroutines.Set(float64(runtime.NumGoroutine())) c.goroutines.Set(float64(runtime.NumGoroutine()))
ch <- c.goroutines ch <- c.goroutines
var stats debug.GCStats
stats.PauseQuantiles = make([]time.Duration, 5)
debug.ReadGCStats(&stats)
quantiles := make(map[float64]float64)
for idx, pq := range stats.PauseQuantiles[1:] {
quantiles[float64(idx+1)/float64(len(stats.PauseQuantiles)-1)] = pq.Seconds()
}
quantiles[0.0] = stats.PauseQuantiles[0].Seconds()
ch <- MustNewConstSummary(c.gcDesc, uint64(stats.NumGC), float64(stats.PauseTotal.Seconds()), quantiles)
} }

View File

@ -1,7 +1,7 @@
package prometheus package prometheus
import ( import (
"reflect" "runtime"
"testing" "testing"
"time" "time"
@ -35,6 +35,9 @@ func TestGoCollector(t *testing.T) {
case Gauge: case Gauge:
pb := &dto.Metric{} pb := &dto.Metric{}
m.Write(pb) m.Write(pb)
if pb.GetGauge() == nil {
continue
}
if old == -1 { if old == -1 {
old = int(pb.GetGauge().GetValue()) old = int(pb.GetGauge().GetValue())
@ -48,8 +51,66 @@ func TestGoCollector(t *testing.T) {
} }
return return
default: }
t.Errorf("want type Gauge, got %s", reflect.TypeOf(metric)) case <-time.After(1 * time.Second):
t.Fatalf("expected collect timed out")
}
}
}
func TestGCCollector(t *testing.T) {
var (
c = NewGoCollector()
ch = make(chan Metric)
waitc = make(chan struct{})
closec = make(chan struct{})
oldGC uint64
oldPause float64
)
defer close(closec)
go func() {
c.Collect(ch)
// force GC
runtime.GC()
<-waitc
c.Collect(ch)
}()
first := true
for {
select {
case metric := <-ch:
switch m := metric.(type) {
case *constSummary, *value:
pb := &dto.Metric{}
m.Write(pb)
if pb.GetSummary() == nil {
continue
}
if len(pb.GetSummary().Quantile) != 5 {
t.Errorf("expected 4 buckets, got %d", len(pb.GetSummary().Quantile))
}
for idx, want := range []float64{0.0, 0.25, 0.5, 0.75, 1.0} {
if *pb.GetSummary().Quantile[idx].Quantile != want {
t.Errorf("bucket #%d is off, got %f, want %f", idx, *pb.GetSummary().Quantile[idx].Quantile, want)
}
}
if first {
first = false
oldGC = *pb.GetSummary().SampleCount
oldPause = *pb.GetSummary().SampleSum
close(waitc)
continue
}
if diff := *pb.GetSummary().SampleCount - oldGC; diff != 1 {
t.Errorf("want 1 new garbage collection run, got %d", diff)
}
if diff := *pb.GetSummary().SampleSum - oldPause; diff <= 0 {
t.Errorf("want moar pause, got %f", diff)
}
return
} }
case <-time.After(1 * time.Second): case <-time.After(1 * time.Second):
t.Fatalf("expected collect timed out") t.Fatalf("expected collect timed out")