metrics, cmd/geth: informational metrics (prometheus, influxdb, opentsb) (#24877)

This chang creates a GaugeInfo metrics type for registering informational (textual) metrics, e.g. geth version number. It also improves the testing for backend-exporters, and uses a shared subpackage in 'internal' to provide sample datasets and ordered registry. 

Implements #21783

---------

Co-authored-by: Martin Holst Swende <martin@swende.se>
This commit is contained in:
Jorge
2023-08-31 13:37:17 -04:00
committed by GitHub
co-authored by Martin Holst Swende
parent 5b159498bb
commit 53f3c2ae65
26 changed files with 751 additions and 144 deletions
+7
View File
@@ -32,6 +32,13 @@ func readMeter(namespace, name string, i interface{}) (string, map[string]interf
"value": metric.Snapshot().Value(),
}
return measurement, fields
case metrics.GaugeInfo:
ms := metric.Snapshot()
measurement := fmt.Sprintf("%s%s.gauge", namespace, name)
fields := map[string]interface{}{
"value": ms.Value().String(),
}
return measurement, fields
case metrics.Histogram:
ms := metric.Snapshot()
if ms.Count() <= 0 {
+114
View File
@@ -0,0 +1,114 @@
// Copyright 2023 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package influxdb
import (
"fmt"
"io"
"net/http"
"net/http/httptest"
"net/url"
"os"
"strings"
"testing"
"github.com/ethereum/go-ethereum/metrics"
"github.com/ethereum/go-ethereum/metrics/internal"
influxdb2 "github.com/influxdata/influxdb-client-go/v2"
)
func TestMain(m *testing.M) {
metrics.Enabled = true
os.Exit(m.Run())
}
func TestExampleV1(t *testing.T) {
r := internal.ExampleMetrics()
var have, want string
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
haveB, _ := io.ReadAll(r.Body)
have = string(haveB)
r.Body.Close()
}))
defer ts.Close()
u, _ := url.Parse(ts.URL)
rep := &reporter{
reg: r,
url: *u,
namespace: "goth.",
}
if err := rep.makeClient(); err != nil {
t.Fatal(err)
}
if err := rep.send(978307200); err != nil {
t.Fatal(err)
}
if wantB, err := os.ReadFile("./testdata/influxdbv1.want"); err != nil {
t.Fatal(err)
} else {
want = string(wantB)
}
if have != want {
t.Errorf("\nhave:\n%v\nwant:\n%v\n", have, want)
t.Logf("have vs want:\n%v", findFirstDiffPos(have, want))
}
}
func TestExampleV2(t *testing.T) {
r := internal.ExampleMetrics()
var have, want string
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
haveB, _ := io.ReadAll(r.Body)
have = string(haveB)
r.Body.Close()
}))
defer ts.Close()
rep := &v2Reporter{
reg: r,
endpoint: ts.URL,
namespace: "goth.",
}
rep.client = influxdb2.NewClient(rep.endpoint, rep.token)
defer rep.client.Close()
rep.write = rep.client.WriteAPI(rep.organization, rep.bucket)
rep.send(978307200)
if wantB, err := os.ReadFile("./testdata/influxdbv2.want"); err != nil {
t.Fatal(err)
} else {
want = string(wantB)
}
if have != want {
t.Errorf("\nhave:\n%v\nwant:\n%v\n", have, want)
t.Logf("have vs want:\n %v", findFirstDiffPos(have, want))
}
}
func findFirstDiffPos(a, b string) string {
yy := strings.Split(b, "\n")
for i, x := range strings.Split(a, "\n") {
if i >= len(yy) {
return fmt.Sprintf("have:%d: %s\nwant:%d: <EOF>", i, x, i)
}
if y := yy[i]; x != y {
return fmt.Sprintf("have:%d: %s\nwant:%d: %s", i, x, i, y)
}
}
return ""
}
+11 -4
View File
@@ -79,7 +79,7 @@ func InfluxDBWithTagsOnce(r metrics.Registry, url, database, username, password,
return fmt.Errorf("unable to make InfluxDB client. err: %v", err)
}
if err := rep.send(); err != nil {
if err := rep.send(0); err != nil {
return fmt.Errorf("unable to send to InfluxDB. err: %v", err)
}
@@ -107,7 +107,7 @@ func (r *reporter) run() {
for {
select {
case <-intervalTicker.C:
if err := r.send(); err != nil {
if err := r.send(0); err != nil {
log.Warn("Unable to send to InfluxDB", "err", err)
}
case <-pingTicker.C:
@@ -123,7 +123,9 @@ func (r *reporter) run() {
}
}
func (r *reporter) send() error {
// send sends the measurements. If provided tstamp is >0, it is used. Otherwise,
// a 'fresh' timestamp is used.
func (r *reporter) send(tstamp int64) error {
bps, err := client.NewBatchPoints(
client.BatchPointsConfig{
Database: r.database,
@@ -132,7 +134,12 @@ func (r *reporter) send() error {
return err
}
r.reg.Each(func(name string, i interface{}) {
now := time.Now()
var now time.Time
if tstamp <= 0 {
now = time.Now()
} else {
now = time.Unix(tstamp, 0)
}
measurement, fields := readMeter(r.namespace, name, i)
if fields == nil {
return
+10 -3
View File
@@ -64,7 +64,7 @@ func (r *v2Reporter) run() {
for {
select {
case <-intervalTicker.C:
r.send()
r.send(0)
case <-pingTicker.C:
_, err := r.client.Health(context.Background())
if err != nil {
@@ -74,9 +74,16 @@ func (r *v2Reporter) run() {
}
}
func (r *v2Reporter) send() {
// send sends the measurements. If provided tstamp is >0, it is used. Otherwise,
// a 'fresh' timestamp is used.
func (r *v2Reporter) send(tstamp int64) {
r.reg.Each(func(name string, i interface{}) {
now := time.Now()
var now time.Time
if tstamp <= 0 {
now = time.Now()
} else {
now = time.Unix(tstamp, 0)
}
measurement, fields := readMeter(r.namespace, name, i)
if fields == nil {
return
+9
View File
@@ -0,0 +1,9 @@
goth.test/counter.count value=12345 978307200000000000
goth.test/counter_float64.count value=54321.98 978307200000000000
goth.test/gauge.gauge value=23456i 978307200000000000
goth.test/gauge_float64.gauge value=34567.89 978307200000000000
goth.test/gauge_info.gauge value="{\"arch\":\"amd64\",\"commit\":\"7caa2d8163ae3132c1c2d6978c76610caee2d949\",\"os\":\"linux\",\"protocol_versions\":\"64 65 66\",\"version\":\"1.10.18-unstable\"}" 978307200000000000
goth.test/histogram.histogram count=3i,max=3i,mean=2,min=1i,p25=1,p50=2,p75=3,p95=3,p99=3,p999=3,p9999=3,stddev=0.816496580927726,variance=0.6666666666666666 978307200000000000
goth.test/meter.meter count=0i,m1=0,m15=0,m5=0,mean=0 978307200000000000
goth.test/resetting_timer.span count=6i,max=120000000i,mean=30000000,min=10000000i,p50=12000000i,p95=120000000i,p99=120000000i 978307200000000000
goth.test/timer.timer count=6i,m1=0,m15=0,m5=0,max=120000000i,mean=38333333.333333336,meanrate=0,min=20000000i,p50=22500000,p75=48000000,p95=120000000,p99=120000000,p999=120000000,p9999=120000000,stddev=36545253.529775314,variance=1335555555555555.2 978307200000000000
+9
View File
@@ -0,0 +1,9 @@
goth.test/counter.count value=12345 978307200000000000
goth.test/counter_float64.count value=54321.98 978307200000000000
goth.test/gauge.gauge value=23456i 978307200000000000
goth.test/gauge_float64.gauge value=34567.89 978307200000000000
goth.test/gauge_info.gauge value="{\"arch\":\"amd64\",\"commit\":\"7caa2d8163ae3132c1c2d6978c76610caee2d949\",\"os\":\"linux\",\"protocol_versions\":\"64 65 66\",\"version\":\"1.10.18-unstable\"}" 978307200000000000
goth.test/histogram.histogram count=3i,max=3i,mean=2,min=1i,p25=1,p50=2,p75=3,p95=3,p99=3,p999=3,p9999=3,stddev=0.816496580927726,variance=0.6666666666666666 978307200000000000
goth.test/meter.meter count=0i,m1=0,m15=0,m5=0,mean=0 978307200000000000
goth.test/resetting_timer.span count=6i,max=120000000i,mean=30000000,min=10000000i,p50=12000000i,p95=120000000i,p99=120000000i 978307200000000000
goth.test/timer.timer count=6i,m1=0,m15=0,m5=0,max=120000000i,mean=38333333.333333336,meanrate=0,min=20000000i,p50=22500000,p75=48000000,p95=120000000,p99=120000000,p999=120000000,p9999=120000000,stddev=36545253.529775314,variance=1335555555555555.2 978307200000000000