From 1990c9e6216fd6b25fdf6447844f36405b75c18e Mon Sep 17 00:00:00 2001 From: Anton Evangelatov Date: Mon, 2 Jul 2018 14:51:02 +0200 Subject: [PATCH] cmd/geth: export metrics to InfluxDB (#16979) * cmd/geth: add flags for metrics export * cmd/geth: update usage fields for metrics flags * metrics/influxdb: update reporter logger to adhere to geth logging convention --- cmd/geth/main.go | 13 +++++++ cmd/geth/usage.go | 13 ++++++- cmd/utils/flags.go | 66 +++++++++++++++++++++++++++++++++--- metrics/influxdb/influxdb.go | 12 +++---- 4 files changed, 93 insertions(+), 11 deletions(-) diff --git a/cmd/geth/main.go b/cmd/geth/main.go index edf2a557a..f4a0ac5df 100644 --- a/cmd/geth/main.go +++ b/cmd/geth/main.go @@ -144,6 +144,15 @@ var ( utils.WhisperMaxMessageSizeFlag, utils.WhisperMinPOWFlag, } + + metricsFlags = []cli.Flag{ + utils.MetricsEnableInfluxDBFlag, + utils.MetricsInfluxDBEndpointFlag, + utils.MetricsInfluxDBDatabaseFlag, + utils.MetricsInfluxDBUsernameFlag, + utils.MetricsInfluxDBPasswordFlag, + utils.MetricsInfluxDBHostTagFlag, + } ) func init() { @@ -186,6 +195,7 @@ func init() { app.Flags = append(app.Flags, consoleFlags...) app.Flags = append(app.Flags, debug.Flags...) app.Flags = append(app.Flags, whisperFlags...) + app.Flags = append(app.Flags, metricsFlags...) app.Before = func(ctx *cli.Context) error { runtime.GOMAXPROCS(runtime.NumCPU()) @@ -208,6 +218,9 @@ func init() { log.Debug("Sanitizing Go's GC trigger", "percent", int(gogc)) godebug.SetGCPercent(int(gogc)) + // Start metrics export if enabled + utils.SetupMetrics(ctx) + // Start system runtime metrics collection go metrics.CollectProcessMetrics(3 * time.Second) diff --git a/cmd/geth/usage.go b/cmd/geth/usage.go index d934c6b02..06db64664 100644 --- a/cmd/geth/usage.go +++ b/cmd/geth/usage.go @@ -206,11 +206,22 @@ var AppHelpFlagGroups = []flagGroup{ { Name: "LOGGING AND DEBUGGING", Flags: append([]cli.Flag{ - utils.MetricsEnabledFlag, utils.FakePoWFlag, utils.NoCompactionFlag, }, debug.Flags...), }, + { + Name: "METRICS AND STATS", + Flags: []cli.Flag{ + utils.MetricsEnabledFlag, + utils.MetricsEnableInfluxDBFlag, + utils.MetricsInfluxDBEndpointFlag, + utils.MetricsInfluxDBDatabaseFlag, + utils.MetricsInfluxDBUsernameFlag, + utils.MetricsInfluxDBPasswordFlag, + utils.MetricsInfluxDBHostTagFlag, + }, + }, { Name: "WHISPER (EXPERIMENTAL)", Flags: whisperFlags, diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index 41a1ac35f..a191e4430 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -27,6 +27,7 @@ import ( "runtime" "strconv" "strings" + "time" "github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/accounts/keystore" @@ -48,6 +49,7 @@ import ( "github.com/ethereum/go-ethereum/les" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/metrics" + "github.com/ethereum/go-ethereum/metrics/influxdb" "github.com/ethereum/go-ethereum/node" "github.com/ethereum/go-ethereum/p2p" "github.com/ethereum/go-ethereum/p2p/discover" @@ -360,10 +362,6 @@ var ( Name: "ethstats", Usage: "Reporting URL of a ethstats service (nodename:secret@host:port)", } - MetricsEnabledFlag = cli.BoolFlag{ - Name: metrics.MetricsEnabledFlag, - Usage: "Enable metrics collection and reporting", - } FakePoWFlag = cli.BoolFlag{ Name: "fakepow", Usage: "Disables proof-of-work verification", @@ -532,6 +530,45 @@ var ( Usage: "Minimum POW accepted", Value: whisper.DefaultMinimumPoW, } + + // Metrics flags + MetricsEnabledFlag = cli.BoolFlag{ + Name: metrics.MetricsEnabledFlag, + Usage: "Enable metrics collection and reporting", + } + MetricsEnableInfluxDBFlag = cli.BoolFlag{ + Name: "metrics.influxdb", + Usage: "Enable metrics export/push to an external InfluxDB database", + } + MetricsInfluxDBEndpointFlag = cli.StringFlag{ + Name: "metrics.influxdb.endpoint", + Usage: "InfluxDB API endpoint to report metrics to", + Value: "http://localhost:8086", + } + MetricsInfluxDBDatabaseFlag = cli.StringFlag{ + Name: "metrics.influxdb.database", + Usage: "InfluxDB database name to push reported metrics to", + Value: "geth", + } + MetricsInfluxDBUsernameFlag = cli.StringFlag{ + Name: "metrics.influxdb.username", + Usage: "Username to authorize access to the database", + Value: "test", + } + MetricsInfluxDBPasswordFlag = cli.StringFlag{ + Name: "metrics.influxdb.password", + Usage: "Password to authorize access to the database", + Value: "test", + } + // The `host` tag is part of every measurement sent to InfluxDB. Queries on tags are faster in InfluxDB. + // It is used so that we can group all nodes and average a measurement across all of them, but also so + // that we can select a specific node and inspect its measurements. + // https://docs.influxdata.com/influxdb/v1.4/concepts/key_concepts/#tag-key + MetricsInfluxDBHostTagFlag = cli.StringFlag{ + Name: "metrics.influxdb.host.tag", + Usage: "InfluxDB `host` tag attached to all measurements", + Value: "localhost", + } ) // MakeDataDir retrieves the currently requested data directory, terminating @@ -1184,6 +1221,27 @@ func SetupNetwork(ctx *cli.Context) { params.TargetGasLimit = ctx.GlobalUint64(TargetGasLimitFlag.Name) } +func SetupMetrics(ctx *cli.Context) { + if metrics.Enabled { + log.Info("Enabling metrics collection") + var ( + enableExport = ctx.GlobalBool(MetricsEnableInfluxDBFlag.Name) + endpoint = ctx.GlobalString(MetricsInfluxDBEndpointFlag.Name) + database = ctx.GlobalString(MetricsInfluxDBDatabaseFlag.Name) + username = ctx.GlobalString(MetricsInfluxDBUsernameFlag.Name) + password = ctx.GlobalString(MetricsInfluxDBPasswordFlag.Name) + hosttag = ctx.GlobalString(MetricsInfluxDBHostTagFlag.Name) + ) + + if enableExport { + log.Info("Enabling metrics export to InfluxDB") + go influxdb.InfluxDBWithTags(metrics.DefaultRegistry, 10*time.Second, endpoint, database, username, password, "geth.", map[string]string{ + "host": hosttag, + }) + } + } +} + // MakeChainDatabase open an LevelDB using the flags passed to the client and will hard crash if it fails. func MakeChainDatabase(ctx *cli.Context, stack *node.Node) ethdb.Database { var ( diff --git a/metrics/influxdb/influxdb.go b/metrics/influxdb/influxdb.go index d5cb4da66..31a5c21b5 100644 --- a/metrics/influxdb/influxdb.go +++ b/metrics/influxdb/influxdb.go @@ -2,10 +2,10 @@ package influxdb import ( "fmt" - "log" uurl "net/url" "time" + "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/metrics" "github.com/influxdata/influxdb/client" ) @@ -35,7 +35,7 @@ func InfluxDB(r metrics.Registry, d time.Duration, url, database, username, pass func InfluxDBWithTags(r metrics.Registry, d time.Duration, url, database, username, password, namespace string, tags map[string]string) { u, err := uurl.Parse(url) if err != nil { - log.Printf("unable to parse InfluxDB url %s. err=%v", url, err) + log.Warn("Unable to parse InfluxDB", "url", url, "err", err) return } @@ -51,7 +51,7 @@ func InfluxDBWithTags(r metrics.Registry, d time.Duration, url, database, userna cache: make(map[string]int64), } if err := rep.makeClient(); err != nil { - log.Printf("unable to make InfluxDB client. err=%v", err) + log.Warn("Unable to make InfluxDB client", "err", err) return } @@ -76,15 +76,15 @@ func (r *reporter) run() { select { case <-intervalTicker: if err := r.send(); err != nil { - log.Printf("unable to send to InfluxDB. err=%v", err) + log.Warn("Unable to send to InfluxDB", "err", err) } case <-pingTicker: _, _, err := r.client.Ping() if err != nil { - log.Printf("got error while sending a ping to InfluxDB, trying to recreate client. err=%v", err) + log.Warn("Got error while sending a ping to InfluxDB, trying to recreate client", "err", err) if err = r.makeClient(); err != nil { - log.Printf("unable to make InfluxDB client. err=%v", err) + log.Warn("Unable to make InfluxDB client", "err", err) } } }