Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add telemetry #589

Merged
merged 23 commits into from
Feb 20, 2025
Merged
Show file tree
Hide file tree
Changes from 19 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
416 changes: 368 additions & 48 deletions Cargo.lock

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ zksync_contracts = { git = "https://github.com/matter-labs/zksync-era", tag = "c
zksync_types = { git = "https://github.com/matter-labs/zksync-era", tag = "core-v26.4.0" }
zksync_vm_interface = { git = "https://github.com/matter-labs/zksync-era", tag = "core-v26.4.0" }
zksync_web3_decl = { git = "https://github.com/matter-labs/zksync-era", tag = "core-v26.4.0" }
zksync_telemetry = { git = "https://github.com/matter-labs/zksync-telemetry.git", branch = "refactoring" }

#########################
# External dependencies #
Expand Down
3 changes: 3 additions & 0 deletions crates/api_server/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,10 @@ anvil_zksync_common.workspace = true
zksync_types.workspace = true
zksync_web3_decl.workspace = true
zksync_error.workspace = true
zksync_telemetry.workspace = true

anyhow.workspace = true
futures.workspace = true
hex.workspace = true
http.workspace = true
jsonrpsee.workspace = true
Expand All @@ -30,3 +32,4 @@ tokio.workspace = true
tower.workspace = true
tower-http.workspace = true
tracing.workspace = true
serde_json.workspace = true
44 changes: 42 additions & 2 deletions crates/api_server/src/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,17 @@ use anvil_zksync_api_decl::{
};
use anvil_zksync_core::node::InMemoryNode;
use anvil_zksync_l1_sidecar::L1Sidecar;
use futures::future::BoxFuture;
use futures::FutureExt;
use http::Method;
use jsonrpsee::server::middleware::http::ProxyGetRequestLayer;
use jsonrpsee::server::{RpcServiceBuilder, ServerBuilder, ServerHandle};
use jsonrpsee::server::middleware::rpc::RpcServiceT;
use jsonrpsee::server::{MethodResponse, RpcServiceBuilder, ServerBuilder, ServerHandle};
use jsonrpsee::types::Request;
use jsonrpsee::RpcModule;
use std::net::SocketAddr;
use tower_http::cors::{AllowOrigin, CorsLayer};
use zksync_telemetry::{get_telemetry, TelemetryProps};

#[derive(Clone)]
pub struct NodeServerBuilder {
Expand Down Expand Up @@ -89,7 +94,10 @@ impl NodeServerBuilder {
.layer(cors_layers)
.layer(health_api_layer),
)
.set_rpc_middleware(RpcServiceBuilder::new().rpc_logger(100));
.set_rpc_middleware(RpcServiceBuilder::new().rpc_logger(100))
.set_rpc_middleware(
RpcServiceBuilder::new().layer_fn(move |service| TelemetryReporter { service }),
);

match server_builder.build(addr).await {
Ok(server) => {
Expand Down Expand Up @@ -127,3 +135,35 @@ impl NodeServer {
(self.run_fn)()
}
}

#[derive(Clone)]
pub struct TelemetryReporter<S> {
service: S,
}

impl<'a, S> RpcServiceT<'a> for TelemetryReporter<S>
where
S: RpcServiceT<'a> + Send + Sync + Clone + 'static,
{
type Future = BoxFuture<'a, MethodResponse>;

fn call(&self, req: Request<'a>) -> Self::Future {
let service = self.service.clone();
let telemetry = get_telemetry().expect("telemetry is not initialized");

async move {
let method = req.method_name();
// Report only anvil and config API usage
if method.starts_with("anvil_") || method.starts_with("config_") {
let _ = telemetry
.track_event(
"rpc_call",
TelemetryProps::new().insert("method", Some(method)).take(),
)
.await;
}
service.call(req).await
}
.boxed()
}
}
3 changes: 2 additions & 1 deletion crates/cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ zksync_error.workspace = true
anvil_zksync_common.workspace = true

zksync_types.workspace = true

zksync_telemetry.workspace = true
alloy = { workspace = true, default-features = false, features = ["signer-mnemonic", "reqwest-rustls-tls"] }
anyhow.workspace = true
clap.workspace = true
Expand All @@ -32,6 +32,7 @@ futures.workspace = true
rand.workspace = true
serde.workspace = true
serde_json.workspace = true
serde_with.workspace = true
tokio.workspace = true
tracing.workspace = true
tracing-subscriber.workspace = true
Expand Down
212 changes: 204 additions & 8 deletions crates/cli/src/cli.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
use crate::utils::parse_genesis_file;
use crate::utils::{
get_cli_command_telemetry_props, parse_genesis_file, TELEMETRY_SENSITIVE_VALUE,
};
use alloy::signers::local::coins_bip39::{English, Mnemonic};
use anvil_zksync_common::{sh_eprintln, sh_err};
use anvil_zksync_config::constants::{
Expand Down Expand Up @@ -34,8 +36,16 @@ use std::{
};
use tokio::time::{Instant, Interval};
use url::Url;
use zksync_telemetry::TelemetryProps;
use zksync_types::{H256, U256};

const DEFAULT_PORT: &str = "8011";
const DEFAULT_HOST: &str = "0.0.0.0";
const DEFAULT_ACCOUNTS: &str = "10";
const DEFAULT_BALANCE: &str = "10000";
const DEFAULT_ALLOW_ORIGIN: &str = "*";
const DEFAULT_TX_ORDER: &str = "fifo";

#[derive(Debug, Parser, Clone)]
#[command(
author = "Matter Labs",
Expand All @@ -62,7 +72,7 @@ pub struct Cli {
#[arg(long, value_name = "OUT_FILE", help_heading = "General Options")]
pub config_out: Option<String>,

#[arg(long, default_value = "8011", help_heading = "Network Options")]
#[arg(long, default_value = DEFAULT_PORT, help_heading = "Network Options")]
/// Port to listen on (default: 8011).
pub port: Option<u16>,

Expand All @@ -71,7 +81,7 @@ pub struct Cli {
long,
value_name = "IP_ADDR",
env = "ANVIL_ZKSYNC_IP_ADDR",
default_value = "0.0.0.0",
default_value = DEFAULT_HOST,
value_delimiter = ',',
help_heading = "Network Options"
)]
Expand Down Expand Up @@ -200,7 +210,7 @@ pub struct Cli {
#[arg(
long,
short,
default_value = "10",
default_value = DEFAULT_ACCOUNTS,
value_name = "NUM",
help_heading = "Account Configuration"
)]
Expand All @@ -209,7 +219,7 @@ pub struct Cli {
/// The balance of every dev account in Ether.
#[arg(
long,
default_value = "10000",
default_value = DEFAULT_BALANCE,
value_name = "NUM",
help_heading = "Account Configuration"
)]
Expand Down Expand Up @@ -306,15 +316,15 @@ pub struct Cli {
pub no_mining: bool,

/// The cors `allow_origin` header
#[arg(long, default_value = "*", help_heading = "Server options")]
#[arg(long, default_value = DEFAULT_ALLOW_ORIGIN, help_heading = "Server options")]
pub allow_origin: String,

/// Disable CORS.
#[arg(long, conflicts_with = "allow_origin", help_heading = "Server options")]
pub no_cors: bool,

/// Transaction ordering in the mempool.
#[arg(long, default_value = "fifo")]
#[arg(long, default_value = DEFAULT_TX_ORDER)]
pub order: TransactionOrder,

/// Enable L1 support.
Expand Down Expand Up @@ -551,6 +561,147 @@ impl Cli {
Ok(config)
}

pub fn into_telemetry_props(self) -> TelemetryProps {
TelemetryProps::new()
.insert("command", get_cli_command_telemetry_props(self.command))
.insert_with("offline", self.offline, |v| v.then_some(v))
.insert_with("health_check_endpoint", self.health_check_endpoint, |v| {
v.then_some(v)
})
.insert_with("config_out", self.config_out, |v| {
v.map(|_| TELEMETRY_SENSITIVE_VALUE)
})
.insert_with("port", self.port, |v| {
v.filter(|&p| p.to_string() != DEFAULT_PORT)
.map(|_| TELEMETRY_SENSITIVE_VALUE)
})
.insert_with("host", &self.host, |v| {
v.first()
.filter(|&h| h.to_string() != DEFAULT_HOST || self.host.len() != 1)
.map(|_| TELEMETRY_SENSITIVE_VALUE)
})
.insert_with("chain_id", self.chain_id, |v| {
v.map(|_| TELEMETRY_SENSITIVE_VALUE)
})
.insert_with("debug_mode", self.debug_mode, |v| v.then_some(v))
.insert_with("show_node_config", self.show_node_config, |v| {
(!v.unwrap_or(false)).then_some(false)
})
.insert_with("show_tx_summary", self.show_tx_summary, |v| {
(!v.unwrap_or(false)).then_some(false)
})
.insert("disable_console_log", self.disable_console_log)
.insert("show_event_logs", self.show_event_logs)
.insert("show_calls", self.show_calls.map(|v| v.to_string()))
.insert("show_outputs", self.show_outputs)
.insert(
"show_storage_logs",
self.show_storage_logs.map(|v| v.to_string()),
)
.insert(
"show_vm_details",
self.show_vm_details.map(|v| v.to_string()),
)
.insert(
"show_gas_details",
self.show_gas_details.map(|v| v.to_string()),
)
.insert("resolve_hashes", self.resolve_hashes)
.insert(
"l1_gas_price",
self.l1_gas_price.map(serde_json::Number::from),
)
.insert(
"l2_gas_price",
self.l2_gas_price.map(serde_json::Number::from),
)
.insert(
"l1_pubdata_price",
self.l1_pubdata_price.map(serde_json::Number::from),
)
.insert(
"price_scale_factor",
self.price_scale_factor.map(|v| {
serde_json::Number::from_f64(v).unwrap_or(serde_json::Number::from(0))
}),
)
.insert(
"limit_scale_factor",
self.limit_scale_factor.map(|v| {
serde_json::Number::from_f64(v as f64).unwrap_or(serde_json::Number::from(0))
}),
)
.insert_with("override_bytecodes_dir", self.override_bytecodes_dir, |v| {
v.map(|_| TELEMETRY_SENSITIVE_VALUE)
})
.insert(
"dev_system_contracts",
self.dev_system_contracts.map(|v| format!("{:?}", v)),
)
.insert_with("emulate_evm", self.emulate_evm, |v| v.then_some(v))
.insert("log", self.log.map(|v| v.to_string()))
.insert_with("log_file_path", self.log_file_path, |v| {
v.map(|_| TELEMETRY_SENSITIVE_VALUE)
})
.insert("silent", self.silent)
.insert("cache", self.cache.map(|v| format!("{:?}", v)))
.insert("reset_cache", self.reset_cache)
.insert_with("cache_dir", self.cache_dir, |v| {
v.map(|_| TELEMETRY_SENSITIVE_VALUE)
})
.insert_with("accounts", self.accounts, |v| {
(v.to_string() != DEFAULT_ACCOUNTS).then_some(serde_json::Number::from(v))
})
.insert_with("balance", self.balance, |v| {
(v.to_string() != DEFAULT_BALANCE).then_some(serde_json::Number::from(v))
})
.insert("timestamp", self.timestamp.map(serde_json::Number::from))
.insert_with("init", self.init, |v| v.map(|_| TELEMETRY_SENSITIVE_VALUE))
.insert_with("state", self.state, |v| {
v.map(|_| TELEMETRY_SENSITIVE_VALUE)
})
.insert(
"state_interval",
self.state_interval.map(serde_json::Number::from),
)
.insert_with("dump_state", self.dump_state, |v| {
v.map(|_| TELEMETRY_SENSITIVE_VALUE)
})
.insert_with(
"preserve_historical_states",
self.preserve_historical_states,
|v| v.then_some(v),
)
.insert_with("load_state", self.load_state, |v| {
v.map(|_| TELEMETRY_SENSITIVE_VALUE)
})
.insert_with("mnemonic", self.mnemonic, |v| {
v.map(|_| TELEMETRY_SENSITIVE_VALUE)
})
.insert_with("mnemonic_random", self.mnemonic_random, |v| {
v.map(|_| TELEMETRY_SENSITIVE_VALUE)
})
.insert_with("mnemonic_seed", self.mnemonic_seed, |v| {
v.map(|_| TELEMETRY_SENSITIVE_VALUE)
})
.insert_with("derivation_path", self.derivation_path, |v| {
v.map(|_| TELEMETRY_SENSITIVE_VALUE)
})
.insert_with("auto_impersonate", self.auto_impersonate, |v| {
v.then_some(v)
})
.insert("block_time", self.block_time.map(|v| format!("{:?}", v)))
.insert_with("no_mining", self.no_mining, |v| v.then_some(v))
.insert_with("allow_origin", self.allow_origin, |v| {
(v != DEFAULT_ALLOW_ORIGIN).then_some(TELEMETRY_SENSITIVE_VALUE)
})
.insert_with("no_cors", self.no_cors, |v| v.then_some(v))
.insert_with("order", self.order, |v| {
(v.to_string() != DEFAULT_TX_ORDER).then_some(v.to_string())
})
.take()
}

fn account_generator(&self) -> AccountGenerator {
let mut gen = AccountGenerator::new(self.accounts as usize)
.phrase(DEFAULT_MNEMONIC)
Expand Down Expand Up @@ -709,7 +860,7 @@ mod tests {
use super::Cli;
use anvil_zksync_core::node::InMemoryNode;
use clap::Parser;
use serde_json::Value;
use serde_json::{json, Value};
use std::{
env,
net::{IpAddr, Ipv4Addr},
Expand Down Expand Up @@ -854,4 +1005,49 @@ mod tests {

Ok(())
}

#[tokio::test]
async fn test_cli_telemetry_data_skips_missing_args() -> anyhow::Result<()> {
let args = Cli::parse_from(["anvil-zksync"]).into_telemetry_props();
let json = args.to_inner();
let expected_json: serde_json::Value = json!({});
assert_eq!(json, expected_json);
Ok(())
}

#[tokio::test]
async fn test_cli_telemetry_data_hides_sensitive_data() -> anyhow::Result<()> {
let args = Cli::parse_from([
"anvil-zksync",
"--offline",
"--host",
"100.100.100.100",
"--port",
"3333",
"--chain-id",
"123",
"--config-out",
"/some/path",
"fork",
"--fork-url",
"mainnet",
])
.into_telemetry_props();
let json = args.to_inner();
let expected_json: serde_json::Value = json!({
"command": {
"args": {
"fork_url": "Mainnet"
},
"name": "fork"
},
"offline": true,
"config_out": "***",
"port": "***",
"host": "***",
"chain_id": "***"
});
assert_eq!(json, expected_json);
Ok(())
}
}
Loading