Skip to content

Commit 229ca25

Browse files
authored
refactor(dash-spv): make logging a built-in event handler (#745)
Move `LoggingEventHandler` out of the CLI binary and into `dash-spv` itself, then have `DashSpvClient::new` prepend it to the consumer-supplied handler list. Every consumer (CLI, FFI, integration tests, future embedders) now gets event log output for free as soon as a `tracing` subscriber is installed, instead of each one re-implementing the same bridge. The handler is `pub(crate)` since consumers never need to construct or reference it. Per-event silencing is available through the standard `tracing` target filter `dash_spv::client::event_handler=warn`, so no opt-out flag is needed.
1 parent dca03ce commit 229ca25

4 files changed

Lines changed: 77 additions & 48 deletions

File tree

dash-spv/src/client/event_handler.rs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,36 @@ pub trait EventHandler: Send + Sync + 'static {
3434
/// No-op implementation for consumers that don't need event notifications.
3535
impl EventHandler for () {}
3636

37+
/// Built-in handler that mirrors all events into `tracing` so consumers get
38+
/// log output once they install a tracing subscriber (e.g., via `init_logging`).
39+
///
40+
/// Attached automatically by `DashSpvClient::new` ahead of consumer-supplied
41+
/// handlers. To silence event logs without affecting other subscribers, set a
42+
/// per-target filter such as `dash_spv::client::event_handler=warn`.
43+
pub(crate) struct LoggingEventHandler;
44+
45+
impl EventHandler for LoggingEventHandler {
46+
fn on_sync_event(&self, event: &SyncEvent) {
47+
tracing::info!("SyncEvent: {}", event.description());
48+
}
49+
50+
fn on_network_event(&self, event: &NetworkEvent) {
51+
tracing::info!("NetworkEvent: {}", event.description());
52+
}
53+
54+
fn on_progress(&self, progress: &SyncProgress) {
55+
tracing::info!("SyncProgress: {}", progress);
56+
}
57+
58+
fn on_wallet_event(&self, event: &WalletEvent) {
59+
tracing::info!("WalletEvent: {}", event.description());
60+
}
61+
62+
fn on_error(&self, error: &str) {
63+
tracing::error!("{}", error);
64+
}
65+
}
66+
3767
/// Spawns a task that monitors a broadcast channel and dispatches events to the handler.
3868
///
3969
/// On failure, the error message is sent via `on_failure` so the coordinator can report

dash-spv/src/client/lifecycle.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
//! - Genesis block initialization
99
//! - Wallet data loading
1010
11+
use super::event_handler::LoggingEventHandler;
1112
use super::{ClientConfig, DashSpvClient, EventHandler};
1213
use crate::chain::checkpoints::{mainnet_checkpoints, testnet_checkpoints, CheckpointManager};
1314
use crate::error::{Result, SpvError};
@@ -36,6 +37,10 @@ impl<W: WalletInterface, N: NetworkManager, S: StorageManager> DashSpvClient<W,
3637
wallet: Arc<RwLock<W>>,
3738
event_handlers: Vec<Arc<dyn EventHandler>>,
3839
) -> Result<Self> {
40+
let event_handlers: Vec<Arc<dyn EventHandler>> =
41+
std::iter::once(Arc::new(LoggingEventHandler) as Arc<dyn EventHandler>)
42+
.chain(event_handlers)
43+
.collect();
3944
// Validate configuration
4045
config.validate().map_err(SpvError::Config)?;
4146

dash-spv/src/client/mod.rs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,4 +76,28 @@ mod tests {
7676
let wallet_ref = client.wallet();
7777
let _wallet_guard = wallet_ref.read().await;
7878
}
79+
80+
#[tokio::test]
81+
async fn client_attaches_builtin_logging_handler() {
82+
let config = ClientConfig::mainnet()
83+
.without_filters()
84+
.without_masternodes()
85+
.with_mempool_tracking(MempoolStrategy::FetchAll)
86+
.with_storage_path(TempDir::new().unwrap().path());
87+
88+
let network_manager = MockNetworkManager::new();
89+
let storage =
90+
DiskStorageManager::with_temp_dir().await.expect("Failed to create tmp storage");
91+
let wallet = Arc::new(RwLock::new(WalletManager::<ManagedWalletInfo>::new(config.network)));
92+
93+
let client = DashSpvClient::new(config, network_manager, storage, wallet, Vec::new())
94+
.await
95+
.expect("client construction must succeed");
96+
97+
assert_eq!(
98+
client.event_handlers.len(),
99+
1,
100+
"constructor should auto-attach the built-in logging handler",
101+
);
102+
}
79103
}

