Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions prometheus/go_collector.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,13 @@ package prometheus

import (
"runtime"
"runtime/debug"
"time"
)

type goCollector struct {
goroutines Gauge
gcDesc *Desc
}

// NewGoCollector returns a collector which exports metrics about the current
Expand All @@ -16,16 +19,32 @@ func NewGoCollector() *goCollector {
Name: "process_goroutines",
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.
func (c *goCollector) Describe(ch chan<- *Desc) {
ch <- c.goroutines.Desc()
ch <- c.gcDesc
}

// Collect returns the current state of all metrics of the collector.
func (c *goCollector) Collect(ch chan<- Metric) {
c.goroutines.Set(float64(runtime.NumGoroutine()))
ch <- c.goroutines

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please remove the empty line after the function signature.

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:] {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But why skip the min? The Go runtime provides it, why not mirror it, too?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As what? The zero-th pecentile?
Something like quantiles[0.0] = stats.PauseQuantiles[0].Seconds() ?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, that would make most sense, IMHO. Objections welcome. :)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok, done.

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)
}
67 changes: 64 additions & 3 deletions prometheus/go_collector_test.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package prometheus

import (
"reflect"
"runtime"
"testing"
"time"

Expand Down Expand Up @@ -35,6 +35,9 @@ func TestGoCollector(t *testing.T) {
case Gauge:
pb := &dto.Metric{}
m.Write(pb)
if pb.GetGauge() == nil {
continue
}

if old == -1 {
old = int(pb.GetGauge().GetValue())
Expand All @@ -48,8 +51,66 @@ func TestGoCollector(t *testing.T) {
}

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):
t.Fatalf("expected collect timed out")
Expand Down