diff --git a/cmd/geth/chaincmd.go b/cmd/geth/chaincmd.go index 3de7b2bdc..247c202bc 100644 --- a/cmd/geth/chaincmd.go +++ b/cmd/geth/chaincmd.go @@ -85,6 +85,8 @@ The dumpgenesis command dumps the genesis block configuration in JSON format to utils.CacheGCFlag, utils.MetricsEnabledFlag, utils.MetricsEnabledExpensiveFlag, + utils.MetricsHTTPFlag, + utils.MetricsPortFlag, utils.MetricsEnableInfluxDBFlag, utils.MetricsInfluxDBEndpointFlag, utils.MetricsInfluxDBDatabaseFlag, diff --git a/cmd/geth/main.go b/cmd/geth/main.go index f1e8e2421..03ac7bee5 100644 --- a/cmd/geth/main.go +++ b/cmd/geth/main.go @@ -199,6 +199,8 @@ var ( metricsFlags = []cli.Flag{ utils.MetricsEnabledFlag, utils.MetricsEnabledExpensiveFlag, + utils.MetricsHTTPFlag, + utils.MetricsPortFlag, utils.MetricsEnableInfluxDBFlag, utils.MetricsInfluxDBEndpointFlag, utils.MetricsInfluxDBDatabaseFlag, diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index 61595e367..afcf6afbd 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -51,6 +51,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/exp" "github.com/ethereum/go-ethereum/metrics/influxdb" "github.com/ethereum/go-ethereum/miner" "github.com/ethereum/go-ethereum/node" @@ -689,6 +690,21 @@ var ( Name: "metrics.expensive", Usage: "Enable expensive metrics collection and reporting", } + + // MetricsHTTPFlag defines the endpoint for a stand-alone metrics HTTP endpoint. + // Since the pprof service enables sensitive/vulnerable behavior, this allows a user + // to enable a public-OK metrics endpoint without having to worry about ALSO exposing + // other profiling behavior or information. + MetricsHTTPFlag = cli.StringFlag{ + Name: "metrics.addr", + Usage: "Enable stand-alone metrics HTTP server listening interface", + Value: "127.0.0.1", + } + MetricsPortFlag = cli.IntFlag{ + Name: "metrics.port", + Usage: "Metrics HTTP server listening port", + Value: 6060, + } MetricsEnableInfluxDBFlag = cli.BoolFlag{ Name: "metrics.influxdb", Usage: "Enable metrics export/push to an external InfluxDB database", @@ -1734,6 +1750,7 @@ func RegisterGraphQLService(stack *node.Node, endpoint string, cors, vhosts []st func SetupMetrics(ctx *cli.Context) { if metrics.Enabled { log.Info("Enabling metrics collection") + var ( enableExport = ctx.GlobalBool(MetricsEnableInfluxDBFlag.Name) endpoint = ctx.GlobalString(MetricsInfluxDBEndpointFlag.Name) @@ -1749,6 +1766,12 @@ func SetupMetrics(ctx *cli.Context) { go influxdb.InfluxDBWithTags(metrics.DefaultRegistry, 10*time.Second, endpoint, database, username, password, "geth.", tagsMap) } + + if ctx.GlobalIsSet(MetricsHTTPFlag.Name) { + address := fmt.Sprintf("%s:%d", ctx.GlobalString(MetricsHTTPFlag.Name), ctx.GlobalInt(MetricsPortFlag.Name)) + log.Info("Enabling stand-alone metrics HTTP endpoint", "address", address) + exp.Setup(address) + } } } diff --git a/internal/debug/flags.go b/internal/debug/flags.go index 70c31cf0e..3b077b6e0 100644 --- a/internal/debug/flags.go +++ b/internal/debug/flags.go @@ -195,15 +195,19 @@ func Setup(ctx *cli.Context) error { } address := fmt.Sprintf("%s:%d", listenHost, port) - StartPProf(address) + // This context value ("metrics.addr") represents the utils.MetricsHTTPFlag.Name. + // It cannot be imported because it will cause a cyclical dependency. + StartPProf(address, !ctx.GlobalIsSet("metrics.addr")) } return nil } -func StartPProf(address string) { +func StartPProf(address string, withMetrics bool) { // Hook go-metrics into expvar on any /debug/metrics request, load all vars // from the registry into expvar, and execute regular expvar handler. - exp.Exp(metrics.DefaultRegistry) + if withMetrics { + exp.Exp(metrics.DefaultRegistry) + } http.Handle("/memsize/", http.StripPrefix("/memsize", &Memsize)) log.Info("Starting pprof server", "addr", fmt.Sprintf("http://%s/debug/pprof", address)) go func() { diff --git a/metrics/exp/exp.go b/metrics/exp/exp.go index 55820f1aa..f510b8381 100644 --- a/metrics/exp/exp.go +++ b/metrics/exp/exp.go @@ -8,6 +8,7 @@ import ( "net/http" "sync" + "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/metrics" "github.com/ethereum/go-ethereum/metrics/prometheus" ) @@ -52,6 +53,20 @@ func ExpHandler(r metrics.Registry) http.Handler { return http.HandlerFunc(e.expHandler) } +// Setup starts a dedicated metrics server at the given address. +// This function enables metrics reporting separate from pprof. +func Setup(address string) { + m := http.NewServeMux() + m.Handle("/debug/metrics", ExpHandler(metrics.DefaultRegistry)) + m.Handle("/debug/metrics/prometheus", prometheus.Handler(metrics.DefaultRegistry)) + log.Info("Starting metrics server", "addr", fmt.Sprintf("http://%s/debug/metrics", address)) + go func() { + if err := http.ListenAndServe(address, m); err != nil { + log.Error("Failure in running metrics server", "err", err) + } + }() +} + func (exp *exp) getInt(name string) *expvar.Int { var v *expvar.Int exp.expvarLock.Lock() diff --git a/mobile/geth.go b/mobile/geth.go index 27f1c4ed9..f20243426 100644 --- a/mobile/geth.go +++ b/mobile/geth.go @@ -113,7 +113,7 @@ func NewNode(datadir string, config *NodeConfig) (stack *Node, _ error) { } if config.PprofAddress != "" { - debug.StartPProf(config.PprofAddress) + debug.StartPProf(config.PprofAddress, true) } // Create the empty networking stack