diff --git a/cmd/simulator/config/flags.go b/cmd/simulator/config/flags.go index d1db9f1b68..8fb3d3c5fd 100644 --- a/cmd/simulator/config/flags.go +++ b/cmd/simulator/config/flags.go @@ -28,6 +28,7 @@ const ( TimeoutKey = "timeout" BatchSizeKey = "batch-size" MetricsPortKey = "metrics-port" + MetricsOutputKey = "metrics-output" ) var ( @@ -37,28 +38,30 @@ var ( ) type Config struct { - Endpoints []string `json:"endpoints"` - MaxFeeCap int64 `json:"max-fee-cap"` - MaxTipCap int64 `json:"max-tip-cap"` - Workers int `json:"workers"` - TxsPerWorker uint64 `json:"txs-per-worker"` - KeyDir string `json:"key-dir"` - Timeout time.Duration `json:"timeout"` - BatchSize uint64 `json:"batch-size"` - MetricsPort uint64 `json:"metrics-port"` + Endpoints []string `json:"endpoints"` + MaxFeeCap int64 `json:"max-fee-cap"` + MaxTipCap int64 `json:"max-tip-cap"` + Workers int `json:"workers"` + TxsPerWorker uint64 `json:"txs-per-worker"` + KeyDir string `json:"key-dir"` + Timeout time.Duration `json:"timeout"` + BatchSize uint64 `json:"batch-size"` + MetricsPort uint64 `json:"metrics-port"` + MetricsOutput string `json:"metrics-output"` } func BuildConfig(v *viper.Viper) (Config, error) { c := Config{ - Endpoints: v.GetStringSlice(EndpointsKey), - MaxFeeCap: v.GetInt64(MaxFeeCapKey), - MaxTipCap: v.GetInt64(MaxTipCapKey), - Workers: v.GetInt(WorkersKey), - TxsPerWorker: v.GetUint64(TxsPerWorkerKey), - KeyDir: v.GetString(KeyDirKey), - Timeout: v.GetDuration(TimeoutKey), - BatchSize: v.GetUint64(BatchSizeKey), - MetricsPort: v.GetUint64(MetricsPortKey), + Endpoints: v.GetStringSlice(EndpointsKey), + MaxFeeCap: v.GetInt64(MaxFeeCapKey), + MaxTipCap: v.GetInt64(MaxTipCapKey), + Workers: v.GetInt(WorkersKey), + TxsPerWorker: v.GetUint64(TxsPerWorkerKey), + KeyDir: v.GetString(KeyDirKey), + Timeout: v.GetDuration(TimeoutKey), + BatchSize: v.GetUint64(BatchSizeKey), + MetricsPort: v.GetUint64(MetricsPortKey), + MetricsOutput: v.GetString(MetricsOutputKey), } if len(c.Endpoints) == 0 { return c, ErrNoEndpoints @@ -122,4 +125,5 @@ func addSimulatorFlags(fs *pflag.FlagSet) { fs.String(LogLevelKey, "info", "Specify the log level to use in the simulator") fs.Uint64(BatchSizeKey, 100, "Specify the batchsize for the worker to issue and confirm txs") fs.Uint64(MetricsPortKey, 8082, "Specify the port to use for the metrics server") + fs.String(MetricsOutputKey, "", "Specify the file to write metrics in json format, or empy to write to stdout (defaults to stdout)") } diff --git a/cmd/simulator/load/loader.go b/cmd/simulator/load/loader.go index 80eaeeaacf..0dffe80bb7 100644 --- a/cmd/simulator/load/loader.go +++ b/cmd/simulator/load/loader.go @@ -150,7 +150,8 @@ func ExecuteLoader(ctx context.Context, config config.Config) error { }() m := metrics.NewDefaultMetrics() - ms := m.Serve(ctx, strconv.Itoa(int(config.MetricsPort)), MetricsEndpoint) + metricsCtx := context.Background() + ms := m.Serve(metricsCtx, strconv.Itoa(int(config.MetricsPort)), MetricsEndpoint) defer ms.Shutdown() // Construct the arguments for the load simulator @@ -186,13 +187,13 @@ func ExecuteLoader(ctx context.Context, config config.Config) error { // to fund gas for all of their transactions. maxFeeCap := new(big.Int).Mul(big.NewInt(params.GWei), big.NewInt(config.MaxFeeCap)) minFundsPerAddr := new(big.Int).Mul(maxFeeCap, big.NewInt(int64(config.TxsPerWorker*params.TxGas))) - + fundStart := time.Now() log.Info("Distributing funds", "numTxsPerWorker", config.TxsPerWorker, "minFunds", minFundsPerAddr) keys, err = DistributeFunds(ctx, clients[0], keys, config.Workers, minFundsPerAddr, m) if err != nil { return err } - log.Info("Distributed funds successfully") + log.Info("Distributed funds successfully", "time", time.Since(fundStart)) pks := make([]*ecdsa.PrivateKey, 0, len(keys)) senders := make([]common.Address, 0, len(keys)) @@ -225,11 +226,12 @@ func ExecuteLoader(ctx context.Context, config config.Config) error { Value: common.Big0, }) } - + txSequenceStart := time.Now() txSequences, err := txs.GenerateTxSequences(ctx, txGenerator, clients[0], pks, config.TxsPerWorker, false) if err != nil { return err } + log.Info("Created transaction sequences successfully", "time", time.Since(txSequenceStart)) workers := make([]txs.Worker[*types.Transaction], 0, len(clients)) for i, client := range clients { @@ -237,6 +239,9 @@ func ExecuteLoader(ctx context.Context, config config.Config) error { } loader := New(workers, txSequences, config.BatchSize, m) err = loader.Execute(ctx) - ms.Print() // Print regardless of execution error + prerr := m.Print(config.MetricsOutput) // Print regardless of execution error + if prerr != nil { + log.Warn("Failed to print metrics", "error", prerr) + } return err } diff --git a/cmd/simulator/metrics/metrics.go b/cmd/simulator/metrics/metrics.go index a711d47514..f9d44bbbcb 100644 --- a/cmd/simulator/metrics/metrics.go +++ b/cmd/simulator/metrics/metrics.go @@ -5,11 +5,11 @@ package metrics import ( "context" + "encoding/json" "errors" "fmt" - "io/ioutil" "net/http" - "strings" + "os" "github.com/ethereum/go-ethereum/log" "github.com/prometheus/client_golang/prometheus" @@ -108,22 +108,32 @@ func (ms *MetricsServer) Shutdown() { <-ms.stopCh } -func (ms *MetricsServer) Print() { - // Get response from server - resp, err := http.Get(fmt.Sprintf("http://localhost:%s%s", ms.metricsPort, ms.metricsEndpoint)) +func (m *Metrics) Print(outputFile string) error { + metrics, err := m.reg.Gather() if err != nil { - log.Error("cannot get response from metrics servers", "err", err) - return + return err } - // Read response body - respBody, err := ioutil.ReadAll(resp.Body) - if err != nil { - log.Error("cannot read response body", "err", err) - return - } - // Print out formatted individual metrics - parts := strings.Split(string(respBody), "\n") - for _, s := range parts { - fmt.Printf(" \t\t\t%s\n", s) + + if outputFile == "" { + // Printout to stdout + fmt.Println("*** Metrics ***") + for _, mf := range metrics { + for _, m := range mf.GetMetric() { + fmt.Printf("Type: %s, Name: %s, Description: %s, Values: %s\n", mf.GetType().String(), mf.GetName(), mf.GetHelp(), m.String()) + } + } + fmt.Println("***************") + } else { + jsonFile, err := os.Create(outputFile) + if err != nil { + return err + } + defer jsonFile.Close() + + if err := json.NewEncoder(jsonFile).Encode(metrics); err != nil { + return err + } } + + return nil }