diff --git a/op-batcher/metrics/metrics.go b/op-batcher/metrics/metrics.go index f7093a6e87f93..31ba792cb63d3 100644 --- a/op-batcher/metrics/metrics.go +++ b/op-batcher/metrics/metrics.go @@ -185,7 +185,7 @@ func (m *Metrics) RecordLatestL1Block(l1ref eth.L1BlockRef) { m.RecordL1Ref("latest", l1ref) } -// RecordL2BlockLoaded should be called when a new L2 block was loaded into the +// RecordL2BlocksLoaded should be called when a new L2 block was loaded into the // channel manager (but not processed yet). func (m *Metrics) RecordL2BlocksLoaded(l2ref eth.L2BlockRef) { m.RecordL2Ref(StageLoaded, l2ref) diff --git a/op-e2e/actions/l2_proposer.go b/op-e2e/actions/l2_proposer.go index cc31df477b1e7..d5347a79ed531 100644 --- a/op-e2e/actions/l2_proposer.go +++ b/op-e2e/actions/l2_proposer.go @@ -14,6 +14,7 @@ import ( "github.com/stretchr/testify/require" "github.com/ethereum-optimism/optimism/op-node/sources" + "github.com/ethereum-optimism/optimism/op-proposer/metrics" "github.com/ethereum-optimism/optimism/op-proposer/proposer" opcrypto "github.com/ethereum-optimism/optimism/op-service/crypto" "github.com/ethereum-optimism/optimism/op-service/txmgr" @@ -60,7 +61,7 @@ func NewL2Proposer(t Testing, log log.Logger, cfg *ProposerCfg, l1 *ethclient.Cl SignerFnFactory: signer, } - dr, err := proposer.NewL2OutputSubmitter(proposerCfg, log) + dr, err := proposer.NewL2OutputSubmitter(proposerCfg, log, metrics.NoopMetrics) require.NoError(t, err) return &L2Proposer{ diff --git a/op-e2e/migration_test.go b/op-e2e/migration_test.go index 69bf616e65065..7ee2cead07161 100644 --- a/op-e2e/migration_test.go +++ b/op-e2e/migration_test.go @@ -15,6 +15,7 @@ import ( batchermetrics "github.com/ethereum-optimism/optimism/op-batcher/metrics" "github.com/ethereum-optimism/optimism/op-node/chaincfg" "github.com/ethereum-optimism/optimism/op-node/sources" + proposermetrics "github.com/ethereum-optimism/optimism/op-proposer/metrics" l2os "github.com/ethereum-optimism/optimism/op-proposer/proposer" oplog "github.com/ethereum-optimism/optimism/op-service/log" @@ -362,7 +363,7 @@ func TestMigration(t *testing.T) { Format: "text", }, PrivateKey: hexPriv(secrets.Proposer), - }, lgr.New("module", "proposer")) + }, lgr.New("module", "proposer"), proposermetrics.NoopMetrics) require.NoError(t, err) t.Cleanup(func() { proposer.Stop() diff --git a/op-e2e/setup.go b/op-e2e/setup.go index fea710838265f..6108eddbf497a 100644 --- a/op-e2e/setup.go +++ b/op-e2e/setup.go @@ -37,6 +37,7 @@ import ( "github.com/ethereum-optimism/optimism/op-node/rollup/driver" "github.com/ethereum-optimism/optimism/op-node/sources" "github.com/ethereum-optimism/optimism/op-node/testlog" + proposermetrics "github.com/ethereum-optimism/optimism/op-proposer/metrics" l2os "github.com/ethereum-optimism/optimism/op-proposer/proposer" oplog "github.com/ethereum-optimism/optimism/op-service/log" ) @@ -572,7 +573,7 @@ func (cfg SystemConfig) Start(_opts ...SystemConfigOption) (*System, error) { Format: "text", }, PrivateKey: hexPriv(cfg.Secrets.Proposer), - }, sys.cfg.Loggers["proposer"]) + }, sys.cfg.Loggers["proposer"], proposermetrics.NoopMetrics) if err != nil { return nil, fmt.Errorf("unable to setup l2 output submitter: %w", err) } diff --git a/op-proposer/metrics/metrics.go b/op-proposer/metrics/metrics.go new file mode 100644 index 0000000000000..a5296b637d55f --- /dev/null +++ b/op-proposer/metrics/metrics.go @@ -0,0 +1,100 @@ +package metrics + +import ( + "context" + + "github.com/ethereum-optimism/optimism/op-node/eth" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/ethclient" + "github.com/ethereum/go-ethereum/log" + "github.com/prometheus/client_golang/prometheus" + + opmetrics "github.com/ethereum-optimism/optimism/op-service/metrics" +) + +const Namespace = "op_proposer" + +type Metricer interface { + RecordInfo(version string) + RecordUp() + + // Records all L1 and L2 block events + opmetrics.RefMetricer + + RecordL2BlocksProposed(l2ref eth.L2BlockRef) +} + +type Metrics struct { + ns string + registry *prometheus.Registry + factory opmetrics.Factory + + opmetrics.RefMetrics + + Info prometheus.GaugeVec + Up prometheus.Gauge +} + +var _ Metricer = (*Metrics)(nil) + +func NewMetrics(procName string) *Metrics { + if procName == "" { + procName = "default" + } + ns := Namespace + "_" + procName + + registry := opmetrics.NewRegistry() + factory := opmetrics.With(registry) + + return &Metrics{ + ns: ns, + registry: registry, + factory: factory, + + RefMetrics: opmetrics.MakeRefMetrics(ns, factory), + + Info: *factory.NewGaugeVec(prometheus.GaugeOpts{ + Namespace: ns, + Name: "info", + Help: "Pseudo-metric tracking version and config info", + }, []string{ + "version", + }), + Up: factory.NewGauge(prometheus.GaugeOpts{ + Namespace: ns, + Name: "up", + Help: "1 if the op-proposer has finished starting up", + }), + } +} + +func (m *Metrics) Serve(ctx context.Context, host string, port int) error { + return opmetrics.ListenAndServe(ctx, m.registry, host, port) +} + +func (m *Metrics) StartBalanceMetrics(ctx context.Context, + l log.Logger, client *ethclient.Client, account common.Address) { + opmetrics.LaunchBalanceMetrics(ctx, l, m.registry, m.ns, client, account) +} + +// RecordInfo sets a pseudo-metric that contains versioning and +// config info for the op-proposer. +func (m *Metrics) RecordInfo(version string) { + m.Info.WithLabelValues(version).Set(1) +} + +// RecordUp sets the up metric to 1. +func (m *Metrics) RecordUp() { + prometheus.MustRegister() + m.Up.Set(1) +} + +const ( + BlockProposed = "proposed" +) + +// RecordL2BlocksProposed should be called when new L2 block is proposed +func (m *Metrics) RecordL2BlocksProposed(l2ref eth.L2BlockRef) { + m.RecordL2Ref(BlockProposed, l2ref) +} diff --git a/op-proposer/metrics/noop.go b/op-proposer/metrics/noop.go new file mode 100644 index 0000000000000..d5cf33e58ea9d --- /dev/null +++ b/op-proposer/metrics/noop.go @@ -0,0 +1,15 @@ +package metrics + +import ( + "github.com/ethereum-optimism/optimism/op-node/eth" + opmetrics "github.com/ethereum-optimism/optimism/op-service/metrics" +) + +type noopMetrics struct{ opmetrics.NoopRefMetrics } + +var NoopMetrics Metricer = new(noopMetrics) + +func (*noopMetrics) RecordInfo(version string) {} +func (*noopMetrics) RecordUp() {} + +func (*noopMetrics) RecordL2BlocksProposed(l2ref eth.L2BlockRef) {} diff --git a/op-proposer/proposer/l2_output_submitter.go b/op-proposer/proposer/l2_output_submitter.go index 72bc25f5a1c7f..c8c6d35c37de7 100644 --- a/op-proposer/proposer/l2_output_submitter.go +++ b/op-proposer/proposer/l2_output_submitter.go @@ -24,9 +24,9 @@ import ( "github.com/ethereum-optimism/optimism/op-bindings/bindings" "github.com/ethereum-optimism/optimism/op-node/eth" "github.com/ethereum-optimism/optimism/op-node/sources" + "github.com/ethereum-optimism/optimism/op-proposer/metrics" opcrypto "github.com/ethereum-optimism/optimism/op-service/crypto" oplog "github.com/ethereum-optimism/optimism/op-service/log" - opmetrics "github.com/ethereum-optimism/optimism/op-service/metrics" oppprof "github.com/ethereum-optimism/optimism/op-service/pprof" oprpc "github.com/ethereum-optimism/optimism/op-service/rpc" "github.com/ethereum-optimism/optimism/op-service/txmgr" @@ -49,9 +49,10 @@ func Main(version string, cliCtx *cli.Context) error { } l := oplog.NewLogger(cfg.LogConfig) + m := metrics.NewMetrics("default") l.Info("Initializing L2 Output Submitter") - l2OutputSubmitter, err := NewL2OutputSubmitterFromCLIConfig(cfg, l) + l2OutputSubmitter, err := NewL2OutputSubmitterFromCLIConfig(cfg, l, m) if err != nil { l.Error("Unable to create the L2 Output Submitter", "error", err) return err @@ -78,17 +79,15 @@ func Main(version string, cliCtx *cli.Context) error { }() } - registry := opmetrics.NewRegistry() metricsCfg := cfg.MetricsConfig if metricsCfg.Enabled { l.Info("starting metrics server", "addr", metricsCfg.ListenAddr, "port", metricsCfg.ListenPort) go func() { - if err := opmetrics.ListenAndServe(ctx, registry, metricsCfg.ListenAddr, metricsCfg.ListenPort); err != nil { + if err := m.Serve(ctx, metricsCfg.ListenAddr, metricsCfg.ListenPort); err != nil { l.Error("error starting metrics server", err) } }() - addr := l2OutputSubmitter.from - opmetrics.LaunchBalanceMetrics(ctx, l, registry, "", l2OutputSubmitter.l1Client, addr) + m.StartBalanceMetrics(ctx, l, l2OutputSubmitter.l1Client, l2OutputSubmitter.from) } rpcCfg := cfg.RPCConfig @@ -98,6 +97,9 @@ func Main(version string, cliCtx *cli.Context) error { return fmt.Errorf("error starting RPC server: %w", err) } + m.RecordInfo(version) + m.RecordUp() + interruptChannel := make(chan os.Signal, 1) signal.Notify(interruptChannel, []os.Signal{ os.Interrupt, @@ -117,6 +119,7 @@ type L2OutputSubmitter struct { wg sync.WaitGroup done chan struct{} log log.Logger + metr metrics.Metricer ctx context.Context cancel context.CancelFunc @@ -143,7 +146,7 @@ type L2OutputSubmitter struct { } // NewL2OutputSubmitterFromCLIConfig creates a new L2 Output Submitter given the CLI Config -func NewL2OutputSubmitterFromCLIConfig(cfg CLIConfig, l log.Logger) (*L2OutputSubmitter, error) { +func NewL2OutputSubmitterFromCLIConfig(cfg CLIConfig, l log.Logger, m metrics.Metricer) (*L2OutputSubmitter, error) { signer, fromAddress, err := opcrypto.SignerFactoryFromConfig(l, cfg.PrivateKey, cfg.Mnemonic, cfg.L2OutputHDPath, cfg.SignerConfig) if err != nil { return nil, err @@ -185,11 +188,11 @@ func NewL2OutputSubmitterFromCLIConfig(cfg CLIConfig, l log.Logger) (*L2OutputSu SignerFnFactory: signer, } - return NewL2OutputSubmitter(proposerCfg, l) + return NewL2OutputSubmitter(proposerCfg, l, m) } // NewL2OutputSubmitter creates a new L2 Output Submitter -func NewL2OutputSubmitter(cfg Config, l log.Logger) (*L2OutputSubmitter, error) { +func NewL2OutputSubmitter(cfg Config, l log.Logger, m metrics.Metricer) (*L2OutputSubmitter, error) { ctx, cancel := context.WithCancel(context.Background()) cCtx, cCancel := context.WithTimeout(ctx, defaultDialTimeout) @@ -228,6 +231,7 @@ func NewL2OutputSubmitter(cfg Config, l log.Logger) (*L2OutputSubmitter, error) log: l, ctx: ctx, cancel: cancel, + metr: m, l1Client: cfg.L1Client, rollupClient: cfg.RollupClient, @@ -413,9 +417,9 @@ func (l *L2OutputSubmitter) loop() { l.log.Error("Failed to send proposal transaction", "err", err) cancel() break - } else { - cancel() } + l.metr.RecordL2BlocksProposed(output.BlockRef) + cancel() case <-l.done: return