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); + } +} +