Files
reth/crates/optimism/cli/src/app.rs
Kamil Szczygieł 1fbd5a95f8 feat: Support for sending logs through OTLP (#21039)
Co-authored-by: Matthias Seitz <matthias.seitz@outlook.de>
2026-01-14 21:29:00 +00:00

155 lines
6.5 KiB
Rust

use crate::{Cli, Commands};
use eyre::{eyre, Result};
use reth_cli::chainspec::ChainSpecParser;
use reth_cli_commands::launcher::Launcher;
use reth_cli_runner::CliRunner;
use reth_node_core::args::{OtlpInitStatus, OtlpLogsStatus};
use reth_node_metrics::recorder::install_prometheus_recorder;
use reth_optimism_chainspec::OpChainSpec;
use reth_optimism_consensus::OpBeaconConsensus;
use reth_optimism_node::{OpExecutorProvider, OpNode};
use reth_rpc_server_types::RpcModuleValidator;
use reth_tracing::{FileWorkerGuard, Layers};
use std::{fmt, sync::Arc};
use tracing::{info, warn};
/// A wrapper around a parsed CLI that handles command execution.
#[derive(Debug)]
pub struct CliApp<Spec: ChainSpecParser, Ext: clap::Args + fmt::Debug, Rpc: RpcModuleValidator> {
cli: Cli<Spec, Ext, Rpc>,
runner: Option<CliRunner>,
layers: Option<Layers>,
guard: Option<FileWorkerGuard>,
}
impl<C, Ext, Rpc> CliApp<C, Ext, Rpc>
where
C: ChainSpecParser<ChainSpec = OpChainSpec>,
Ext: clap::Args + fmt::Debug,
Rpc: RpcModuleValidator,
{
pub(crate) fn new(cli: Cli<C, Ext, Rpc>) -> Self {
Self { cli, runner: None, layers: Some(Layers::new()), guard: None }
}
/// Sets the runner for the CLI commander.
///
/// This replaces any existing runner with the provided one.
pub fn set_runner(&mut self, runner: CliRunner) {
self.runner = Some(runner);
}
/// Access to tracing layers.
///
/// Returns a mutable reference to the tracing layers, or error
/// if tracing initialized and layers have detached already.
pub fn access_tracing_layers(&mut self) -> Result<&mut Layers> {
self.layers.as_mut().ok_or_else(|| eyre!("Tracing already initialized"))
}
/// Execute the configured cli command.
///
/// This accepts a closure that is used to launch the node via the
/// [`NodeCommand`](reth_cli_commands::node::NodeCommand).
pub fn run(mut self, launcher: impl Launcher<C, Ext>) -> Result<()> {
let runner = match self.runner.take() {
Some(runner) => runner,
None => CliRunner::try_default_runtime()?,
};
// add network name to logs dir
// Add network name if available to the logs dir
if let Some(chain_spec) = self.cli.command.chain_spec() {
self.cli.logs.log_file_directory =
self.cli.logs.log_file_directory.join(chain_spec.chain.to_string());
}
self.init_tracing(&runner)?;
// Install the prometheus recorder to be sure to record all metrics
install_prometheus_recorder();
let components = |spec: Arc<OpChainSpec>| {
(OpExecutorProvider::optimism(spec.clone()), Arc::new(OpBeaconConsensus::new(spec)))
};
match self.cli.command {
Commands::Node(command) => {
// Validate RPC modules using the configured validator
if let Some(http_api) = &command.rpc.http_api {
Rpc::validate_selection(http_api, "http.api").map_err(|e| eyre!("{e}"))?;
}
if let Some(ws_api) = &command.rpc.ws_api {
Rpc::validate_selection(ws_api, "ws.api").map_err(|e| eyre!("{e}"))?;
}
runner.run_command_until_exit(|ctx| command.execute(ctx, launcher))
}
Commands::Init(command) => {
runner.run_blocking_until_ctrl_c(command.execute::<OpNode>())
}
Commands::InitState(command) => {
runner.run_blocking_until_ctrl_c(command.execute::<OpNode>())
}
Commands::ImportOp(command) => {
runner.run_blocking_until_ctrl_c(command.execute::<OpNode>())
}
Commands::ImportReceiptsOp(command) => {
runner.run_blocking_until_ctrl_c(command.execute::<OpNode>())
}
Commands::DumpGenesis(command) => runner.run_blocking_until_ctrl_c(command.execute()),
Commands::Db(command) => {
runner.run_blocking_command_until_exit(|ctx| command.execute::<OpNode>(ctx))
}
Commands::Stage(command) => {
runner.run_command_until_exit(|ctx| command.execute::<OpNode, _>(ctx, components))
}
Commands::P2P(command) => runner.run_until_ctrl_c(command.execute::<OpNode>()),
Commands::Config(command) => runner.run_until_ctrl_c(command.execute()),
Commands::Prune(command) => runner.run_until_ctrl_c(command.execute::<OpNode>()),
#[cfg(feature = "dev")]
Commands::TestVectors(command) => runner.run_until_ctrl_c(command.execute()),
Commands::ReExecute(command) => {
runner.run_until_ctrl_c(command.execute::<OpNode>(components))
}
}
}
/// Initializes tracing with the configured options.
///
/// If file logging is enabled, this function stores guard to the struct.
/// For gRPC OTLP, it requires tokio runtime context.
pub fn init_tracing(&mut self, runner: &CliRunner) -> Result<()> {
if self.guard.is_none() {
let mut layers = self.layers.take().unwrap_or_default();
let otlp_status = runner.block_on(self.cli.traces.init_otlp_tracing(&mut layers))?;
let otlp_logs_status = runner.block_on(self.cli.traces.init_otlp_logs(&mut layers))?;
self.guard = self.cli.logs.init_tracing_with_layers(layers)?;
info!(target: "reth::cli", "Initialized tracing, debug log directory: {}", self.cli.logs.log_file_directory);
match otlp_status {
OtlpInitStatus::Started(endpoint) => {
info!(target: "reth::cli", "Started OTLP {:?} tracing export to {endpoint}", self.cli.traces.protocol);
}
OtlpInitStatus::NoFeature => {
warn!(target: "reth::cli", "Provided OTLP tracing arguments do not have effect, compile with the `otlp` feature")
}
OtlpInitStatus::Disabled => {}
}
match otlp_logs_status {
OtlpLogsStatus::Started(endpoint) => {
info!(target: "reth::cli", "Started OTLP {:?} logs export to {endpoint}", self.cli.traces.protocol);
}
OtlpLogsStatus::NoFeature => {
warn!(target: "reth::cli", "Provided OTLP logs arguments do not have effect, compile with the `otlp-logs` feature")
}
OtlpLogsStatus::Disabled => {}
}
}
Ok(())
}
}