dash-spv/src/main.rs

Lines changed: 18 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,9 @@ use std::process;
55
use std::sync::Arc;
66

77
use clap::{Parser, ValueEnum};
8-
use dash_spv::network::NetworkEvent;
9-
use dash_spv::sync::{SyncEvent, SyncProgress};
10-
use dash_spv::{ClientConfig, DashSpvClient, EventHandler, LevelFilter, MempoolStrategy, Network};
8+
use dash_spv::{ClientConfig, DashSpvClient, LevelFilter, MempoolStrategy, Network};
119
use key_wallet::wallet::managed_wallet_info::ManagedWalletInfo;
12-
use key_wallet_manager::{WalletEvent, WalletManager};
10+
use key_wallet_manager::WalletManager;
1311
use tokio_util::sync::CancellationToken;
1412

1513
/// Network selection for CLI
@@ -71,31 +69,6 @@ impl From<LogLevelArg> for LevelFilter {
7169
}
7270
}
7371

74-
/// Logs all SPV client events via tracing.
75-
struct LoggingEventHandler;
76-
77-
impl EventHandler for LoggingEventHandler {
78-
fn on_sync_event(&self, event: &SyncEvent) {
79-
tracing::info!("{}", event.description());
80-
}
81-
82-
fn on_network_event(&self, event: &NetworkEvent) {
83-
tracing::info!("{}", event.description());
84-
}
85-
86-
fn on_progress(&self, progress: &SyncProgress) {
87-
tracing::info!("Sync progress: {}", progress);
88-
}
89-
90-
fn on_wallet_event(&self, event: &WalletEvent) {
91-
tracing::info!("Wallet: {}", event.description());
92-
}
93-
94-
fn on_error(&self, error: &str) {
95-
tracing::error!("{}", error);
96-
}
97-
}
98-
9972
/// Dash SPV (Simplified Payment Verification) client
10073
#[derive(Parser, Debug)]
10174
#[command(name = "dash-spv", version = dash_spv::VERSION, about)]
@@ -325,25 +298,22 @@ async fn run_client<S: dash_spv::storage::StorageManager>(
325298
wallet: Arc<tokio::sync::RwLock<WalletManager<ManagedWalletInfo>>>,
326299
) -> Result<(), Box<dyn std::error::Error>> {
327300
// Create and start the client
328-
let client = match DashSpvClient::<
329-
WalletManager<ManagedWalletInfo>,
330-
dash_spv::network::manager::PeerNetworkManager,
331-
S,
332-
>::new(
333-
config.clone(),
334-
network_manager,
335-
storage_manager,
336-
wallet.clone(),
337-
vec![Arc::new(LoggingEventHandler)],
338-
)
339-
.await
340-
{
341-
Ok(client) => client,
342-
Err(e) => {
343-
eprintln!("Failed to create SPV client: {}", e);
344-
process::exit(1);
345-
}
346-
};
301+
let client =
302+
match DashSpvClient::<
303+
WalletManager<ManagedWalletInfo>,
304+
dash_spv::network::manager::PeerNetworkManager,
305+
S,
306+
>::new(
307+
config.clone(), network_manager, storage_manager, wallet.clone(), Vec::new()
308+
)
309+
.await
310+
{
311+
Ok(client) => client,
312+
Err(e) => {
313+
eprintln!("Failed to create SPV client: {}", e);
314+
process::exit(1);
315+
}
316+
};
347317

348318
let shutdown_token = CancellationToken::new();
349319
let ctrl_c_token = shutdown_token.clone();

0 commit comments

Comments
 (0)