diff --git a/polkadot/cli/Cargo.toml b/polkadot/cli/Cargo.toml index 8cc19b0f66..edb9da5f91 100644 --- a/polkadot/cli/Cargo.toml +++ b/polkadot/cli/Cargo.toml @@ -12,6 +12,7 @@ log = "0.3" atty = "0.2" regex = "0.2" time = "0.1" +slog = "^2" ansi_term = "0.10" lazy_static = "1.0" hex-literal = "0.1" @@ -28,6 +29,7 @@ substrate-state-machine = { path = "../../substrate/state-machine" } substrate-rpc = { path = "../../substrate/rpc" } substrate-rpc-servers = { path = "../../substrate/rpc-servers" } substrate-network = { path = "../../substrate/network" } +substrate-telemetry = { path = "../../substrate/telemetry" } polkadot-primitives = { path = "../primitives" } polkadot-service = { path = "../service" } polkadot-transaction-pool = { path = "../transaction-pool" } diff --git a/polkadot/cli/src/cli.yml b/polkadot/cli/src/cli.yml index 3f37d79239..1d5798378e 100644 --- a/polkadot/cli/src/cli.yml +++ b/polkadot/cli/src/cli.yml @@ -67,4 +67,19 @@ args: value_name: CHAIN_SPEC help: Specify the chain specification (one of dev, local or poc-2) takes_value: true + - name: + long: name + value_name: NAME + help: The human-readable name for this node, as reported to the telemetry server, if enabled + takes_value: true + - telemetry: + short: t + long: telemetry + help: Should connect to the Polkadot telemetry server (off by default) + takes_value: false + - telemetry-url: + long: telemetry-url + value_name: TELEMETRY_URL + help: The URL of the telemetry server. Implies --telemetry. + takes_value: true subcommands: diff --git a/polkadot/cli/src/informant.rs b/polkadot/cli/src/informant.rs index c5bbe0c2a2..dab75e7a9e 100644 --- a/polkadot/cli/src/informant.rs +++ b/polkadot/cli/src/informant.rs @@ -39,18 +39,22 @@ pub fn start(service: &Service, handle: reactor::Handle) let network = service.network(); let client = service.client(); + let txpool = service.transaction_pool(); let display_notifications = interval.map_err(|e| debug!("Timer error: {:?}", e)).for_each(move |_| { let sync_status = network.status(); if let Ok(best_block) = client.best_block_header() { let hash = best_block.hash(); + let num_peers = sync_status.num_peers; let status = match (sync_status.sync.state, sync_status.sync.best_seen_block) { (SyncState::Idle, _) => "Idle".into(), (SyncState::Downloading, None) => "Syncing".into(), (SyncState::Downloading, Some(n)) => format!("Syncing, target=#{}", n), }; - info!(target: "polkadot", "{} ({} peers), best: #{} ({})", status, sync_status.num_peers, best_block.number, hash) + let txpool_status = txpool.light_status(); + info!(target: "polkadot", "{} ({} peers), best: #{} ({})", status, sync_status.num_peers, best_block.number, hash); + telemetry!("system.interval"; "status" => status, "peers" => num_peers, "height" => best_block.number, "best" => ?hash, "txcount" => txpool_status.transaction_count); } else { warn!("Error getting best block information"); } @@ -60,10 +64,18 @@ pub fn start(service: &Service, handle: reactor::Handle) let client = service.client(); let display_block_import = client.import_notification_stream().for_each(|n| { info!(target: "polkadot", "Imported #{} ({})", n.header.number, n.hash); + telemetry!("block.import"; "height" => n.header.number, "best" => ?n.hash); Ok(()) }); + let txpool = service.transaction_pool(); + let display_txpool_import = txpool.import_notification_stream().for_each(move |_| { + let status = txpool.light_status(); + telemetry!("txpool.import"; "mem_usage" => status.mem_usage, "count" => status.transaction_count, "sender" => status.senders); + Ok(()) + }); handle.spawn(display_notifications); handle.spawn(display_block_import); + handle.spawn(display_txpool_import); } diff --git a/polkadot/cli/src/lib.rs b/polkadot/cli/src/lib.rs index 0d56f0d11e..bcba3c67e7 100644 --- a/polkadot/cli/src/lib.rs +++ b/polkadot/cli/src/lib.rs @@ -39,6 +39,10 @@ extern crate substrate_rpc; extern crate substrate_rpc_servers as rpc; extern crate polkadot_primitives; extern crate polkadot_service as service; +#[macro_use] +extern crate slog; // needed until we can reexport `slog_info` from `substrate_telemetry` +#[macro_use] +extern crate substrate_telemetry; extern crate polkadot_transaction_pool as txpool; #[macro_use] @@ -57,11 +61,14 @@ use std::io; use std::net::SocketAddr; use std::path::{Path, PathBuf}; use polkadot_primitives::Block; +use substrate_telemetry::{init_telemetry, TelemetryConfig}; use futures::sync::mpsc; use futures::{Sink, Future, Stream}; use tokio_core::reactor; -use service::ChainSpec; +use service::{OptionChainSpec, ChainSpec}; + +const DEFAULT_TELEMETRY_URL: &str = "wss://telemetry.polkadot.io:443"; struct Configuration(service::Configuration); @@ -113,8 +120,36 @@ pub fn run(args: I) -> error::Result<()> where init_logger(log_pattern); fdlimit::raise_fd_limit(); + info!("Parity ·:· Polkadot"); + info!(" version {}", crate_version!()); + info!(" by Parity Technologies, 2017, 2018"); + let mut config = service::Configuration::default(); + if let Some(name) = matches.value_of("name") { + config.name = name.into(); + info!("Node name: {}", config.name); + } + + let _guard = if matches.is_present("telemetry") || matches.value_of("telemetry-url").is_some() { + let name = config.name.clone(); + let chain = config.chain_spec.clone(); + Some(init_telemetry(TelemetryConfig { + url: matches.value_of("telemetry-url").unwrap_or(DEFAULT_TELEMETRY_URL).into(), + on_connect: Box::new(move || { + telemetry!("system.connected"; + "name" => name.clone(), + "implementation" => "parity-polkadot", + "version" => crate_version!(), + "config" => "", + "chain" => <&'static str>::from(chain) + ); + }), + })) + } else { + None + }; + let base_path = matches.value_of("base-path") .map(|x| Path::new(x).to_owned()) .unwrap_or_else(default_base_path); @@ -129,28 +164,24 @@ pub fn run(args: I) -> error::Result<()> where let mut role = service::Role::FULL; if matches.is_present("collator") { - info!("Starting collator."); + info!("Starting collator"); role = service::Role::COLLATOR; } else if matches.is_present("validator") { - info!("Starting validator."); + info!("Starting validator"); role = service::Role::VALIDATOR; } else if matches.is_present("light") { - info!("Starting light."); + info!("Starting (light)"); role = service::Role::LIGHT; + } else { + info!("Starting (heavy)"); } match matches.value_of("chain") { - Some("dev") => config.chain_spec = ChainSpec::Development, - Some("local") => config.chain_spec = ChainSpec::LocalTestnet, - Some("poc-2") => config.chain_spec = ChainSpec::PoC2Testnet, None => (), - Some(unknown) => panic!("Invalid chain name: {}", unknown), + Some(n) => config.chain_spec = OptionChainSpec::from(n).inner() + .unwrap_or_else(|| panic!("Invalid chain name: {}", n)), } - info!("Chain specification: {}", match config.chain_spec { - ChainSpec::Development => "Development", - ChainSpec::LocalTestnet => "Local Testnet", - ChainSpec::PoC2Testnet => "PoC-2 Testnet", - }); + info!("Chain specification: {}", config.chain_spec); config.roles = role; { diff --git a/polkadot/runtime/src/lib.rs b/polkadot/runtime/src/lib.rs index 9d9b3f9a59..ffeb5d48f2 100644 --- a/polkadot/runtime/src/lib.rs +++ b/polkadot/runtime/src/lib.rs @@ -41,7 +41,6 @@ extern crate hex_literal; #[cfg(test)] extern crate substrate_serializer; -#[cfg_attr(feature = "std", macro_use)] extern crate substrate_primitives; #[macro_use] diff --git a/polkadot/service/Cargo.toml b/polkadot/service/Cargo.toml index 4bc62b271a..c9cd65c6d7 100644 --- a/polkadot/service/Cargo.toml +++ b/polkadot/service/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "polkadot-service" -version = "0.1.0" +version = "0.2.0" authors = ["Parity Technologies "] [dependencies] @@ -9,7 +9,10 @@ futures = "0.1.17" parking_lot = "0.4" tokio-timer = "0.1.2" error-chain = "0.11" +lazy_static = "1.0" log = "0.3" +slog = "^2" +clap = "2.27" tokio-core = "0.1.12" exit-future = "0.1" ed25519 = { path = "../../substrate/ed25519" } @@ -28,3 +31,4 @@ substrate-client-db = { path = "../../substrate/client/db" } substrate-codec = { path = "../../substrate/codec" } substrate-executor = { path = "../../substrate/executor" } substrate-state-machine = { path = "../../substrate/state-machine" } +substrate-telemetry = { path = "../../substrate/telemetry" } diff --git a/polkadot/service/src/config.rs b/polkadot/service/src/config.rs index e5c27ef776..46127a8580 100644 --- a/polkadot/service/src/config.rs +++ b/polkadot/service/src/config.rs @@ -22,7 +22,7 @@ pub use network::NetworkConfiguration; /// The chain specification (this should eventually be replaced by a more general JSON-based chain /// specification). -#[derive(Clone, Copy)] +#[derive(Clone, Copy, Debug)] pub enum ChainSpec { /// Whatever the current runtime is, with just Alice as an auth. Development, @@ -32,6 +32,47 @@ pub enum ChainSpec { PoC2Testnet, } +/// Synonym for Option because we cannot `impl From<..> for Option` +pub struct OptionChainSpec(Option); + +impl OptionChainSpec { + /// Return the inner part. + pub fn inner(self) -> Option { + self.0 + } +} + +impl<'a> From<&'a str> for OptionChainSpec { + fn from(s: &'a str) -> Self { + OptionChainSpec(Some(match s { + "dev" => ChainSpec::Development, + "local" => ChainSpec::LocalTestnet, + "poc-2" => ChainSpec::PoC2Testnet, + _ => return OptionChainSpec(None), + })) + } +} + +impl From for &'static str { + fn from(s: ChainSpec) -> &'static str { + match s { + ChainSpec::Development => "dev", + ChainSpec::LocalTestnet => "local", + ChainSpec::PoC2Testnet => "poc-2", + } + } +} + +impl ::std::fmt::Display for ChainSpec { + fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { + write!(f, "{}", match *self { + ChainSpec::Development => "Development", + ChainSpec::LocalTestnet => "Local Testnet", + ChainSpec::PoC2Testnet => "PoC-2 Testnet", + }) + } +} + /// Service configuration. #[derive(Clone)] pub struct Configuration { @@ -49,6 +90,10 @@ pub struct Configuration { pub keys: Vec, /// Chain specification. pub chain_spec: ChainSpec, + /// Telemetry server URL, optional - only `Some` if telemetry reporting is enabled + pub telemetry: Option, + /// Node name. + pub name: String, } impl Default for Configuration { @@ -61,6 +106,8 @@ impl Default for Configuration { database_path: Default::default(), keys: Default::default(), chain_spec: ChainSpec::Development, + telemetry: Default::default(), + name: "Anonymous".into(), } } } diff --git a/polkadot/service/src/lib.rs b/polkadot/service/src/lib.rs index 35b72cd5d3..44f7e714be 100644 --- a/polkadot/service/src/lib.rs +++ b/polkadot/service/src/lib.rs @@ -19,8 +19,8 @@ extern crate futures; extern crate ed25519; +extern crate clap; extern crate exit_future; -extern crate parking_lot; extern crate tokio_timer; extern crate polkadot_primitives; extern crate polkadot_runtime; @@ -40,9 +40,13 @@ extern crate tokio_core; extern crate substrate_client as client; extern crate substrate_client_db as client_db; +#[macro_use] +extern crate substrate_telemetry; #[macro_use] extern crate error_chain; #[macro_use] +extern crate slog; // needed until we can reexport `slog_info` from `substrate_telemetry` +#[macro_use] extern crate log; #[macro_use] extern crate hex_literal; @@ -71,7 +75,7 @@ use network::ManageNetwork; use exit_future::Signal; pub use self::error::{ErrorKind, Error}; -pub use config::{Configuration, Role, ChainSpec}; +pub use config::{Configuration, Role, OptionChainSpec, ChainSpec}; type CodeExecutor = NativeExecutor; @@ -410,7 +414,8 @@ impl Service let (client, on_demand) = client_creator(db_settings, executor, genesis_builder)?; let api = api_creator(client.clone()); let best_header = client.best_block_header()?; - info!("Starting Polkadot. Best block is #{}", best_header.number); + info!("Best block is #{}", best_header.number); + telemetry!("node.start"; "height" => best_header.number, "best" => ?best_header.hash()); let transaction_pool = Arc::new(TransactionPool::new(config.transaction_pool)); let transaction_pool_adapter = Arc::new(TransactionPoolAdapter { pool: transaction_pool.clone(), @@ -430,6 +435,7 @@ impl Service let barrier = ::std::sync::Arc::new(Barrier::new(2)); on_demand.map(|on_demand| on_demand.set_service_link(Arc::downgrade(&network))); + let thread = { let client = client.clone(); let network = network.clone(); @@ -445,11 +451,11 @@ impl Service // block notifications let network1 = network.clone(); let txpool1 = txpool.clone(); + let events = client.import_notification_stream() .for_each(move |notification| { network1.on_block_imported(notification.hash, ¬ification.header); prune_imported(&*api, &*txpool1, notification.hash); - Ok(()) }); core.handle().spawn(events);