From bde2ff034317db977354e0855e6406f9428899ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A9ter=20Szil=C3=A1gyi?= Date: Tue, 23 Jun 2015 19:12:48 +0300 Subject: cmd/geth, rpc/api: move the metrics into the new console --- rpc/api/debug.go | 64 +++++++++++++++++++++++++++++++++++++++++++++++++++++ rpc/api/debug_js.go | 5 +++++ 2 files changed, 69 insertions(+) (limited to 'rpc/api') diff --git a/rpc/api/debug.go b/rpc/api/debug.go index b451d8662..871786c6f 100644 --- a/rpc/api/debug.go +++ b/rpc/api/debug.go @@ -2,6 +2,8 @@ package api import ( "fmt" + "strings" + "time" "github.com/ethereum/ethash" "github.com/ethereum/go-ethereum/core/state" @@ -11,6 +13,7 @@ import ( "github.com/ethereum/go-ethereum/rpc/codec" "github.com/ethereum/go-ethereum/rpc/shared" "github.com/ethereum/go-ethereum/xeth" + "github.com/rcrowley/go-metrics" ) const ( @@ -26,6 +29,7 @@ var ( "debug_processBlock": (*debugApi).ProcessBlock, "debug_seedHash": (*debugApi).SeedHash, "debug_setHead": (*debugApi).SetHead, + "debug_metrics": (*debugApi).Metrics, } ) @@ -171,3 +175,63 @@ func (self *debugApi) SeedHash(req *shared.Request) (interface{}, error) { return nil, err } } + +func (self *debugApi) Metrics(req *shared.Request) (interface{}, error) { + // Create a rate formatter + units := []string{"", "K", "M", "G", "T", "E", "P"} + round := func(value float64, prec int) string { + unit := 0 + for value >= 1000 { + unit, value, prec = unit+1, value/1000, 2 + } + return fmt.Sprintf(fmt.Sprintf("%%.%df%s", prec, units[unit]), value) + } + format := func(total float64, rate float64) string { + return fmt.Sprintf("%s (%s/s)", round(total, 0), round(rate, 2)) + } + // Iterate over all the metrics, and just dump for now + counters := make(map[string]interface{}) + metrics.DefaultRegistry.Each(func(name string, metric interface{}) { + // Create or retrieve the counter hierarchy for this metric + root, parts := counters, strings.Split(name, "/") + for _, part := range parts[:len(parts)-1] { + if _, ok := root[part]; !ok { + root[part] = make(map[string]interface{}) + } + root = root[part].(map[string]interface{}) + } + name = parts[len(parts)-1] + + // Fill the counter with the metric details + switch metric := metric.(type) { + case metrics.Meter: + root[name] = map[string]interface{}{ + "Avg01Min": format(metric.Rate1()*60, metric.Rate1()), + "Avg05Min": format(metric.Rate5()*300, metric.Rate5()), + "Avg15Min": format(metric.Rate15()*900, metric.Rate15()), + "Total": format(float64(metric.Count()), metric.RateMean()), + } + + case metrics.Timer: + root[name] = map[string]interface{}{ + "Avg01Min": format(metric.Rate1()*60, metric.Rate1()), + "Avg05Min": format(metric.Rate5()*300, metric.Rate5()), + "Avg15Min": format(metric.Rate15()*900, metric.Rate15()), + "Count": format(float64(metric.Count()), metric.RateMean()), + "Maximum": time.Duration(metric.Max()).String(), + "Minimum": time.Duration(metric.Min()).String(), + "Percentile": map[string]interface{}{ + "20": time.Duration(metric.Percentile(0.2)).String(), + "50": time.Duration(metric.Percentile(0.5)).String(), + "80": time.Duration(metric.Percentile(0.8)).String(), + "95": time.Duration(metric.Percentile(0.95)).String(), + "99": time.Duration(metric.Percentile(0.99)).String(), + }, + } + + default: + root[name] = "Unknown metric type" + } + }) + return counters, nil +} diff --git a/rpc/api/debug_js.go b/rpc/api/debug_js.go index 35fecb75f..e48e4df06 100644 --- a/rpc/api/debug_js.go +++ b/rpc/api/debug_js.go @@ -50,6 +50,11 @@ web3._extend({ ], properties: [ + new web3._extend.Property({ + name: 'metrics', + getter: 'debug_metrics', + outputFormatter: function(obj) { return obj; } + }) ] }); ` -- cgit v1.2.3 From e5b820c47b9343d3801e1ebbeb4a8f40843ea87c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A9ter=20Szil=C3=A1gyi?= Date: Wed, 24 Jun 2015 14:38:58 +0300 Subject: cmd/geth, rpc/api: extend metrics API, add a basic monitor command --- rpc/api/debug.go | 92 ++++++++++++++++++++++++++++++++++++--------------- rpc/api/debug_args.go | 24 ++++++++++++++ rpc/api/debug_js.go | 12 ++++--- 3 files changed, 96 insertions(+), 32 deletions(-) (limited to 'rpc/api') diff --git a/rpc/api/debug.go b/rpc/api/debug.go index 871786c6f..45c99f295 100644 --- a/rpc/api/debug.go +++ b/rpc/api/debug.go @@ -177,6 +177,10 @@ func (self *debugApi) SeedHash(req *shared.Request) (interface{}, error) { } func (self *debugApi) Metrics(req *shared.Request) (interface{}, error) { + args := new(MetricsArgs) + if err := self.codec.Decode(req.Params, &args); err != nil { + return nil, shared.NewDecodeParamError(err.Error()) + } // Create a rate formatter units := []string{"", "K", "M", "G", "T", "E", "P"} round := func(value float64, prec int) string { @@ -202,35 +206,69 @@ func (self *debugApi) Metrics(req *shared.Request) (interface{}, error) { } name = parts[len(parts)-1] - // Fill the counter with the metric details - switch metric := metric.(type) { - case metrics.Meter: - root[name] = map[string]interface{}{ - "Avg01Min": format(metric.Rate1()*60, metric.Rate1()), - "Avg05Min": format(metric.Rate5()*300, metric.Rate5()), - "Avg15Min": format(metric.Rate15()*900, metric.Rate15()), - "Total": format(float64(metric.Count()), metric.RateMean()), + // Fill the counter with the metric details, formatting if requested + if args.Raw { + switch metric := metric.(type) { + case metrics.Meter: + root[name] = map[string]interface{}{ + "Avg01Min": metric.Rate1(), + "Avg05Min": metric.Rate5(), + "Avg15Min": metric.Rate15(), + "AvgTotal": metric.RateMean(), + "Total": float64(metric.Count()), + } + + case metrics.Timer: + root[name] = map[string]interface{}{ + "Avg01Min": metric.Rate1(), + "Avg05Min": metric.Rate5(), + "Avg15Min": metric.Rate15(), + "AvgTotal": metric.RateMean(), + "Total": float64(metric.Count()), + "Maximum": metric.Max(), + "Minimum": metric.Min(), + "Percentile": map[string]interface{}{ + "20": metric.Percentile(0.2), + "50": metric.Percentile(0.5), + "80": metric.Percentile(0.8), + "95": metric.Percentile(0.95), + "99": metric.Percentile(0.99), + }, + } + + default: + root[name] = "Unknown metric type" } - - case metrics.Timer: - root[name] = map[string]interface{}{ - "Avg01Min": format(metric.Rate1()*60, metric.Rate1()), - "Avg05Min": format(metric.Rate5()*300, metric.Rate5()), - "Avg15Min": format(metric.Rate15()*900, metric.Rate15()), - "Count": format(float64(metric.Count()), metric.RateMean()), - "Maximum": time.Duration(metric.Max()).String(), - "Minimum": time.Duration(metric.Min()).String(), - "Percentile": map[string]interface{}{ - "20": time.Duration(metric.Percentile(0.2)).String(), - "50": time.Duration(metric.Percentile(0.5)).String(), - "80": time.Duration(metric.Percentile(0.8)).String(), - "95": time.Duration(metric.Percentile(0.95)).String(), - "99": time.Duration(metric.Percentile(0.99)).String(), - }, + } else { + switch metric := metric.(type) { + case metrics.Meter: + root[name] = map[string]interface{}{ + "Avg01Min": format(metric.Rate1()*60, metric.Rate1()), + "Avg05Min": format(metric.Rate5()*300, metric.Rate5()), + "Avg15Min": format(metric.Rate15()*900, metric.Rate15()), + "Total": format(float64(metric.Count()), metric.RateMean()), + } + + case metrics.Timer: + root[name] = map[string]interface{}{ + "Avg01Min": format(metric.Rate1()*60, metric.Rate1()), + "Avg05Min": format(metric.Rate5()*300, metric.Rate5()), + "Avg15Min": format(metric.Rate15()*900, metric.Rate15()), + "Count": format(float64(metric.Count()), metric.RateMean()), + "Maximum": time.Duration(metric.Max()).String(), + "Minimum": time.Duration(metric.Min()).String(), + "Percentile": map[string]interface{}{ + "20": time.Duration(metric.Percentile(0.2)).String(), + "50": time.Duration(metric.Percentile(0.5)).String(), + "80": time.Duration(metric.Percentile(0.8)).String(), + "95": time.Duration(metric.Percentile(0.95)).String(), + "99": time.Duration(metric.Percentile(0.99)).String(), + }, + } + + default: + root[name] = "Unknown metric type" } - - default: - root[name] = "Unknown metric type" } }) return counters, nil diff --git a/rpc/api/debug_args.go b/rpc/api/debug_args.go index b9b5aa27e..b72fb03ae 100644 --- a/rpc/api/debug_args.go +++ b/rpc/api/debug_args.go @@ -4,6 +4,7 @@ import ( "encoding/json" "fmt" "math/big" + "reflect" "github.com/ethereum/go-ethereum/rpc/shared" ) @@ -45,3 +46,26 @@ func (args *WaitForBlockArgs) UnmarshalJSON(b []byte) (err error) { return nil } + +type MetricsArgs struct { + Raw bool +} + +func (args *MetricsArgs) UnmarshalJSON(b []byte) (err error) { + var obj []interface{} + if err := json.Unmarshal(b, &obj); err != nil { + return shared.NewDecodeParamError(err.Error()) + } + if len(obj) > 1 { + return fmt.Errorf("metricsArgs needs 0, 1 arguments") + } + // default values when not provided + if len(obj) >= 1 && obj[0] != nil { + if value, ok := obj[0].(bool); !ok { + return fmt.Errorf("invalid argument %v", reflect.TypeOf(obj[0])) + } else { + args.Raw = value + } + } + return nil +} diff --git a/rpc/api/debug_js.go b/rpc/api/debug_js.go index e48e4df06..bd3a6dfb2 100644 --- a/rpc/api/debug_js.go +++ b/rpc/api/debug_js.go @@ -46,15 +46,17 @@ web3._extend({ params: 1, inputFormatter: [web3._extend.formatters.formatInputInt], outputFormatter: function(obj) { return obj; } + }), + new web3._extend.Method({ + name: 'metrics', + call: 'debug_metrics', + params: 1, + inputFormatter: [web3._extend.formatters.formatInputBool], + outputFormatter: function(obj) { return obj; } }) ], properties: [ - new web3._extend.Property({ - name: 'metrics', - getter: 'debug_metrics', - outputFormatter: function(obj) { return obj; } - }) ] }); ` -- cgit v1.2.3 From 92ef33d97a437dce2d7b55f06342de388d95f575 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A9ter=20Szil=C3=A1gyi?= Date: Wed, 24 Jun 2015 18:30:00 +0300 Subject: rpc/api, cmd/geth: retrievel all percentiles, add time units --- rpc/api/debug.go | 43 ++++++++++++++++++++++--------------------- 1 file changed, 22 insertions(+), 21 deletions(-) (limited to 'rpc/api') diff --git a/rpc/api/debug.go b/rpc/api/debug.go index 45c99f295..5975f88ab 100644 --- a/rpc/api/debug.go +++ b/rpc/api/debug.go @@ -193,6 +193,11 @@ func (self *debugApi) Metrics(req *shared.Request) (interface{}, error) { format := func(total float64, rate float64) string { return fmt.Sprintf("%s (%s/s)", round(total, 0), round(rate, 2)) } + // Create the percentile units + percentiles := make([]float64, 101) + for i := 0; i <= 100; i++ { + percentiles[i] = float64(i) / 100 + } // Iterate over all the metrics, and just dump for now counters := make(map[string]interface{}) metrics.DefaultRegistry.Each(func(name string, metric interface{}) { @@ -211,29 +216,25 @@ func (self *debugApi) Metrics(req *shared.Request) (interface{}, error) { switch metric := metric.(type) { case metrics.Meter: root[name] = map[string]interface{}{ - "Avg01Min": metric.Rate1(), - "Avg05Min": metric.Rate5(), - "Avg15Min": metric.Rate15(), - "AvgTotal": metric.RateMean(), - "Total": float64(metric.Count()), + "AvgRate01Min": metric.Rate1(), + "AvgRate05Min": metric.Rate5(), + "AvgRate15Min": metric.Rate15(), + "MeanRate": metric.RateMean(), + "Total": float64(metric.Count()), } case metrics.Timer: + ps := make(map[string]interface{}) + for i, p := range metric.Percentiles(percentiles) { + ps[fmt.Sprintf("%d", i)] = p + } root[name] = map[string]interface{}{ - "Avg01Min": metric.Rate1(), - "Avg05Min": metric.Rate5(), - "Avg15Min": metric.Rate15(), - "AvgTotal": metric.RateMean(), - "Total": float64(metric.Count()), - "Maximum": metric.Max(), - "Minimum": metric.Min(), - "Percentile": map[string]interface{}{ - "20": metric.Percentile(0.2), - "50": metric.Percentile(0.5), - "80": metric.Percentile(0.8), - "95": metric.Percentile(0.95), - "99": metric.Percentile(0.99), - }, + "AvgRate01Min": metric.Rate1(), + "AvgRate05Min": metric.Rate5(), + "AvgRate15Min": metric.Rate15(), + "MeanRate": metric.RateMean(), + "Total": float64(metric.Count()), + "Percentiles": ps, } default: @@ -254,10 +255,10 @@ func (self *debugApi) Metrics(req *shared.Request) (interface{}, error) { "Avg01Min": format(metric.Rate1()*60, metric.Rate1()), "Avg05Min": format(metric.Rate5()*300, metric.Rate5()), "Avg15Min": format(metric.Rate15()*900, metric.Rate15()), - "Count": format(float64(metric.Count()), metric.RateMean()), + "Total": format(float64(metric.Count()), metric.RateMean()), "Maximum": time.Duration(metric.Max()).String(), "Minimum": time.Duration(metric.Min()).String(), - "Percentile": map[string]interface{}{ + "Percentiles": map[string]interface{}{ "20": time.Duration(metric.Percentile(0.2)).String(), "50": time.Duration(metric.Percentile(0.5)).String(), "80": time.Duration(metric.Percentile(0.8)).String(), -- cgit v1.2.3 From fdbf8be7356cb8a80c6fdfe0d24b0863903e1832 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A9ter=20Szil=C3=A1gyi?= Date: Thu, 25 Jun 2015 15:33:26 +0300 Subject: cmd/geth, rpc/api: fix reported metrics issues --- rpc/api/debug.go | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) (limited to 'rpc/api') diff --git a/rpc/api/debug.go b/rpc/api/debug.go index 5975f88ab..f16f62d2e 100644 --- a/rpc/api/debug.go +++ b/rpc/api/debug.go @@ -193,11 +193,6 @@ func (self *debugApi) Metrics(req *shared.Request) (interface{}, error) { format := func(total float64, rate float64) string { return fmt.Sprintf("%s (%s/s)", round(total, 0), round(rate, 2)) } - // Create the percentile units - percentiles := make([]float64, 101) - for i := 0; i <= 100; i++ { - percentiles[i] = float64(i) / 100 - } // Iterate over all the metrics, and just dump for now counters := make(map[string]interface{}) metrics.DefaultRegistry.Each(func(name string, metric interface{}) { @@ -220,21 +215,23 @@ func (self *debugApi) Metrics(req *shared.Request) (interface{}, error) { "AvgRate05Min": metric.Rate5(), "AvgRate15Min": metric.Rate15(), "MeanRate": metric.RateMean(), - "Total": float64(metric.Count()), + "Overall": float64(metric.Count()), } case metrics.Timer: - ps := make(map[string]interface{}) - for i, p := range metric.Percentiles(percentiles) { - ps[fmt.Sprintf("%d", i)] = p - } root[name] = map[string]interface{}{ "AvgRate01Min": metric.Rate1(), "AvgRate05Min": metric.Rate5(), "AvgRate15Min": metric.Rate15(), "MeanRate": metric.RateMean(), - "Total": float64(metric.Count()), - "Percentiles": ps, + "Overall": float64(metric.Count()), + "Percentiles": map[string]interface{}{ + "5": metric.Percentile(0.05), + "20": metric.Percentile(0.2), + "50": metric.Percentile(0.5), + "80": metric.Percentile(0.8), + "95": metric.Percentile(0.95), + }, } default: @@ -247,7 +244,7 @@ func (self *debugApi) Metrics(req *shared.Request) (interface{}, error) { "Avg01Min": format(metric.Rate1()*60, metric.Rate1()), "Avg05Min": format(metric.Rate5()*300, metric.Rate5()), "Avg15Min": format(metric.Rate15()*900, metric.Rate15()), - "Total": format(float64(metric.Count()), metric.RateMean()), + "Overall": format(float64(metric.Count()), metric.RateMean()), } case metrics.Timer: @@ -255,15 +252,15 @@ func (self *debugApi) Metrics(req *shared.Request) (interface{}, error) { "Avg01Min": format(metric.Rate1()*60, metric.Rate1()), "Avg05Min": format(metric.Rate5()*300, metric.Rate5()), "Avg15Min": format(metric.Rate15()*900, metric.Rate15()), - "Total": format(float64(metric.Count()), metric.RateMean()), + "Overall": format(float64(metric.Count()), metric.RateMean()), "Maximum": time.Duration(metric.Max()).String(), "Minimum": time.Duration(metric.Min()).String(), "Percentiles": map[string]interface{}{ + "5": time.Duration(metric.Percentile(0.05)).String(), "20": time.Duration(metric.Percentile(0.2)).String(), "50": time.Duration(metric.Percentile(0.5)).String(), "80": time.Duration(metric.Percentile(0.8)).String(), "95": time.Duration(metric.Percentile(0.95)).String(), - "99": time.Duration(metric.Percentile(0.99)).String(), }, } -- cgit v1.2.3