diff --git a/metrics/prometheus/collector.go b/metrics/prometheus/collector.go index 4295464373..c8785415bb 100644 --- a/metrics/prometheus/collector.go +++ b/metrics/prometheus/collector.go @@ -13,6 +13,7 @@ // // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see . + package prometheus import ( @@ -43,21 +44,26 @@ var ( nameQuantileTagTemplate = "{name=\"%s\",quantile=\"%s\"} %v\n" ) +// bufPool is a global pool of byte buffers to avoid constant reallocations. var bufPool sync.Pool +// getBuf retrieves a new empty (but possibly non-0 capacity) buffer. func getBuf() *bytes.Buffer { - buf := bufPool.Get() - if buf == nil { - return &bytes.Buffer{} + if item := bufPool.Get(); item != nil { + buf := item.(*bytes.Buffer) + buf.Reset() + return buf } - return buf.(*bytes.Buffer) + return &bytes.Buffer{} } +// giveBuf returns a used byte buffer to the pool. func giveBuf(buf *bytes.Buffer) { - buf.Reset() bufPool.Put(buf) } +// collector is a collection of byte buffers that aggregate Prometheus reports +// for different metric types. type collector struct { counters *bytes.Buffer gauges *bytes.Buffer @@ -67,6 +73,7 @@ type collector struct { resettingTimers *bytes.Buffer } +// newCollector createa a new Prometheus metric aggregator. func newCollector() *collector { return &collector{ counters: getBuf(), @@ -78,7 +85,9 @@ func newCollector() *collector { } } -func (c *collector) reset() { +// close releases all internally held byte buffers to be reused by subsequent +// metrics collection runs. +func (c *collector) close() { giveBuf(c.counters) giveBuf(c.gauges) giveBuf(c.histograms) @@ -87,38 +96,34 @@ func (c *collector) reset() { giveBuf(c.resettingTimers) } -func (c *collector) result() *bytes.Buffer { +// aggregate iterates over the different metric types and aggregates them into +// a single byte buffer. +func (c *collector) aggregate() *bytes.Buffer { buf := getBuf() if c.counters.Len() > 0 { buf.Write(countersHeader) buf.Write(c.counters.Bytes()) } - if c.gauges.Len() > 0 { buf.Write(gaugesHeader) buf.Write(c.gauges.Bytes()) } - if c.meters.Len() > 0 { buf.Write(meterHeader) buf.Write(c.meters.Bytes()) } - if c.histograms.Len() > 0 { buf.Write(histogramHeader) buf.Write(c.histograms.Bytes()) } - if c.timers.Len() > 0 { buf.Write(timerHeader) buf.Write(c.timers.Bytes()) } - if c.resettingTimers.Len() > 0 { buf.Write(resettingTimerHeader) buf.Write(c.resettingTimers.Bytes()) } - return buf } diff --git a/metrics/prometheus/handler.go b/metrics/prometheus/prometheus.go similarity index 67% rename from metrics/prometheus/handler.go rename to metrics/prometheus/prometheus.go index 389137da2a..847493263a 100644 --- a/metrics/prometheus/handler.go +++ b/metrics/prometheus/prometheus.go @@ -13,48 +13,47 @@ // // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see . + +// Package prometheus exposes go-metrics into a Prometheus format. package prometheus import ( "fmt" "net/http" + "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/metrics" ) -// Handler returns http handler which dump metrics in prometheus format +// Handler returns an HTTP handler which dump metrics in Prometheus format. func Handler(reg metrics.Registry) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + // Aggregate all the metris into a Prometheus collector c := newCollector() - defer c.reset() + defer c.close() reg.Each(func(name string, i interface{}) { switch m := i.(type) { case metrics.Counter: - ms := m.Snapshot() - c.addCounter(name, ms) + c.addCounter(name, m.Snapshot()) case metrics.Gauge: - ms := m.Snapshot() - c.addGuage(name, ms) + c.addGuage(name, m.Snapshot()) case metrics.GaugeFloat64: - ms := m.Snapshot() - c.addGuageFloat64(name, ms) + c.addGuageFloat64(name, m.Snapshot()) case metrics.Histogram: - ms := m.Snapshot() - c.addHistogram(name, ms) + c.addHistogram(name, m.Snapshot()) case metrics.Meter: - ms := m.Snapshot() - c.addMeter(name, ms) + c.addMeter(name, m.Snapshot()) case metrics.Timer: - ms := m.Snapshot() - c.addTimer(name, ms) + c.addTimer(name, m.Snapshot()) case metrics.ResettingTimer: - ms := m.Snapshot() - c.addResettingTimer(name, ms) + c.addResettingTimer(name, m.Snapshot()) + default: + log.Warn("Unknown Prometheus metric type", "type", fmt.Sprintf("%T", i)) } }) - - res := c.result() + // Aggregate the results into a single buffer and send to the user + res := c.aggregate() defer giveBuf(res) w.Header().Add("Content-Type", "text/plain")