From a21f2a099874b21d13ea460a18215d806625b468 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Tue, 19 Apr 2016 09:58:26 +0200 Subject: [PATCH 1/3] Exposing loggin via RPC --- parity/main.rs | 34 +++++++++++++++++++-------- rpc/src/v1/impls/ethcore.rs | 19 ++++++++++++--- rpc/src/v1/traits/ethcore.rs | 8 +++++++ util/src/log.rs | 45 ++++++++++++++++++++++++++++++++++++ 4 files changed, 94 insertions(+), 12 deletions(-) diff --git a/parity/main.rs b/parity/main.rs index 8e9c79b27..e5e1da864 100644 --- a/parity/main.rs +++ b/parity/main.rs @@ -278,30 +278,40 @@ struct Args { flag_networkid: Option, } -fn setup_log(init: &Option) { +fn setup_log(init: &Option) -> Arc { use rlog::*; + let mut levels = String::new(); let mut builder = LogBuilder::new(); builder.filter(None, LogLevelFilter::Info); if env::var("RUST_LOG").is_ok() { - builder.parse(&env::var("RUST_LOG").unwrap()); + let lvl = &env::var("RUST_LOG").unwrap(); + levels.push_str(&lvl); + levels.push_str(","); + builder.parse(lvl); } if let Some(ref s) = *init { + levels.push_str(s); builder.parse(s); } - let format = |record: &LogRecord| { + let logs = Arc::new(RotatingLogger::new(levels)); + let log2 = logs.clone(); + let format = move |record: &LogRecord| { let timestamp = time::strftime("%Y-%m-%d %H:%M:%S %Z", &time::now()).unwrap(); - if max_log_level() <= LogLevelFilter::Info { + let format = if max_log_level() <= LogLevelFilter::Info { format!("{}{}", timestamp, record.args()) } else { format!("{}{}:{}: {}", timestamp, record.level(), record.target(), record.args()) - } + }; + log2.append(format.clone()); + format }; builder.format(format); builder.init().unwrap(); + logs } #[cfg(feature = "rpc")] @@ -313,6 +323,7 @@ fn setup_rpc_server( url: &SocketAddr, cors_domain: Option, apis: Vec<&str>, + logger: Arc, ) -> RpcServer { use rpc::v1::*; @@ -326,7 +337,7 @@ fn setup_rpc_server( server.add_delegate(EthFilterClient::new(&client, &miner).to_delegate()); }, "personal" => server.add_delegate(PersonalClient::new(&secret_store).to_delegate()), - "ethcore" => server.add_delegate(EthcoreClient::new(&miner).to_delegate()), + "ethcore" => server.add_delegate(EthcoreClient::new(&miner, logger.clone()).to_delegate()), _ => { die!("{}: Invalid API name to be enabled.", api); }, @@ -348,6 +359,7 @@ fn setup_webapp_server( miner: Arc, url: &SocketAddr, auth: Option<(String, String)>, + logger: Arc, ) -> WebappServer { use rpc::v1::*; @@ -357,7 +369,7 @@ fn setup_webapp_server( server.add_delegate(EthClient::new(&client, &sync, &secret_store, &miner).to_delegate()); server.add_delegate(EthFilterClient::new(&client, &miner).to_delegate()); server.add_delegate(PersonalClient::new(&secret_store).to_delegate()); - server.add_delegate(EthcoreClient::new(&miner).to_delegate()); + server.add_delegate(EthcoreClient::new(&miner, logger).to_delegate()); let start_result = match auth { None => { server.start_unsecure_http(url) @@ -386,6 +398,7 @@ fn setup_rpc_server( _url: &SocketAddr, _cors_domain: Option, _apis: Vec<&str>, + _logger: Arc, ) -> ! { die!("Your Parity version has been compiled without JSON-RPC support.") } @@ -401,6 +414,7 @@ fn setup_webapp_server( _miner: Arc, _url: &SocketAddr, _auth: Option<(String, String)>, + _logger: Arc, ) -> ! { die!("Your Parity version has been compiled without WebApps support.") } @@ -687,7 +701,7 @@ impl Configuration { let panic_handler = PanicHandler::new_in_arc(); // Setup logging - setup_log(&self.args.flag_logging); + let logger = setup_log(&self.args.flag_logging); // Raise fdlimit unsafe { ::fdlimit::raise_fd_limit(); } @@ -738,7 +752,8 @@ impl Configuration { miner.clone(), &addr, cors_domain, - apis.split(',').collect() + apis.split(',').collect(), + logger.clone(), )) } else { None @@ -772,6 +787,7 @@ impl Configuration { miner.clone(), &addr, auth, + logger.clone(), )) } else { None diff --git a/rpc/src/v1/impls/ethcore.rs b/rpc/src/v1/impls/ethcore.rs index d9764842c..f15d0a3d5 100644 --- a/rpc/src/v1/impls/ethcore.rs +++ b/rpc/src/v1/impls/ethcore.rs @@ -15,8 +15,9 @@ // along with Parity. If not, see . //! Ethcore-specific rpc implementation. -use util::{U256, Address}; +use util::{U256, Address, RotatingLogger}; use std::sync::{Arc, Weak}; +use std::ops::Deref; use jsonrpc_core::*; use ethminer::{MinerService}; use v1::traits::Ethcore; @@ -26,13 +27,15 @@ use v1::types::Bytes; pub struct EthcoreClient where M: MinerService { miner: Weak, + logger: Arc, } impl EthcoreClient where M: MinerService { /// Creates new `EthcoreClient`. - pub fn new(miner: &Arc) -> Self { + pub fn new(miner: &Arc, logger: Arc) -> Self { EthcoreClient { - miner: Arc::downgrade(miner) + miner: Arc::downgrade(miner), + logger: logger, } } } @@ -78,4 +81,14 @@ impl Ethcore for EthcoreClient where M: MinerService + 'static { fn gas_floor_target(&self, _: Params) -> Result { to_value(&take_weak!(self.miner).gas_floor_target()) } + + fn dev_logs(&self, _params: Params) -> Result { + let logs = self.logger.logs(); + to_value(&logs.deref()) + } + + fn dev_logs_levels(&self, _params: Params) -> Result { + to_value(&self.logger.levels()) + } + } diff --git a/rpc/src/v1/traits/ethcore.rs b/rpc/src/v1/traits/ethcore.rs index fb96ef035..b8e881408 100644 --- a/rpc/src/v1/traits/ethcore.rs +++ b/rpc/src/v1/traits/ethcore.rs @@ -42,6 +42,12 @@ pub trait Ethcore: Sized + Send + Sync + 'static { /// Returns minimal gas price for transaction to be included in queue. fn min_gas_price(&self, _: Params) -> Result { rpc_unimplemented!() } + /// Returns latest logs + fn dev_logs(&self, _: Params) -> Result { rpc_unimplemented!() } + + /// Returns logs levels + fn dev_logs_levels(&self, _: Params) -> Result { rpc_unimplemented!() } + /// Should be used to convert object to io delegate. fn to_delegate(self) -> IoDelegate { let mut delegate = IoDelegate::new(Arc::new(self)); @@ -53,6 +59,8 @@ pub trait Ethcore: Sized + Send + Sync + 'static { delegate.add_method("ethcore_extraData", Ethcore::extra_data); delegate.add_method("ethcore_gasFloorTarget", Ethcore::gas_floor_target); delegate.add_method("ethcore_minGasPrice", Ethcore::min_gas_price); + delegate.add_method("ethcore_devLogs", Ethcore::dev_logs); + delegate.add_method("ethcore_devLogsLevels", Ethcore::dev_logs_levels); delegate } } diff --git a/util/src/log.rs b/util/src/log.rs index 72b97caf5..c5ad40239 100644 --- a/util/src/log.rs +++ b/util/src/log.rs @@ -19,6 +19,8 @@ use std::env; use rlog::{LogLevelFilter}; use env_logger::LogBuilder; +use std::sync::{RwLock, RwLockReadGuard}; +use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; lazy_static! { static ref LOG_DUMMY: bool = { @@ -40,3 +42,46 @@ lazy_static! { pub fn init_log() { let _ = *LOG_DUMMY; } + +static LOG_SIZE : usize = 128; + +pub struct RotatingLogger { + idx: AtomicUsize, + levels: String, + logs: RwLock>, +} + +impl RotatingLogger { + + pub fn new(levels: String) -> Self { + RotatingLogger { + idx: AtomicUsize::new(0), + levels: levels, + logs: RwLock::new(Vec::with_capacity(LOG_SIZE)), + } + } + + pub fn append(&self, log: String) { + let idx = self.idx.fetch_add(1, Ordering::SeqCst); + let idx = idx % LOG_SIZE; + self.logs.write().unwrap().insert(idx, log); + } + + pub fn levels(&self) -> &str { + &self.levels + } + + pub fn logs(&self) -> RwLockReadGuard> { + self.logs.read().unwrap() + } + +} + +#[cfg(test)] +mod test { + #[test] + fn should_have_some_tests() { + assert_eq!(true, false); + } +} + From 407ab30503d83b9a60544e2725dff27fcc9ce48f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Tue, 19 Apr 2016 19:08:13 +0200 Subject: [PATCH 2/3] Using ArrayVec to store logs --- miner/src/transaction_queue.rs | 6 ++--- rpc/src/v1/impls/ethcore.rs | 2 +- rpc/src/v1/tests/ethcore.rs | 47 +++++++++++++++++++++++++++++----- util/src/log.rs | 24 +++++++++-------- 4 files changed, 58 insertions(+), 21 deletions(-) diff --git a/miner/src/transaction_queue.rs b/miner/src/transaction_queue.rs index ecfb2b8b0..6a1721f1d 100644 --- a/miner/src/transaction_queue.rs +++ b/miner/src/transaction_queue.rs @@ -672,10 +672,10 @@ impl TransactionQueue { } fn check_too_cheap(is_in: bool) -> Result<(), TransactionError> { - if !is_in { - Err(TransactionError::TooCheapToReplace) - } else { + if is_in { Ok(()) + } else { + Err(TransactionError::TooCheapToReplace) } } diff --git a/rpc/src/v1/impls/ethcore.rs b/rpc/src/v1/impls/ethcore.rs index f15d0a3d5..36116cfaa 100644 --- a/rpc/src/v1/impls/ethcore.rs +++ b/rpc/src/v1/impls/ethcore.rs @@ -84,7 +84,7 @@ impl Ethcore for EthcoreClient where M: MinerService + 'static { fn dev_logs(&self, _params: Params) -> Result { let logs = self.logger.logs(); - to_value(&logs.deref()) + to_value(&logs.deref().as_slice()) } fn dev_logs_levels(&self, _params: Params) -> Result { diff --git a/rpc/src/v1/tests/ethcore.rs b/rpc/src/v1/tests/ethcore.rs index 06d4ffc1c..51542e9dc 100644 --- a/rpc/src/v1/tests/ethcore.rs +++ b/rpc/src/v1/tests/ethcore.rs @@ -22,16 +22,20 @@ use ethminer::MinerService; use v1::tests::helpers::TestMinerService; use util::numbers::*; use rustc_serialize::hex::FromHex; +use util::log::RotatingLogger; fn miner_service() -> Arc { Arc::new(TestMinerService::default()) } +fn logger() -> Arc { + Arc::new(RotatingLogger::new("rpc=trace".to_owned())) +} #[test] fn rpc_ethcore_extra_data() { let miner = miner_service(); - let ethcore = EthcoreClient::new(&miner).to_delegate(); + let ethcore = EthcoreClient::new(&miner, logger()).to_delegate(); let io = IoHandler::new(); io.add_delegate(ethcore); @@ -45,7 +49,7 @@ fn rpc_ethcore_extra_data() { #[test] fn rpc_ethcore_gas_floor_target() { let miner = miner_service(); - let ethcore = EthcoreClient::new(&miner).to_delegate(); + let ethcore = EthcoreClient::new(&miner, logger()).to_delegate(); let io = IoHandler::new(); io.add_delegate(ethcore); @@ -58,7 +62,7 @@ fn rpc_ethcore_gas_floor_target() { #[test] fn rpc_ethcore_min_gas_price() { let miner = miner_service(); - let ethcore = EthcoreClient::new(&miner).to_delegate(); + let ethcore = EthcoreClient::new(&miner, logger()).to_delegate(); let io = IoHandler::new(); io.add_delegate(ethcore); @@ -71,7 +75,7 @@ fn rpc_ethcore_min_gas_price() { #[test] fn rpc_ethcore_set_min_gas_price() { let miner = miner_service(); - let ethcore = EthcoreClient::new(&miner).to_delegate(); + let ethcore = EthcoreClient::new(&miner, logger()).to_delegate(); let io = IoHandler::new(); io.add_delegate(ethcore); @@ -85,7 +89,7 @@ fn rpc_ethcore_set_min_gas_price() { #[test] fn rpc_ethcore_set_gas_floor_target() { let miner = miner_service(); - let ethcore = EthcoreClient::new(&miner).to_delegate(); + let ethcore = EthcoreClient::new(&miner, logger()).to_delegate(); let io = IoHandler::new(); io.add_delegate(ethcore); @@ -99,7 +103,7 @@ fn rpc_ethcore_set_gas_floor_target() { #[test] fn rpc_ethcore_set_extra_data() { let miner = miner_service(); - let ethcore = EthcoreClient::new(&miner).to_delegate(); + let ethcore = EthcoreClient::new(&miner, logger()).to_delegate(); let io = IoHandler::new(); io.add_delegate(ethcore); @@ -113,7 +117,7 @@ fn rpc_ethcore_set_extra_data() { #[test] fn rpc_ethcore_set_author() { let miner = miner_service(); - let ethcore = EthcoreClient::new(&miner).to_delegate(); + let ethcore = EthcoreClient::new(&miner, logger()).to_delegate(); let io = IoHandler::new(); io.add_delegate(ethcore); @@ -123,3 +127,32 @@ fn rpc_ethcore_set_author() { assert_eq!(io.handle_request(request), Some(response.to_owned())); assert_eq!(miner.author(), Address::from_str("cd1722f3947def4cf144679da39c4c32bdc35681").unwrap()); } + +#[test] +fn rpc_ethcore_dev_logs() { + let miner = miner_service(); + let logger = logger(); + logger.append("a".to_owned()); + logger.append("b".to_owned()); + let ethcore = EthcoreClient::new(&miner, logger.clone()).to_delegate(); + let io = IoHandler::new(); + io.add_delegate(ethcore); + + let request = r#"{"jsonrpc": "2.0", "method": "ethcore_devLogs", "params":[], "id": 1}"#; + let response = r#"{"jsonrpc":"2.0","result":["b","a"],"id":1}"#; + + assert_eq!(io.handle_request(request), Some(response.to_owned())); +} + +#[test] +fn rpc_ethcore_dev_logs_levels() { + let miner = miner_service(); + let ethcore = EthcoreClient::new(&miner, logger()).to_delegate(); + let io = IoHandler::new(); + io.add_delegate(ethcore); + + let request = r#"{"jsonrpc": "2.0", "method": "ethcore_devLogsLevels", "params":[], "id": 1}"#; + let response = r#"{"jsonrpc":"2.0","result":"rpc=trace","id":1}"#; + + assert_eq!(io.handle_request(request), Some(response.to_owned())); +} diff --git a/util/src/log.rs b/util/src/log.rs index c5ad40239..45017bfcd 100644 --- a/util/src/log.rs +++ b/util/src/log.rs @@ -20,7 +20,7 @@ use std::env; use rlog::{LogLevelFilter}; use env_logger::LogBuilder; use std::sync::{RwLock, RwLockReadGuard}; -use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; +use arrayvec::ArrayVec; lazy_static! { static ref LOG_DUMMY: bool = { @@ -43,35 +43,39 @@ pub fn init_log() { let _ = *LOG_DUMMY; } -static LOG_SIZE : usize = 128; +const LOG_SIZE : usize = 128; +/// Logger implementation that keeps up to `LOG_SIZE` log elements. pub struct RotatingLogger { - idx: AtomicUsize, + /// Defined logger levels levels: String, - logs: RwLock>, + /// Logs array. Latest log is always at index 0 + logs: RwLock>, } impl RotatingLogger { + /// Creates new `RotatingLogger` with given levels. + /// It does not enforce levels - it's just read only. pub fn new(levels: String) -> Self { RotatingLogger { - idx: AtomicUsize::new(0), levels: levels, - logs: RwLock::new(Vec::with_capacity(LOG_SIZE)), + logs: RwLock::new(ArrayVec::<[_; LOG_SIZE]>::new()), } } + /// Append new log entry pub fn append(&self, log: String) { - let idx = self.idx.fetch_add(1, Ordering::SeqCst); - let idx = idx % LOG_SIZE; - self.logs.write().unwrap().insert(idx, log); + self.logs.write().unwrap().insert(0, log); } + /// Return levels pub fn levels(&self) -> &str { &self.levels } - pub fn logs(&self) -> RwLockReadGuard> { + /// Return logs + pub fn logs(&self) -> RwLockReadGuard> { self.logs.read().unwrap() } From c56a67a55ad14813f3dc2204b3ef7a244d2c7e10 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Wed, 20 Apr 2016 00:47:56 +0200 Subject: [PATCH 3/3] Adding tests for RotatingLogger --- util/src/log.rs | 33 +++++++++++++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/util/src/log.rs b/util/src/log.rs index 45017bfcd..172957c13 100644 --- a/util/src/log.rs +++ b/util/src/log.rs @@ -83,9 +83,38 @@ impl RotatingLogger { #[cfg(test)] mod test { + use super::RotatingLogger; + + fn logger() -> RotatingLogger { + RotatingLogger::new("test".to_owned()) + } + #[test] - fn should_have_some_tests() { - assert_eq!(true, false); + fn should_return_log_levels() { + // given + let logger = logger(); + + // when + let levels = logger.levels(); + + // then + assert_eq!(levels, "test"); + } + + #[test] + fn should_return_latest_logs() { + // given + let logger = logger(); + + // when + logger.append("a".to_owned()); + logger.append("b".to_owned()); + + // then + let logs = logger.logs(); + assert_eq!(logs[0], "b".to_owned()); + assert_eq!(logs[1], "a".to_owned()); + assert_eq!(logs.len(), 2); } }