diff --git a/Cargo.lock b/Cargo.lock index 6f40bff4..b9896f6b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2048,7 +2048,7 @@ dependencies = [ "bitflags 2.9.0", "cexpr", "clang-sys", - "itertools 0.10.5", + "itertools 0.11.0", "lazy_static", "lazycell", "log", @@ -7469,6 +7469,10 @@ dependencies = [ "op-alloy-network", "op-alloy-rpc-types-engine", "op-revm", + "opentelemetry 0.29.1", + "opentelemetry-otlp 0.29.0", + "opentelemetry-semantic-conventions", + "opentelemetry_sdk 0.29.0", "rand 0.9.0", "reth", "reth-basic-payload-builder", @@ -7516,6 +7520,7 @@ dependencies = [ "tokio-util", "tower 0.4.13", "tracing", + "tracing-opentelemetry 0.30.0", "tracing-subscriber 0.3.19", "uuid", ] @@ -7621,6 +7626,20 @@ dependencies = [ "tracing", ] +[[package]] +name = "opentelemetry" +version = "0.29.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e87237e2775f74896f9ad219d26a2081751187eb7c9f5c58dde20a23b95d16c" +dependencies = [ + "futures-core", + "futures-sink", + "js-sys", + "pin-project-lite", + "thiserror 2.0.12", + "tracing", +] + [[package]] name = "opentelemetry-http" version = "0.28.0" @@ -7630,7 +7649,21 @@ dependencies = [ "async-trait", "bytes", "http 1.3.1", - "opentelemetry", + "opentelemetry 0.28.0", + "reqwest 0.12.15", + "tracing", +] + +[[package]] +name = "opentelemetry-http" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46d7ab32b827b5b495bd90fa95a6cb65ccc293555dcc3199ae2937d2d237c8ed" +dependencies = [ + "async-trait", + "bytes", + "http 1.3.1", + "opentelemetry 0.29.1", "reqwest 0.12.15", "tracing", ] @@ -7644,10 +7677,10 @@ dependencies = [ "async-trait", "futures-core", "http 1.3.1", - "opentelemetry", - "opentelemetry-http", - "opentelemetry-proto", - "opentelemetry_sdk", + "opentelemetry 0.28.0", + "opentelemetry-http 0.28.0", + "opentelemetry-proto 0.28.0", + "opentelemetry_sdk 0.28.0", "prost", "reqwest 0.12.15", "serde_json", @@ -7657,6 +7690,26 @@ dependencies = [ "tracing", ] +[[package]] +name = "opentelemetry-otlp" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d899720fe06916ccba71c01d04ecd77312734e2de3467fd30d9d580c8ce85656" +dependencies = [ + "futures-core", + "http 1.3.1", + "opentelemetry 0.29.1", + "opentelemetry-http 0.29.0", + "opentelemetry-proto 0.29.0", + "opentelemetry_sdk 0.29.0", + "prost", + "reqwest 0.12.15", + "thiserror 2.0.12", + "tokio", + "tonic", + "tracing", +] + [[package]] name = "opentelemetry-proto" version = "0.28.0" @@ -7665,13 +7718,31 @@ checksum = "56f8870d3024727e99212eb3bb1762ec16e255e3e6f58eeb3dc8db1aa226746d" dependencies = [ "base64 0.22.1", "hex", - "opentelemetry", - "opentelemetry_sdk", + "opentelemetry 0.28.0", + "opentelemetry_sdk 0.28.0", "prost", "serde", "tonic", ] +[[package]] +name = "opentelemetry-proto" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c40da242381435e18570d5b9d50aca2a4f4f4d8e146231adb4e7768023309b3" +dependencies = [ + "opentelemetry 0.29.1", + "opentelemetry_sdk 0.29.0", + "prost", + "tonic", +] + +[[package]] +name = "opentelemetry-semantic-conventions" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84b29a9f89f1a954936d5aa92f19b2feec3c8f3971d3e96206640db7f9706ae3" + [[package]] name = "opentelemetry_sdk" version = "0.28.0" @@ -7683,7 +7754,7 @@ dependencies = [ "futures-executor", "futures-util", "glob", - "opentelemetry", + "opentelemetry 0.28.0", "percent-encoding", "rand 0.8.5", "serde_json", @@ -7693,6 +7764,24 @@ dependencies = [ "tracing", ] +[[package]] +name = "opentelemetry_sdk" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afdefb21d1d47394abc1ba6c57363ab141be19e27cc70d0e422b7f303e4d290b" +dependencies = [ + "futures-channel", + "futures-executor", + "futures-util", + "glob", + "opentelemetry 0.29.1", + "percent-encoding", + "rand 0.9.0", + "serde_json", + "thiserror 2.0.12", + "tracing", +] + [[package]] name = "option-ext" version = "0.2.0" @@ -8646,7 +8735,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a56d757972c98b346a9b766e3f02746cde6dd1cd1d1d563472929fdd74bec4d" dependencies = [ "anyhow", - "itertools 0.10.5", + "itertools 0.14.0", "proc-macro2", "quote", "syn 2.0.100", @@ -12435,9 +12524,9 @@ dependencies = [ "metrics-util", "moka", "op-alloy-rpc-types-engine", - "opentelemetry", - "opentelemetry-otlp", - "opentelemetry_sdk", + "opentelemetry 0.28.0", + "opentelemetry-otlp 0.28.0", + "opentelemetry_sdk 0.28.0", "parking_lot", "paste", "rustls 0.23.26", @@ -12449,7 +12538,7 @@ dependencies = [ "tower 0.4.13", "tower-http 0.5.2", "tracing", - "tracing-opentelemetry", + "tracing-opentelemetry 0.29.0", "tracing-subscriber 0.3.19", "url", ] @@ -14666,8 +14755,26 @@ checksum = "721f2d2569dce9f3dfbbddee5906941e953bfcdf736a62da3377f5751650cc36" dependencies = [ "js-sys", "once_cell", - "opentelemetry", - "opentelemetry_sdk", + "opentelemetry 0.28.0", + "opentelemetry_sdk 0.28.0", + "smallvec", + "tracing", + "tracing-core", + "tracing-log", + "tracing-subscriber 0.3.19", + "web-time", +] + +[[package]] +name = "tracing-opentelemetry" +version = "0.30.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd8e764bd6f5813fd8bebc3117875190c5b0415be8f7f8059bffb6ecd979c444" +dependencies = [ + "js-sys", + "once_cell", + "opentelemetry 0.29.1", + "opentelemetry_sdk 0.29.0", "smallvec", "tracing", "tracing-core", diff --git a/crates/op-rbuilder/Cargo.toml b/crates/op-rbuilder/Cargo.toml index 2159c04e..462408cb 100644 --- a/crates/op-rbuilder/Cargo.toml +++ b/crates/op-rbuilder/Cargo.toml @@ -88,6 +88,13 @@ tokio-tungstenite = "0.26.2" rand = "0.9.0" tracing-subscriber = { version = "0.3.18", features = ["env-filter", "json"] } +# OpenTelemetry dependencies +opentelemetry = { version = "0.29.1", optional = true } +opentelemetry_sdk = { version = "0.29", optional = true } +opentelemetry-otlp = { version = "0.29.0", optional = true, features = ["grpc-tonic"] } +opentelemetry-semantic-conventions = { version = "0.29.0", optional = true } +tracing-opentelemetry = { version = "0.30.0", optional = true } + # `flashblocks` branch rollup-boost = { git = "http://github.com/flashbots/rollup-boost", rev = "60885346d4cf7f241de82790478195747433d472" } @@ -95,7 +102,7 @@ rollup-boost = { git = "http://github.com/flashbots/rollup-boost", rev = "608853 tikv-jemallocator = { version = "0.6", optional = true } [features] -default = ["jemalloc"] +default = ["jemalloc", "telemetry"] jemalloc = [ "dep:tikv-jemallocator", @@ -118,6 +125,14 @@ min-trace-logs = ["tracing/release_max_level_trace"] integration = [] flashblocks = [] +telemetry = [ + "opentelemetry", + "opentelemetry_sdk", + "opentelemetry-otlp", + "opentelemetry-semantic-conventions", + "tracing-opentelemetry", +] + [[bin]] name = "op-rbuilder" path = "src/main.rs" diff --git a/crates/op-rbuilder/src/args.rs b/crates/op-rbuilder/src/args.rs index 1e99c7fd..c7a8f810 100644 --- a/crates/op-rbuilder/src/args.rs +++ b/crates/op-rbuilder/src/args.rs @@ -41,4 +41,16 @@ pub struct OpRbuilderArgs { /// Signals whether to log pool transaction events #[arg(long = "builder.log-pool-transactions", default_value = "false")] pub log_pool_transactions: bool, + /// OpenTelemetry endpoint for metrics + #[cfg(feature = "telemetry")] + #[arg(long, value_name = "TRACING")] + pub tracing: bool, + /// OpenTelemetry endpoint for metrics + #[cfg(feature = "telemetry")] + #[arg( + long, + value_name = "TRACING_ENDPOINT", + default_value = "http://localhost:4317" + )] + pub tracing_endpoint: String, } diff --git a/crates/op-rbuilder/src/main.rs b/crates/op-rbuilder/src/main.rs index c4d43f9f..eb5daffa 100644 --- a/crates/op-rbuilder/src/main.rs +++ b/crates/op-rbuilder/src/main.rs @@ -1,9 +1,12 @@ use clap::Parser; +use eyre::Result; use monitoring::Monitoring; use reth::providers::CanonStateSubscriptions; +use reth::CliRunner; use reth_optimism_cli::{chainspec::OpChainSpecParser, Cli}; use reth_optimism_node::node::OpAddOnsBuilder; use reth_optimism_node::OpNode; +use tokio::runtime::Runtime; #[cfg(feature = "flashblocks")] use payload_builder::CustomOpPayloadBuilder; @@ -24,59 +27,79 @@ pub mod payload_builder; #[cfg(not(feature = "flashblocks"))] mod payload_builder_vanilla; mod primitives; +#[cfg(feature = "telemetry")] +mod telemetry; #[cfg(test)] mod tester; mod tx_signer; use monitor_tx_pool::monitor_tx_pool; -fn main() { - Cli::::parse() - .run(|builder, builder_args| async move { - let rollup_args = builder_args.rollup_args; +fn main() -> Result<()> { + let cli = Cli::::parse(); - let op_node = OpNode::new(rollup_args.clone()); - let handle = builder - .with_types::() - .with_components(op_node.components().payload(CustomOpPayloadBuilder::new( - builder_args.builder_signer, - builder_args.flashblocks_ws_url, - builder_args.chain_block_time, - builder_args.flashblock_block_time, - ))) - .with_add_ons( - OpAddOnsBuilder::default() - .with_sequencer(rollup_args.sequencer.clone()) - .with_enable_tx_conditional(rollup_args.enable_tx_conditional) - .build(), - ) - .on_node_started(move |ctx| { - let new_canonical_blocks = ctx.provider().canonical_state_stream(); - let builder_signer = builder_args.builder_signer; + // Create runtime in the outer scope + let rt = Runtime::new()?; + // let _guard = rt.enter(); - if builder_args.log_pool_transactions { - tracing::info!("Logging pool transactions"); - ctx.task_executor.spawn_critical( - "txlogging", - Box::pin(async move { - monitor_tx_pool(ctx.pool.all_transactions_event_listener()).await; - }), - ); - } + #[cfg(feature = "telemetry")] + let mut provider = + rt.block_on(async { telemetry::TelemetryControl::init_from_commands(&cli.command) })?; + let runner = CliRunner::from_runtime(rt); + + cli.with_runner(runner, |builder, builder_args| async move { + // Wrap the main execution in a span + let span = tracing::info_span!("op-rbuilder"); + let _enter = span.enter(); + + let rollup_args = builder_args.rollup_args; + + let op_node = OpNode::new(rollup_args.clone()); + let handle = builder + .with_types::() + .with_components(op_node.components().payload(CustomOpPayloadBuilder::new( + builder_args.builder_signer, + builder_args.flashblocks_ws_url, + builder_args.chain_block_time, + builder_args.flashblock_block_time, + ))) + .with_add_ons( + OpAddOnsBuilder::default() + .with_sequencer(rollup_args.sequencer.clone()) + .with_enable_tx_conditional(rollup_args.enable_tx_conditional) + .build(), + ) + .on_node_started(move |ctx| { + let new_canonical_blocks = ctx.provider().canonical_state_stream(); + let builder_signer = builder_args.builder_signer; + + if builder_args.log_pool_transactions { + tracing::info!("Logging pool transactions"); ctx.task_executor.spawn_critical( - "monitoring", + "txlogging", Box::pin(async move { - let monitoring = Monitoring::new(builder_signer); - let _ = monitoring.run_with_stream(new_canonical_blocks).await; + monitor_tx_pool(ctx.pool.all_transactions_event_listener()).await; }), ); + } + + ctx.task_executor.spawn_critical( + "monitoring", + Box::pin(async move { + let monitoring = Monitoring::new(builder_signer); + let _ = monitoring.run_with_stream(new_canonical_blocks).await; + }), + ); + + Ok(()) + }) + .launch() + .await?; + + handle.node_exit_future.await?; - Ok(()) - }) - .launch() - .await?; + Ok(()) + })?; - handle.node_exit_future.await - }) - .unwrap(); + Ok(()) } diff --git a/crates/op-rbuilder/src/telemetry.rs b/crates/op-rbuilder/src/telemetry.rs new file mode 100644 index 00000000..8e670469 --- /dev/null +++ b/crates/op-rbuilder/src/telemetry.rs @@ -0,0 +1,93 @@ +use crate::args::OpRbuilderArgs; +use eyre::Result; +use opentelemetry::{trace::TracerProvider, KeyValue}; +use opentelemetry_otlp::WithExportConfig; +use opentelemetry_sdk::{ + trace::{RandomIdGenerator, Sampler, SdkTracer, SdkTracerProvider}, + Resource, +}; +use opentelemetry_semantic_conventions::{ + resource::{SERVICE_NAME, SERVICE_VERSION}, + SCHEMA_URL, +}; +use reth_optimism_cli::chainspec::OpChainSpecParser; +use reth_optimism_cli::commands::Commands; +use tracing::Level; +use tracing_opentelemetry::OpenTelemetryLayer; +use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt}; + +#[derive(Default)] +pub struct TelemetryControl { + tracer: Option, + provider: Option, +} + +impl TelemetryControl { + pub fn init_from_commands( + commands: &Commands, + ) -> Result { + if let Commands::Node(command) = commands { + Self::init_with_args(&command.ext) + } else { + Ok(Self::default()) + } + } + + pub fn init_with_args(args: &OpRbuilderArgs) -> Result { + if args.tracing { + Self::init(&args.tracing_endpoint) + } else { + Ok(Self::default()) + } + } + + /// Initialize OpenTelemetry tracing with OTLP exporter + pub fn init(tracing_endpoint: &str) -> Result { + let exporter = opentelemetry_otlp::SpanExporter::builder() + .with_tonic() + // .with_endpoint(tracing_endpoint) + .build()?; + + let provider = opentelemetry_sdk::trace::SdkTracerProvider::builder() + .with_resource(resource()) + .with_simple_exporter(exporter) + .build(); + + // opentelemetry::global::set_tracer_provider(provider.clone()); + let tracer = provider.tracer("tracing-op-rbuilder"); + tracing_subscriber::registry() + .with(tracing_subscriber::filter::LevelFilter::from_level( + Level::INFO, + )) + .with(tracing_subscriber::fmt::layer()) + .with(OpenTelemetryLayer::new(tracer)) + .try_init()?; + + tracing::info!("OTLP Initalized"); + + let tracer = provider.tracer("tracing-op-rbuilder-root"); + Ok(Self { + tracer: Some(tracer), + provider: Some(provider), + }) + } + + pub fn shutdown(&mut self) -> Result<()> { + if let Some(provider) = self.provider.take() { + provider.shutdown()?; + } + Ok(()) + } +} + +fn resource() -> Resource { + Resource::builder() + .with_schema_url( + [ + KeyValue::new(SERVICE_NAME, env!("CARGO_PKG_NAME")), + KeyValue::new(SERVICE_VERSION, env!("CARGO_PKG_VERSION")), + ], + SCHEMA_URL, + ) + .build() +}