From 42cecb9e2542d5a2a403d77e80055f30a48ee26c Mon Sep 17 00:00:00 2001 From: romsters Date: Wed, 27 Nov 2024 15:07:28 +0200 Subject: [PATCH] feat: add silent and show-only-contract-logs flags --- SUPPORTED_APIS.md | 23 +++++ src/config/cli.rs | 14 ++- src/config/mod.rs | 29 ++++++ src/main.rs | 2 +- src/namespaces/config.rs | 10 ++ src/node/config_api.rs | 15 +++ src/node/in_memory.rs | 218 +++++++++++++++++++++------------------ src/observability.rs | 3 +- test_endpoints.http | 11 ++ 9 files changed, 223 insertions(+), 102 deletions(-) diff --git a/SUPPORTED_APIS.md b/SUPPORTED_APIS.md index 0206b109..62bcc8e3 100644 --- a/SUPPORTED_APIS.md +++ b/SUPPORTED_APIS.md @@ -39,6 +39,7 @@ The `status` options are: | [`CONFIG`](#config-namespace) | [`config_setShowStorageLogs`](#config_setshowstoragelogs) | `SUPPORTED` | Updates `show_storage_logs` to print storage log reads/writes | | [`CONFIG`](#config-namespace) | [`config_setShowVmDetails`](#config_setshowvmdetails) | `SUPPORTED` | Updates `show_vm_details` to print more detailed results from vm execution | | [`CONFIG`](#config-namespace) | [`config_setShowGasDetails`](#config_setshowgasdetails) | `SUPPORTED` | Updates `show_gas_details` to print more details about gas estimation and usage | +| [`CONFIG`](#config-namespace) | [`config_setShowOnlyContractLogs`](#config_setshowonlycontractlogs) | `SUPPORTED` | Updates `show_only_contract_logs` | | [`CONFIG`](#config-namespace) | [`config_setLogLevel`](#config_setloglevel) | `SUPPORTED` | Sets the logging level for the node and only displays the node logs. | | [`CONFIG`](#config-namespace) | [`config_setLogging`](#config_setlogging) | `SUPPORTED` | Sets the fine-tuned logging levels for the node and any of its dependencies | | [`DEBUG`](#debug-namespace) | [`debug_traceCall`](#debug_tracecall) | `SUPPORTED` | Performs a call and returns structured traces of the execution | @@ -359,6 +360,28 @@ curl --request POST \ --data '{"jsonrpc": "2.0","id": "1","method": "config_setResolveHashes","params": [true]}' ``` +### `config_setShowOnlyContractLogs` + +[source](src/node/config_api.rs) + +Updates `show_only_contract_logs` +#### Arguments + ++ `value: boolean` + +#### Status + +`SUPPORTED` + +#### Example + +```bash +curl --request POST \ + --url http://localhost:8011/ \ + --header 'content-type: application/json' \ + --data '{"jsonrpc": "2.0","id": "1","method": "config_setShowOnlyContractLogs","params": [true]}' +``` + ### `config_setLogLevel` [source](src/node/config_api.rs) diff --git a/src/config/cli.rs b/src/config/cli.rs index 9aa69e94..0329cd1b 100644 --- a/src/config/cli.rs +++ b/src/config/cli.rs @@ -70,7 +70,7 @@ pub struct Cli { /// Show call debug information. pub show_calls: Option, - #[arg(long, help_heading = "Debugging Options")] + #[arg(long, default_missing_value = "true", num_args(0..=1), help_heading = "Debugging Options")] /// Show call output information. pub show_outputs: Option, @@ -86,11 +86,15 @@ pub struct Cli { /// Show gas details information. pub show_gas_details: Option, - #[arg(long, help_heading = "Debugging Options")] + #[arg(long, default_missing_value = "true", num_args(0..=1), help_heading = "Debugging Options")] /// If true, the tool will try to resolve ABI and topic names for better readability. /// May decrease performance. pub resolve_hashes: Option, + #[arg(long, default_missing_value = "true", num_args(0..=1), help_heading = "Debugging Options")] + /// If true, the tool will only show contract logs. + pub show_only_contract_logs: Option, + // Gas Configuration #[arg(long, help_heading = "Gas Configuration")] /// Custom L1 gas price (in wei). @@ -138,6 +142,10 @@ pub struct Cli { /// Log file path (default: era_test_node.log). pub log_file_path: Option, + #[arg(long, default_missing_value = "true", num_args(0..=1), help_heading = "Logging Configuration")] + /// If true, the tool will not print anything on startup. + pub silent: Option, + // Cache Options #[arg(long, help_heading = "Cache Options")] /// Cache type (none, memory, or disk). Default: "disk". @@ -312,6 +320,8 @@ impl Cli { .with_gas_limit_scale(self.limit_scale_factor) .with_price_scale(self.price_scale_factor) .with_resolve_hashes(self.resolve_hashes) + .with_show_only_contract_logs(self.show_only_contract_logs) + .with_silent(self.silent) .with_system_contracts(self.dev_system_contracts) .with_override_bytecodes_dir(self.override_bytecodes_dir.clone()) // Added .with_log_level(self.log) diff --git a/src/config/mod.rs b/src/config/mod.rs index b5926e4b..92af47de 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -68,6 +68,10 @@ pub struct TestNodeConfig { pub show_gas_details: ShowGasDetails, /// Whether to resolve hash references pub resolve_hashes: bool, + /// Don’t print anything on startup if true + pub silent: bool, + /// Print only contract logs + pub show_only_contract_logs: bool, /// Configuration for system contracts pub system_contracts_options: system_contracts::Options, /// Directory to override bytecodes @@ -124,6 +128,8 @@ impl Default for TestNodeConfig { show_vm_details: Default::default(), show_gas_details: Default::default(), resolve_hashes: false, + show_only_contract_logs: false, + silent: false, system_contracts_options: Default::default(), override_bytecodes_dir: None, use_evm_emulator: false, @@ -170,6 +176,11 @@ impl TestNodeConfig { ) .expect("Failed writing json"); } + + if self.silent || self.show_only_contract_logs { + return; + } + let color = CustomColor::new(13, 71, 198); println!("{}", BANNER.custom_color(color)); @@ -576,6 +587,24 @@ impl TestNodeConfig { self } + /// Enable or disable silent mode + #[must_use] + pub fn with_silent(mut self, silent: Option) -> Self { + if let Some(silent) = silent { + self.silent = silent; + } + self + } + + /// Enable or disable printing only contract logs + #[must_use] + pub fn with_show_only_contract_logs(mut self, show_only_contract_logs: Option) -> Self { + if let Some(show_only_contract_logs) = show_only_contract_logs { + self.show_only_contract_logs = show_only_contract_logs; + } + self + } + /// Check if resolving hashes is enabled pub fn is_resolve_hashes_enabled(&self) -> bool { self.resolve_hashes diff --git a/src/main.rs b/src/main.rs index cc2b9663..a0f549d7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -118,7 +118,7 @@ async fn main() -> anyhow::Result<()> { // Initialize the tracing subscriber let observability = - Observability::init(vec!["era_test_node".into()], log_level_filter, log_file)?; + Observability::init(vec!["era_test_node".into()], log_level_filter, log_file, config.silent)?; // Use `Command::Run` as default. let command = command.as_ref().unwrap_or(&Command::Run); diff --git a/src/namespaces/config.rs b/src/namespaces/config.rs index 5cafbb2c..b0811078 100644 --- a/src/namespaces/config.rs +++ b/src/namespaces/config.rs @@ -85,6 +85,16 @@ pub trait ConfigurationApiNamespaceT { #[rpc(name = "config_setResolveHashes", returns = "bool")] fn config_set_resolve_hashes(&self, value: bool) -> Result; + /// Set show_only_contract_logs for the InMemoryNodeInner + /// + /// # Parameters + /// - `value`: A bool to update show_only_contract_logs to + /// + /// # Returns + /// The updated/current `show_only_contract_logs` value for the InMemoryNodeInner. + #[rpc(name = "config_setShowOnlyContractLogs", returns = "bool")] + fn config_set_show_only_contract_logs(&self, value: bool) -> Result; + /// Set the logging for the InMemoryNodeInner /// /// # Parameters diff --git a/src/node/config_api.rs b/src/node/config_api.rs index cbc03ccc..5022bbc5 100644 --- a/src/node/config_api.rs +++ b/src/node/config_api.rs @@ -183,6 +183,21 @@ impl Configurat }) } + fn config_set_show_only_contract_logs(&self, value: bool) -> Result { + self.get_inner() + .write() + .map_err(|err| { + tracing::error!("failed acquiring lock: {:?}", err); + into_jsrpc_error(Web3Error::InternalError(anyhow::Error::msg( + "Failed to acquire write lock for inner node state.", + ))) + }) + .map(|mut writer| { + writer.config.show_only_contract_logs = value; + writer.config.show_only_contract_logs + }) + } + fn config_set_log_level(&self, level: LogLevel) -> Result { let Some(observability) = &self.observability else { return Err(into_jsrpc_error(Web3Error::InternalError( diff --git a/src/node/in_memory.rs b/src/node/in_memory.rs index 9fef3219..563fccd5 100644 --- a/src/node/in_memory.rs +++ b/src/node/in_memory.rs @@ -1098,34 +1098,39 @@ impl InMemoryNode { .take() .unwrap_or_default(); - match &tx_result.result { - ExecutionResult::Success { output } => { - tracing::info!("Call: {}", "SUCCESS".green()); - let output_bytes = zksync_types::web3::Bytes::from(output.clone()); - tracing::info!("Output: {}", serde_json::to_string(&output_bytes).unwrap()); - } - ExecutionResult::Revert { output } => { - tracing::info!("Call: {}: {}", "FAILED".red(), output); - } - ExecutionResult::Halt { reason } => { - tracing::info!("Call: {} {}", "HALTED".red(), reason) - } - }; + if !inner.config.show_only_contract_logs { + match &tx_result.result { + ExecutionResult::Success { output } => { + tracing::info!("Call: {}", "SUCCESS".green()); + let output_bytes = zksync_types::web3::Bytes::from(output.clone()); + tracing::info!("Output: {}", serde_json::to_string(&output_bytes).unwrap()); + } + ExecutionResult::Revert { output } => { + tracing::info!("Call: {}: {}", "FAILED".red(), output); + } + ExecutionResult::Halt { reason } => { + tracing::info!("Call: {} {}", "HALTED".red(), reason) + } + }; + // Don't print the header if only contract calls should be shown + tracing::info!("=== Console Logs: "); + } - tracing::info!("=== Console Logs: "); for call in &call_traces { inner.console_log_handler.handle_call_recursive(call); } - tracing::info!("=== Call traces:"); - for call in &call_traces { - formatter::print_call( - call, - 0, - &inner.config.show_calls, - inner.config.show_outputs, - inner.config.resolve_hashes, - ); + if !inner.config.show_only_contract_logs { + tracing::info!("=== Call traces:"); + for call in &call_traces { + formatter::print_call( + call, + 0, + &inner.config.show_calls, + inner.config.show_outputs, + inner.config.resolve_hashes, + ); + } } Ok(tx_result.result) @@ -1390,85 +1395,92 @@ impl InMemoryNode { let spent_on_pubdata = tx_result.statistics.gas_used - tx_result.statistics.computational_gas_used as u64; - tracing::info!("┌─────────────────────────┐"); - tracing::info!("│ TRANSACTION SUMMARY │"); - tracing::info!("└─────────────────────────┘"); + if !inner.config.show_only_contract_logs { + tracing::info!("┌─────────────────────────┐"); + tracing::info!("│ TRANSACTION SUMMARY │"); + tracing::info!("└─────────────────────────┘"); - match &tx_result.result { - ExecutionResult::Success { .. } => tracing::info!("Transaction: {}", "SUCCESS".green()), - ExecutionResult::Revert { .. } => tracing::info!("Transaction: {}", "FAILED".red()), - ExecutionResult::Halt { .. } => tracing::info!("Transaction: {}", "HALTED".red()), - } + match &tx_result.result { + ExecutionResult::Success { .. } => tracing::info!("Transaction: {}", "SUCCESS".green()), + ExecutionResult::Revert { .. } => tracing::info!("Transaction: {}", "FAILED".red()), + ExecutionResult::Halt { .. } => tracing::info!("Transaction: {}", "HALTED".red()), + } - tracing::info!("Initiator: {:?}", tx.initiator_account()); - tracing::info!("Payer: {:?}", tx.payer()); - tracing::info!( - "Gas - Limit: {} | Used: {} | Refunded: {}", - to_human_size(tx.gas_limit()), - to_human_size(tx.gas_limit() - tx_result.refunds.gas_refunded), - to_human_size(tx_result.refunds.gas_refunded.into()) - ); + tracing::info!("Initiator: {:?}", tx.initiator_account()); + tracing::info!("Payer: {:?}", tx.payer()); + tracing::info!( + "Gas - Limit: {} | Used: {} | Refunded: {}", + to_human_size(tx.gas_limit()), + to_human_size(tx.gas_limit() - tx_result.refunds.gas_refunded), + to_human_size(tx_result.refunds.gas_refunded.into()) + ); - match inner.config.show_gas_details { - ShowGasDetails::None => tracing::info!( - "Use --show-gas-details flag or call config_setShowGasDetails to display more info" - ), - ShowGasDetails::All => { - let info = - self.display_detailed_gas_info(bootloader_debug_result.get(), spent_on_pubdata); - if info.is_err() { - tracing::info!( - "{}\nError: {}", - "!!! FAILED TO GET DETAILED GAS INFO !!!".to_owned().red(), - info.unwrap_err() - ); + match inner.config.show_gas_details { + ShowGasDetails::None => tracing::info!( + "Use --show-gas-details flag or call config_setShowGasDetails to display more info" + ), + ShowGasDetails::All => { + let info = + self.display_detailed_gas_info(bootloader_debug_result.get(), spent_on_pubdata); + if info.is_err() { + tracing::info!( + "{}\nError: {}", + "!!! FAILED TO GET DETAILED GAS INFO !!!".to_owned().red(), + info.unwrap_err() + ); + } } } - } - if inner.config.show_storage_logs != ShowStorageLogs::None { - print_storage_logs_details(&inner.config.show_storage_logs, &tx_result); - } + if inner.config.show_storage_logs != ShowStorageLogs::None { + print_storage_logs_details(&inner.config.show_storage_logs, &tx_result); + } + + if inner.config.show_vm_details != ShowVMDetails::None { + formatter::print_vm_details(&tx_result); + } - if inner.config.show_vm_details != ShowVMDetails::None { - formatter::print_vm_details(&tx_result); + // Don't print the header if only contract calls should be shown + tracing::info!(""); + tracing::info!("==== Console logs: "); } - tracing::info!(""); - tracing::info!("==== Console logs: "); for call in call_traces { inner.console_log_handler.handle_call_recursive(call); } - tracing::info!(""); - let call_traces_count = if !call_traces.is_empty() { - // All calls/sub-calls are stored within the first call trace - call_traces[0].calls.len() - } else { - 0 - }; - tracing::info!( - "==== {} Use --show-calls flag or call config_setShowCalls to display more info.", - format!("{:?} call traces. ", call_traces_count).bold() - ); - if inner.config.show_calls != ShowCalls::None { - for call in call_traces { - formatter::print_call( - call, - 0, - &inner.config.show_calls, - inner.config.show_outputs, - inner.config.resolve_hashes, - ); + if !inner.config.show_only_contract_logs { + tracing::info!(""); + let call_traces_count = if !call_traces.is_empty() { + // All calls/sub-calls are stored within the first call trace + call_traces[0].calls.len() + } else { + 0 + }; + tracing::info!( + "==== {} Use --show-calls flag or call config_setShowCalls to display more info.", + format!("{:?} call traces. ", call_traces_count).bold() + ); + + if inner.config.show_calls != ShowCalls::None { + for call in call_traces { + formatter::print_call( + call, + 0, + &inner.config.show_calls, + inner.config.show_outputs, + inner.config.resolve_hashes, + ); + } + } + tracing::info!(""); + tracing::info!( + "==== {}", + format!("{} events", tx_result.logs.events.len()).bold() + ); + for event in &tx_result.logs.events { + formatter::print_event(event, inner.config.resolve_hashes); } - } - tracing::info!(""); - tracing::info!( - "==== {}", - format!("{} events", tx_result.logs.events.len()).bold() - ); - for event in &tx_result.logs.events { - formatter::print_event(event, inner.config.resolve_hashes); } let mut bytecodes = HashMap::new(); @@ -1498,21 +1510,31 @@ impl InMemoryNode { let tx_hash = l2_tx.hash(); let transaction_type = l2_tx.common_data.transaction_type; - tracing::info!(""); - tracing::info!("Validating {}", format!("{:?}", tx_hash).bold()); + let disable_tracing = { + let inner = self + .inner + .read() + .map_err(|_| anyhow::anyhow!("Failed to acquire read lock"))?; + inner.config.silent || inner.config.show_only_contract_logs + }; - self.validate_tx(&l2_tx)?; + if !disable_tracing { + tracing::info!(""); + tracing::info!("Validating {}", format!("{:?}", tx_hash).bold()); + } - tracing::info!("Executing {}", format!("{:?}", tx_hash).bold()); + self.validate_tx(&l2_tx)?; - { - let mut inner = self - .inner - .write() - .map_err(|_| anyhow::anyhow!("Failed to acquire write lock"))?; - inner.filters.notify_new_pending_transaction(tx_hash); + if !disable_tracing { + tracing::info!("Executing {}", format!("{:?}", tx_hash).bold()); } + self + .inner + .write() + .map_err(|_| anyhow::anyhow!("Failed to acquire write lock"))? + .filters.notify_new_pending_transaction(tx_hash); + let TxExecutionOutput { result, bytecodes, diff --git a/src/observability.rs b/src/observability.rs index 4dd933bb..888916de 100644 --- a/src/observability.rs +++ b/src/observability.rs @@ -58,13 +58,14 @@ impl Observability { binary_names: Vec, log_level_filter: LevelFilter, log_file: File, + disabled: bool, ) -> Result { let directives = binary_names .iter() .map(|x| format!("{}={}", x, log_level_filter.to_string().to_lowercase())) .collect::>() .join(","); - let filter = Self::parse_filter(&directives)?; + let filter = if disabled { EnvFilter::new("off") } else { Self::parse_filter(&directives)? }; let (filter, reload_handle) = reload::Layer::new(filter); let timer_format = diff --git a/test_endpoints.http b/test_endpoints.http index d07cd668..33d8104d 100644 --- a/test_endpoints.http +++ b/test_endpoints.http @@ -156,6 +156,17 @@ content-type: application/json POST http://localhost:8011 content-type: application/json +{ + "jsonrpc": "2.0", + "id": "1", + "method": "config_setShowOnlyContractLogs", + "params": [true] +} + +### +POST http://localhost:8011 +content-type: application/json + { "jsonrpc": "2.0", "id": "1",