Skip to content

Commit 6e35550

Browse files
Miya Chentailingchen
authored andcommitted
cmd, metrics: add prometheus flag to export metrics
1 parent 5ff207a commit 6e35550

File tree

3 files changed

+181
-0
lines changed

3 files changed

+181
-0
lines changed

cmd/geth/main.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,7 @@ var (
116116
utils.IstanbulRequestTimeoutFlag,
117117
utils.IstanbulBlockPeriodFlag,
118118
utils.IstanbulBlockPauseTimeFlag,
119+
utils.PrometheusAddrFlag,
119120
}
120121

121122
rpcFlags = []cli.Flag{

cmd/utils/flags.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -507,6 +507,13 @@ var (
507507
Usage: "Pause time when zero tx in previous block, values should be larger than istanbul.blockperiod",
508508
Value: eth.DefaultConfig.Istanbul.BlockPauseTime,
509509
}
510+
511+
// Prometheus settings
512+
PrometheusAddrFlag = cli.StringFlag{
513+
Name: metrics.PrometheusAddrFlag,
514+
Usage: "prometheus push-gateway address",
515+
Value: "",
516+
}
510517
)
511518

512519
// MakeDataDir retrieves the currently requested data directory, terminating

metrics/prometheus.go

Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
// Copyright 2015 The go-ethereum Authors
2+
// This file is part of the go-ethereum library.
3+
//
4+
// The go-ethereum library is free software: you can redistribute it and/or modify
5+
// it under the terms of the GNU Lesser General Public License as published by
6+
// the Free Software Foundation, either version 3 of the License, or
7+
// (at your option) any later version.
8+
//
9+
// The go-ethereum library is distributed in the hope that it will be useful,
10+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
// GNU Lesser General Public License for more details.
13+
//
14+
// You should have received a copy of the GNU Lesser General Public License
15+
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
16+
17+
package metrics
18+
19+
import (
20+
"errors"
21+
"fmt"
22+
"os"
23+
"strings"
24+
"time"
25+
26+
"github.com/ethereum/go-ethereum/log"
27+
"github.com/prometheus/client_golang/prometheus"
28+
"github.com/prometheus/client_golang/prometheus/push"
29+
"github.com/rcrowley/go-metrics"
30+
)
31+
32+
var (
33+
// format should be --promaddr 127.0.0.1:9091
34+
PrometheusAddrFlag = "promaddr"
35+
36+
prometheusAddr = ""
37+
// enabled is the flag specifying if metrics are enable or not.
38+
prometheusEnabled = false
39+
)
40+
41+
// Init enables or disables the metrics system. Since we need this to run before
42+
// any other code gets to create meters and timers, we'll actually do an ugly hack
43+
// and peek into the command line args for the metrics flag.
44+
func init() {
45+
for i, arg := range os.Args {
46+
if strings.TrimLeft(arg, "-") == PrometheusAddrFlag {
47+
prometheusAddr = os.Args[i+1]
48+
prometheusEnabled = true
49+
log.Info("Enabling prometheus exporter", "addr", prometheusAddr)
50+
}
51+
}
52+
if prometheusEnabled {
53+
r := newPrometheusRegister(prometheusAddr, "geth", getSystemInfo(), metrics.DefaultRegistry)
54+
go r.export()
55+
}
56+
}
57+
58+
type prometheusRegister struct {
59+
namespace string
60+
url string
61+
metricRegistry metrics.Registry
62+
registry *prometheus.Registry
63+
labels map[string]string
64+
gauges map[string]prometheus.Gauge
65+
counters map[string]prometheus.Counter
66+
exportDuration time.Duration
67+
}
68+
69+
func newPrometheusRegister(url, namespace string, labels map[string]string, metricRegistry metrics.Registry) *prometheusRegister {
70+
return &prometheusRegister{
71+
namespace: namespace,
72+
url: url,
73+
metricRegistry: metricRegistry,
74+
registry: prometheus.NewRegistry(),
75+
labels: labels,
76+
gauges: make(map[string]prometheus.Gauge),
77+
counters: make(map[string]prometheus.Counter),
78+
exportDuration: 10 * time.Second,
79+
}
80+
}
81+
82+
func (r *prometheusRegister) GetOrRegisterCounter(name string) prometheus.Counter {
83+
cnt, ok := r.counters[name]
84+
if !ok {
85+
cnt = prometheus.NewCounter(prometheus.CounterOpts{
86+
Namespace: flattenKey(r.namespace),
87+
Subsystem: "",
88+
Name: flattenKey(name),
89+
Help: name,
90+
})
91+
r.registry.MustRegister(cnt)
92+
r.counters[name] = cnt
93+
}
94+
return cnt
95+
}
96+
97+
func (r *prometheusRegister) GetOrRegisterGauge(name string) prometheus.Gauge {
98+
gauge, ok := r.gauges[name]
99+
if !ok {
100+
gauge = prometheus.NewGauge(prometheus.GaugeOpts{
101+
Namespace: flattenKey(r.namespace),
102+
Subsystem: "",
103+
Name: flattenKey(name),
104+
Help: name,
105+
})
106+
r.registry.MustRegister(gauge)
107+
r.gauges[name] = gauge
108+
}
109+
return gauge
110+
}
111+
112+
func (r *prometheusRegister) export() {
113+
for _ = range time.Tick(r.exportDuration) {
114+
r.UpdatePrometheusMetricsOnce()
115+
push.FromGatherer(r.namespace, r.labels, r.url, r.registry)
116+
}
117+
}
118+
119+
func (r *prometheusRegister) gaugeFromNameAndValue(name string, val float64) {
120+
g := r.GetOrRegisterGauge(name)
121+
g.Set(val)
122+
}
123+
124+
func (r *prometheusRegister) UpdatePrometheusMetricsOnce() error {
125+
if !Enabled {
126+
return errors.New("metric is not enabled.")
127+
}
128+
r.metricRegistry.Each(func(name string, i interface{}) {
129+
switch metric := i.(type) {
130+
case metrics.Counter:
131+
r.gaugeFromNameAndValue(name, float64(metric.Count()))
132+
case metrics.Gauge:
133+
case metrics.GaugeFloat64:
134+
r.gaugeFromNameAndValue(name, float64(metric.Value()))
135+
case metrics.Meter:
136+
snap := metric.Snapshot()
137+
r.gaugeFromNameAndValue(name, snap.Rate1())
138+
name5 := fmt.Sprintf("%s.rate5", name)
139+
r.gaugeFromNameAndValue(name5, snap.Rate5())
140+
name15 := fmt.Sprintf("%s.rate15", name)
141+
r.gaugeFromNameAndValue(name15, snap.Rate15())
142+
nameMean := fmt.Sprintf("%s.ratemean", name)
143+
r.gaugeFromNameAndValue(nameMean, snap.RateMean())
144+
case metrics.Timer:
145+
snap := metric.Snapshot()
146+
r.gaugeFromNameAndValue(name, snap.Mean())
147+
name50 := fmt.Sprintf("%s.p50", name)
148+
r.gaugeFromNameAndValue(name50, snap.Percentile(float64(0.5)))
149+
name90 := fmt.Sprintf("%s.p90", name)
150+
r.gaugeFromNameAndValue(name90, snap.Percentile(float64(0.9)))
151+
name95 := fmt.Sprintf("%s.p95", name)
152+
r.gaugeFromNameAndValue(name95, snap.Percentile(float64(0.95)))
153+
}
154+
})
155+
return nil
156+
}
157+
158+
func flattenKey(key string) string {
159+
key = strings.Replace(key, " ", "_", -1)
160+
key = strings.Replace(key, ".", "_", -1)
161+
key = strings.Replace(key, "-", "_", -1)
162+
key = strings.Replace(key, "=", "_", -1)
163+
key = strings.Replace(key, "/", "_", -1)
164+
return key
165+
}
166+
167+
func getSystemInfo() map[string]string {
168+
labels := map[string]string{}
169+
if host, err := os.Hostname(); err == nil {
170+
labels["instance"] = host
171+
}
172+
return labels
173+
}

0 commit comments

Comments
 (0)