From a88dc5c24e02578c61ab16ffa1094e2be706525f Mon Sep 17 00:00:00 2001 From: Enigbe Ochekliye Date: Mon, 18 Nov 2024 21:59:53 +0100 Subject: [PATCH 1/5] feat(logger): implement configurable log writer with filesystem support * Add flexible log writer interface for multiple destinations * Implement filesystem writing capability via FilesystemLogger * Prefix LDK-based objects with 'Ldk' for consistency * Add configuration options for log file path and log level --- bindings/ldk_node.udl | 26 ++++++---- src/builder.rs | 90 +++++++++++++++++++-------------- src/chain/mod.rs | 14 ++--- src/config.rs | 37 +++++++++----- src/connection.rs | 6 +-- src/event.rs | 12 ++--- src/gossip.rs | 14 ++--- src/io/utils.rs | 29 +++++------ src/lib.rs | 19 +++---- src/liquidity.rs | 6 +-- src/logger.rs | 58 ++++++++++++++------- src/payment/bolt11.rs | 21 ++++---- src/payment/bolt12.rs | 10 ++-- src/payment/onchain.rs | 6 +-- src/payment/spontaneous.rs | 9 ++-- src/payment/store.rs | 6 +-- src/payment/unified_qr.rs | 6 +-- src/peer_store.rs | 8 +-- src/tx_broadcaster.rs | 8 +-- src/types.rs | 46 ++++++++--------- src/wallet/mod.rs | 24 ++++----- src/wallet/persist.rs | 8 ++- tests/common/mod.rs | 5 +- tests/integration_tests_rust.rs | 7 ++- 24 files changed, 259 insertions(+), 216 deletions(-) diff --git a/bindings/ldk_node.udl b/bindings/ldk_node.udl index a9ed4ff5b..c85cd483f 100644 --- a/bindings/ldk_node.udl +++ b/bindings/ldk_node.udl @@ -5,13 +5,11 @@ namespace ldk_node { dictionary Config { string storage_dir_path; - string? log_file_path; Network network; sequence? listening_addresses; NodeAlias? node_alias; sequence trusted_peers_0conf; u64 probing_liquidity_limit_multiplier; - LogLevel log_level; AnchorChannelsConfig? anchor_channels_config; SendingParameters? sending_parameters; }; @@ -27,6 +25,20 @@ dictionary EsploraSyncConfig { u64 fee_rate_cache_update_interval_secs; }; +enum LdkLevel { + "Gossip", + "Trace", + "Debug", + "Info", + "Warn", + "Error", +}; + +dictionary FilesystemLoggerConfig { + string log_file_path; + LdkLevel level; +}; + interface Builder { constructor(); [Name=from_config] @@ -41,6 +53,7 @@ interface Builder { void set_gossip_source_rgs(string rgs_server_url); void set_liquidity_source_lsps2(SocketAddress address, PublicKey node_id, string? token); void set_storage_dir_path(string storage_dir_path); + void set_filesystem_logger(FilesystemLoggerConfig fs_config); void set_network(Network network); [Throws=BuildError] void set_listening_addresses(sequence listening_addresses); @@ -535,15 +548,6 @@ interface MaxDustHTLCExposure { FeeRateMultiplier ( u64 multiplier ); }; -enum LogLevel { - "Gossip", - "Trace", - "Debug", - "Info", - "Warn", - "Error", -}; - interface NetworkGraph { sequence list_channels(); ChannelInfo? channel(u64 short_channel_id); diff --git a/src/builder.rs b/src/builder.rs index ceb3c0918..cc95ec0c6 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -6,7 +6,9 @@ // accordance with one or both of these licenses. use crate::chain::{ChainSource, DEFAULT_ESPLORA_SERVER_URL}; -use crate::config::{default_user_config, Config, EsploraSyncConfig, WALLET_KEYS_SEED_LEN}; +use crate::config::{ + default_user_config, Config, EsploraSyncConfig, FilesystemLoggerConfig, WALLET_KEYS_SEED_LEN, +}; use crate::connection::ConnectionManager; use crate::event::EventQueue; @@ -16,7 +18,7 @@ use crate::io::sqlite_store::SqliteStore; use crate::io::utils::{read_node_metrics, write_node_metrics}; use crate::io::vss_store::VssStore; use crate::liquidity::LiquiditySource; -use crate::logger::{log_error, log_info, FilesystemLogger, Logger}; +use crate::logger::{log_error, log_info, LdkLogger, Logger}; use crate::message_handler::NodeCustomMessageHandler; use crate::payment::store::PaymentStore; use crate::peer_store::PeerStore; @@ -27,8 +29,8 @@ use crate::types::{ }; use crate::wallet::persist::KVStoreWalletPersister; use crate::wallet::Wallet; +use crate::Node; use crate::{io, NodeMetrics}; -use crate::{LogLevel, Node}; use lightning::chain::{chainmonitor, BestBlock, Watch}; use lightning::io::Cursor; @@ -106,6 +108,17 @@ impl Default for LiquiditySourceConfig { } } +#[derive(Debug)] +enum LogWriterConfig { + File(FilesystemLoggerConfig), +} + +impl Default for LogWriterConfig { + fn default() -> Self { + Self::File(FilesystemLoggerConfig::default()) + } +} + /// An error encountered during building a [`Node`]. /// /// [`Node`]: crate::Node @@ -182,6 +195,7 @@ pub struct NodeBuilder { chain_data_source_config: Option, gossip_source_config: Option, liquidity_source_config: Option, + log_writer_config: Option, } impl NodeBuilder { @@ -197,12 +211,14 @@ impl NodeBuilder { let chain_data_source_config = None; let gossip_source_config = None; let liquidity_source_config = None; + let log_writer_config = None; Self { config, entropy_source_config, chain_data_source_config, gossip_source_config, liquidity_source_config, + log_writer_config, } } @@ -298,9 +314,9 @@ impl NodeBuilder { self } - /// Sets the log file path if the log file needs to live separate from the storage directory path. - pub fn set_log_file_path(&mut self, log_dir_path: String) -> &mut Self { - self.config.log_file_path = Some(log_dir_path); + /// Configures the [`Node`] instance to write logs to the filesystem. + pub fn set_filesystem_logger(&mut self, fs_config: FilesystemLoggerConfig) -> &mut Self { + self.log_writer_config = Some(LogWriterConfig::File(fs_config)); self } @@ -333,12 +349,6 @@ impl NodeBuilder { Ok(self) } - /// Sets the level at which [`Node`] will log messages. - pub fn set_log_level(&mut self, level: LogLevel) -> &mut Self { - self.config.log_level = level; - self - } - /// Builds a [`Node`] instance with a [`SqliteStore`] backend and according to the options /// previously configured. pub fn build(&self) -> Result { @@ -391,7 +401,10 @@ impl NodeBuilder { ) -> Result { use bitcoin::key::Secp256k1; - let logger = setup_logger(&self.config)?; + let writer = LogWriterConfig::default(); + let log_writer_config = + if let Some(config) = &self.log_writer_config { config } else { &writer }; + let logger = setup_logger(&log_writer_config)?; let seed_bytes = seed_bytes_from_config( &self.config, @@ -456,7 +469,10 @@ impl NodeBuilder { pub fn build_with_vss_store_and_header_provider( &self, vss_url: String, store_id: String, header_provider: Arc, ) -> Result { - let logger = setup_logger(&self.config)?; + let writer = LogWriterConfig::default(); + let log_writer_config = + if let Some(config) = &self.log_writer_config { config } else { &writer }; + let logger = setup_logger(&log_writer_config)?; let seed_bytes = seed_bytes_from_config( &self.config, @@ -488,7 +504,11 @@ impl NodeBuilder { /// Builds a [`Node`] instance according to the options previously configured. pub fn build_with_store(&self, kv_store: Arc) -> Result { - let logger = setup_logger(&self.config)?; + let writer = LogWriterConfig::default(); + let log_writer_config = + if let Some(config) = &self.log_writer_config { config } else { &writer }; + let logger = setup_logger(&log_writer_config)?; + let seed_bytes = seed_bytes_from_config( &self.config, self.entropy_source_config.as_ref(), @@ -610,9 +630,9 @@ impl ArcedNodeBuilder { self.inner.write().unwrap().set_storage_dir_path(storage_dir_path); } - /// Sets the log file path if logs need to live separate from the storage directory path. - pub fn set_log_file_path(&self, log_file_path: String) { - self.inner.write().unwrap().set_log_file_path(log_file_path); + /// Configures the [`Node`] instance to write logs to the filesystem. + pub fn set_filesystem_logger(&self, fs_config: FilesystemLoggerConfig) { + self.inner.write().unwrap().set_filesystem_logger(fs_config); } /// Sets the Bitcoin network used. @@ -635,11 +655,6 @@ impl ArcedNodeBuilder { self.inner.write().unwrap().set_node_alias(node_alias).map(|_| ()) } - /// Sets the level at which [`Node`] will log messages. - pub fn set_log_level(&self, level: LogLevel) { - self.inner.write().unwrap().set_log_level(level); - } - /// Builds a [`Node`] instance with a [`SqliteStore`] backend and according to the options /// previously configured. pub fn build(&self) -> Result, BuildError> { @@ -734,7 +749,7 @@ fn build_with_store_internal( config: Arc, chain_data_source_config: Option<&ChainDataSourceConfig>, gossip_source_config: Option<&GossipSourceConfig>, liquidity_source_config: Option<&LiquiditySourceConfig>, seed_bytes: [u8; 64], - logger: Arc, kv_store: Arc, + logger: Arc, kv_store: Arc, ) -> Result { // Initialize the status fields. let is_listening = Arc::new(AtomicBool::new(false)); @@ -1242,23 +1257,22 @@ fn build_with_store_internal( }) } -/// Sets up the node logger, creating a new log file if it does not exist, or utilizing -/// the existing log file. -fn setup_logger(config: &Config) -> Result, BuildError> { - let log_file_path = match &config.log_file_path { - Some(log_dir) => String::from(log_dir), - None => format!("{}/{}", config.storage_dir_path.clone(), "ldk_node.log"), - }; +/// Sets up the node logger. +fn setup_logger(config: &LogWriterConfig) -> Result, BuildError> { + match config { + LogWriterConfig::File(fs_logger_config) => { + let log_file_path = &fs_logger_config.log_file_path; - Ok(Arc::new( - FilesystemLogger::new(log_file_path, config.log_level) - .map_err(|_| BuildError::LoggerSetupFailed)?, - )) + Ok(Arc::new( + Logger::new_fs_writer(log_file_path.to_string(), fs_logger_config.level) + .map_err(|_| BuildError::LoggerSetupFailed)?, + )) + }, + } } fn seed_bytes_from_config( - config: &Config, entropy_source_config: Option<&EntropySourceConfig>, - logger: Arc, + config: &Config, entropy_source_config: Option<&EntropySourceConfig>, logger: Arc, ) -> Result<[u8; 64], BuildError> { match entropy_source_config { Some(EntropySourceConfig::SeedBytes(bytes)) => Ok(bytes.clone()), @@ -1280,7 +1294,7 @@ fn seed_bytes_from_config( } fn derive_vss_xprv( - config: Arc, seed_bytes: &[u8; 64], logger: Arc, + config: Arc, seed_bytes: &[u8; 64], logger: Arc, ) -> Result { use bitcoin::key::Secp256k1; diff --git a/src/chain/mod.rs b/src/chain/mod.rs index d9fedd453..59ce78f37 100644 --- a/src/chain/mod.rs +++ b/src/chain/mod.rs @@ -21,7 +21,7 @@ use crate::fee_estimator::{ ConfirmationTarget, OnchainFeeEstimator, }; use crate::io::utils::write_node_metrics; -use crate::logger::{log_bytes, log_error, log_info, log_trace, FilesystemLogger, Logger}; +use crate::logger::{log_bytes, log_error, log_info, log_trace, LdkLogger, Logger}; use crate::types::{Broadcaster, ChainMonitor, ChannelManager, DynStore, Sweeper, Wallet}; use crate::{Error, NodeMetrics}; @@ -113,13 +113,13 @@ pub(crate) enum ChainSource { esplora_client: EsploraAsyncClient, onchain_wallet: Arc, onchain_wallet_sync_status: Mutex, - tx_sync: Arc>>, + tx_sync: Arc>>, lightning_wallet_sync_status: Mutex, fee_estimator: Arc, tx_broadcaster: Arc, kv_store: Arc, config: Arc, - logger: Arc, + logger: Arc, node_metrics: Arc>, }, BitcoindRpc { @@ -132,7 +132,7 @@ pub(crate) enum ChainSource { tx_broadcaster: Arc, kv_store: Arc, config: Arc, - logger: Arc, + logger: Arc, node_metrics: Arc>, }, } @@ -141,7 +141,7 @@ impl ChainSource { pub(crate) fn new_esplora( server_url: String, sync_config: EsploraSyncConfig, onchain_wallet: Arc, fee_estimator: Arc, tx_broadcaster: Arc, - kv_store: Arc, config: Arc, logger: Arc, + kv_store: Arc, config: Arc, logger: Arc, node_metrics: Arc>, ) -> Self { let mut client_builder = esplora_client::Builder::new(&server_url); @@ -171,7 +171,7 @@ impl ChainSource { host: String, port: u16, rpc_user: String, rpc_password: String, onchain_wallet: Arc, fee_estimator: Arc, tx_broadcaster: Arc, kv_store: Arc, config: Arc, - logger: Arc, node_metrics: Arc>, + logger: Arc, node_metrics: Arc>, ) -> Self { let bitcoind_rpc_client = Arc::new(BitcoindRpcClient::new(host, port, rpc_user, rpc_password)); @@ -1125,7 +1125,7 @@ impl Filter for ChainSource { fn periodically_archive_fully_resolved_monitors( channel_manager: Arc, chain_monitor: Arc, - kv_store: Arc, logger: Arc, node_metrics: Arc>, + kv_store: Arc, logger: Arc, node_metrics: Arc>, ) -> Result<(), Error> { let mut locked_node_metrics = node_metrics.write().unwrap(); let cur_height = channel_manager.current_best_block().height; diff --git a/src/config.rs b/src/config.rs index 00b147e21..1820a577a 100644 --- a/src/config.rs +++ b/src/config.rs @@ -7,6 +7,7 @@ //! Objects for configuring the node. +use crate::logger::LdkLevel; use crate::payment::SendingParameters; use lightning::ln::msgs::SocketAddress; @@ -14,7 +15,6 @@ use lightning::routing::gossip::NodeAlias; use lightning::util::config::ChannelConfig as LdkChannelConfig; use lightning::util::config::MaxDustHTLCExposure as LdkMaxDustHTLCExposure; use lightning::util::config::UserConfig; -use lightning::util::logger::Level as LogLevel; use bitcoin::secp256k1::PublicKey; use bitcoin::Network; @@ -22,14 +22,15 @@ use bitcoin::Network; use std::time::Duration; // Config defaults -const DEFAULT_STORAGE_DIR_PATH: &str = "/tmp/ldk_node/"; +const DEFAULT_STORAGE_DIR_PATH: &str = "/tmp/ldk_node"; const DEFAULT_NETWORK: Network = Network::Bitcoin; const DEFAULT_BDK_WALLET_SYNC_INTERVAL_SECS: u64 = 80; const DEFAULT_LDK_WALLET_SYNC_INTERVAL_SECS: u64 = 30; const DEFAULT_FEE_RATE_CACHE_UPDATE_INTERVAL_SECS: u64 = 60 * 10; const DEFAULT_PROBING_LIQUIDITY_LIMIT_MULTIPLIER: u64 = 3; -const DEFAULT_LOG_LEVEL: LogLevel = LogLevel::Debug; const DEFAULT_ANCHOR_PER_CHANNEL_RESERVE_SATS: u64 = 25_000; +const DEFAULT_LOG_FILE_PATH: &'static str = "ldk_node.log"; +const DEFAULT_LOG_LEVEL: LdkLevel = LdkLevel::Debug; // The 'stop gap' parameter used by BDK's wallet sync. This seems to configure the threshold // number of derivation indexes after which BDK stops looking for new scripts belonging to the wallet. @@ -103,11 +104,6 @@ pub(crate) const WALLET_KEYS_SEED_LEN: usize = 64; pub struct Config { /// The path where the underlying LDK and BDK persist their data. pub storage_dir_path: String, - /// The path where logs are stored. - /// - /// If set to `None`, logs can be found in `ldk_node.log` in the [`Config::storage_dir_path`] - /// directory. - pub log_file_path: Option, /// The used Bitcoin network. pub network: Network, /// The addresses on which the node will listen for incoming connections. @@ -133,10 +129,6 @@ pub struct Config { /// Channels with available liquidity less than the required amount times this value won't be /// used to send pre-flight probes. pub probing_liquidity_limit_multiplier: u64, - /// The level at which we log messages. - /// - /// Any messages below this level will be excluded from the logs. - pub log_level: LogLevel, /// Configuration options pertaining to Anchor channels, i.e., channels for which the /// `option_anchors_zero_fee_htlc_tx` channel type is negotiated. /// @@ -168,12 +160,10 @@ impl Default for Config { fn default() -> Self { Self { storage_dir_path: DEFAULT_STORAGE_DIR_PATH.to_string(), - log_file_path: None, network: DEFAULT_NETWORK, listening_addresses: None, trusted_peers_0conf: Vec::new(), probing_liquidity_limit_multiplier: DEFAULT_PROBING_LIQUIDITY_LIMIT_MULTIPLIER, - log_level: DEFAULT_LOG_LEVEL, anchor_channels_config: Some(AnchorChannelsConfig::default()), sending_parameters: None, node_alias: None, @@ -439,6 +429,25 @@ impl From for LdkMaxDustHTLCExposure { } } +/// Configuration options for logging to the filesystem. +#[derive(Debug)] +pub struct FilesystemLoggerConfig { + /// The log file path. + /// + /// This specifies the log file path if a destination other than the storage + /// directory, i.e. [`Config::storage_dir_path`], is preferred. + pub log_file_path: String, + /// This specifies the log level. + pub level: LdkLevel, +} + +impl Default for FilesystemLoggerConfig { + fn default() -> Self { + let log_file_path = format!("{}/{}", DEFAULT_STORAGE_DIR_PATH, DEFAULT_LOG_FILE_PATH); + Self { log_file_path, level: DEFAULT_LOG_LEVEL } + } +} + #[cfg(test)] mod tests { use std::str::FromStr; diff --git a/src/connection.rs b/src/connection.rs index 5f665f77e..c4cde717a 100644 --- a/src/connection.rs +++ b/src/connection.rs @@ -5,7 +5,7 @@ // http://opensource.org/licenses/MIT>, at your option. You may not use this file except in // accordance with one or both of these licenses. -use crate::logger::{log_error, log_info, Logger}; +use crate::logger::{log_error, log_info, LdkLogger}; use crate::types::PeerManager; use crate::Error; @@ -21,7 +21,7 @@ use std::time::Duration; pub(crate) struct ConnectionManager where - L::Target: Logger, + L::Target: LdkLogger, { pending_connections: Mutex>>>>, @@ -31,7 +31,7 @@ where impl ConnectionManager where - L::Target: Logger, + L::Target: LdkLogger, { pub(crate) fn new(peer_manager: Arc, logger: L) -> Self { let pending_connections = Mutex::new(HashMap::new()); diff --git a/src/event.rs b/src/event.rs index 1de77e937..edaa561dd 100644 --- a/src/event.rs +++ b/src/event.rs @@ -24,7 +24,7 @@ use crate::io::{ EVENT_QUEUE_PERSISTENCE_KEY, EVENT_QUEUE_PERSISTENCE_PRIMARY_NAMESPACE, EVENT_QUEUE_PERSISTENCE_SECONDARY_NAMESPACE, }; -use crate::logger::{log_debug, log_error, log_info, Logger}; +use crate::logger::{log_debug, log_error, log_info, LdkLogger}; use lightning::events::bump_transaction::BumpTransactionEvent; use lightning::events::{ClosureReason, PaymentPurpose, ReplayEvent}; @@ -282,7 +282,7 @@ impl_writeable_tlv_based_enum!(Event, pub struct EventQueue where - L::Target: Logger, + L::Target: LdkLogger, { queue: Arc>>, waker: Arc>>, @@ -293,7 +293,7 @@ where impl EventQueue where - L::Target: Logger, + L::Target: LdkLogger, { pub(crate) fn new(kv_store: Arc, logger: L) -> Self { let queue = Arc::new(Mutex::new(VecDeque::new())); @@ -372,7 +372,7 @@ where impl ReadableArgs<(Arc, L)> for EventQueue where - L::Target: Logger, + L::Target: LdkLogger, { #[inline] fn read( @@ -436,7 +436,7 @@ impl Future for EventFuture { pub(crate) struct EventHandler where - L::Target: Logger, + L::Target: LdkLogger, { event_queue: Arc>, wallet: Arc, @@ -454,7 +454,7 @@ where impl EventHandler where - L::Target: Logger, + L::Target: LdkLogger, { pub fn new( event_queue: Arc>, wallet: Arc, diff --git a/src/gossip.rs b/src/gossip.rs index 45ceb536f..be6598c5a 100644 --- a/src/gossip.rs +++ b/src/gossip.rs @@ -7,7 +7,7 @@ use crate::chain::ChainSource; use crate::config::RGS_SYNC_TIMEOUT_SECS; -use crate::logger::{log_error, log_trace, FilesystemLogger, Logger}; +use crate::logger::{log_error, log_trace, LdkLogger, Logger}; use crate::types::{GossipSync, Graph, P2PGossipSync, PeerManager, RapidGossipSync, UtxoLookup}; use crate::Error; @@ -21,18 +21,18 @@ use std::time::Duration; pub(crate) enum GossipSource { P2PNetwork { gossip_sync: Arc, - logger: Arc, + logger: Arc, }, RapidGossipSync { gossip_sync: Arc, server_url: String, latest_sync_timestamp: AtomicU32, - logger: Arc, + logger: Arc, }, } impl GossipSource { - pub fn new_p2p(network_graph: Arc, logger: Arc) -> Self { + pub fn new_p2p(network_graph: Arc, logger: Arc) -> Self { let gossip_sync = Arc::new(P2PGossipSync::new( network_graph, None::>, @@ -43,7 +43,7 @@ impl GossipSource { pub fn new_rgs( server_url: String, latest_sync_timestamp: u32, network_graph: Arc, - logger: Arc, + logger: Arc, ) -> Self { let gossip_sync = Arc::new(RapidGossipSync::new(network_graph, Arc::clone(&logger))); let latest_sync_timestamp = AtomicU32::new(latest_sync_timestamp); @@ -128,12 +128,12 @@ impl GossipSource { pub(crate) struct RuntimeSpawner { runtime: Arc>>>, - logger: Arc, + logger: Arc, } impl RuntimeSpawner { pub(crate) fn new( - runtime: Arc>>>, logger: Arc, + runtime: Arc>>>, logger: Arc, ) -> Self { Self { runtime, logger } } diff --git a/src/io/utils.rs b/src/io/utils.rs index 218fec473..8b2d3b77a 100644 --- a/src/io/utils.rs +++ b/src/io/utils.rs @@ -13,7 +13,7 @@ use crate::fee_estimator::OnchainFeeEstimator; use crate::io::{ NODE_METRICS_KEY, NODE_METRICS_PRIMARY_NAMESPACE, NODE_METRICS_SECONDARY_NAMESPACE, }; -use crate::logger::{log_error, FilesystemLogger}; +use crate::logger::{log_error, LdkLogger, Logger}; use crate::peer_store::PeerStore; use crate::sweep::DeprecatedSpendableOutputInfo; use crate::types::{Broadcaster, DynStore, KeysManager, Sweeper}; @@ -24,7 +24,6 @@ use lightning::io::Cursor; use lightning::ln::msgs::DecodeError; use lightning::routing::gossip::NetworkGraph; use lightning::routing::scoring::{ProbabilisticScorer, ProbabilisticScoringDecayParameters}; -use lightning::util::logger::Logger; use lightning::util::persist::{ KVSTORE_NAMESPACE_KEY_ALPHABET, KVSTORE_NAMESPACE_KEY_MAX_LEN, NETWORK_GRAPH_PERSISTENCE_KEY, NETWORK_GRAPH_PERSISTENCE_PRIMARY_NAMESPACE, NETWORK_GRAPH_PERSISTENCE_SECONDARY_NAMESPACE, @@ -72,7 +71,7 @@ pub(crate) fn read_or_generate_seed_file( keys_seed_path: &str, logger: L, ) -> std::io::Result<[u8; WALLET_KEYS_SEED_LEN]> where - L::Target: Logger, + L::Target: LdkLogger, { if Path::new(&keys_seed_path).exists() { let seed = fs::read(keys_seed_path).map_err(|e| { @@ -123,7 +122,7 @@ pub(crate) fn read_network_graph( kv_store: Arc, logger: L, ) -> Result, std::io::Error> where - L::Target: Logger, + L::Target: LdkLogger, { let mut reader = Cursor::new(kv_store.read( NETWORK_GRAPH_PERSISTENCE_PRIMARY_NAMESPACE, @@ -141,7 +140,7 @@ pub(crate) fn read_scorer>, L: Deref + Clone>( kv_store: Arc, network_graph: G, logger: L, ) -> Result, std::io::Error> where - L::Target: Logger, + L::Target: LdkLogger, { let params = ProbabilisticScoringDecayParameters::default(); let mut reader = Cursor::new(kv_store.read( @@ -161,7 +160,7 @@ pub(crate) fn read_event_queue( kv_store: Arc, logger: L, ) -> Result, std::io::Error> where - L::Target: Logger, + L::Target: LdkLogger, { let mut reader = Cursor::new(kv_store.read( EVENT_QUEUE_PERSISTENCE_PRIMARY_NAMESPACE, @@ -179,7 +178,7 @@ pub(crate) fn read_peer_info( kv_store: Arc, logger: L, ) -> Result, std::io::Error> where - L::Target: Logger, + L::Target: LdkLogger, { let mut reader = Cursor::new(kv_store.read( PEER_INFO_PERSISTENCE_PRIMARY_NAMESPACE, @@ -197,7 +196,7 @@ pub(crate) fn read_payments( kv_store: Arc, logger: L, ) -> Result, std::io::Error> where - L::Target: Logger, + L::Target: LdkLogger, { let mut res = Vec::new(); @@ -226,7 +225,7 @@ where pub(crate) fn read_output_sweeper( broadcaster: Arc, fee_estimator: Arc, chain_data_source: Arc, keys_manager: Arc, kv_store: Arc, - logger: Arc, + logger: Arc, ) -> Result { let mut reader = Cursor::new(kv_store.read( OUTPUT_SWEEPER_PERSISTENCE_PRIMARY_NAMESPACE, @@ -264,7 +263,7 @@ pub(crate) fn migrate_deprecated_spendable_outputs( sweeper: Arc, kv_store: Arc, logger: L, ) -> Result<(), std::io::Error> where - L::Target: Logger, + L::Target: LdkLogger, { let best_block = sweeper.current_best_block(); @@ -349,7 +348,7 @@ pub(crate) fn read_node_metrics( kv_store: Arc, logger: L, ) -> Result where - L::Target: Logger, + L::Target: LdkLogger, { let mut reader = Cursor::new(kv_store.read( NODE_METRICS_PRIMARY_NAMESPACE, @@ -366,7 +365,7 @@ pub(crate) fn write_node_metrics( node_metrics: &NodeMetrics, kv_store: Arc, logger: L, ) -> Result<(), Error> where - L::Target: Logger, + L::Target: LdkLogger, { let data = node_metrics.encode(); kv_store @@ -486,7 +485,7 @@ macro_rules! impl_read_write_change_set_type { kv_store: Arc, logger: L, ) -> Result, std::io::Error> where - L::Target: Logger, + L::Target: LdkLogger, { let bytes = match kv_store.read($primary_namespace, $secondary_namespace, $key) { Ok(bytes) => bytes, @@ -526,7 +525,7 @@ macro_rules! impl_read_write_change_set_type { value: &$change_set_type, kv_store: Arc, logger: L, ) -> Result<(), std::io::Error> where - L::Target: Logger, + L::Target: LdkLogger, { let data = ChangeSetSerWrapper(value).encode(); kv_store.write($primary_namespace, $secondary_namespace, $key, &data).map_err(|e| { @@ -600,7 +599,7 @@ impl_read_write_change_set_type!( // Reads the full BdkWalletChangeSet or returns default fields pub(crate) fn read_bdk_wallet_change_set( - kv_store: Arc, logger: Arc, + kv_store: Arc, logger: Arc, ) -> Result, std::io::Error> { let mut change_set = BdkWalletChangeSet::default(); diff --git a/src/lib.rs b/src/lib.rs index 140c6bb41..3f51e0087 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -111,6 +111,9 @@ pub use event::Event; pub use io::utils::generate_entropy_mnemonic; +pub use config::FilesystemLoggerConfig; +pub use logger::LdkLevel; + #[cfg(feature = "uniffi")] use uniffi_types::*; @@ -143,7 +146,7 @@ use types::{ }; pub use types::{ChannelDetails, CustomTlvRecord, PeerDetails, UserChannelId}; -use logger::{log_error, log_info, log_trace, FilesystemLogger, Logger}; +use logger::{log_error, log_info, log_trace, LdkLogger, Logger}; use lightning::chain::BestBlock; use lightning::events::bump_transaction::Wallet as LdkWallet; @@ -153,8 +156,6 @@ use lightning::ln::channelmanager::PaymentId; use lightning::ln::msgs::SocketAddress; use lightning::routing::gossip::NodeAlias; -pub use lightning::util::logger::Level as LogLevel; - use lightning_background_processor::process_events_async; use bitcoin::secp256k1::PublicKey; @@ -181,23 +182,23 @@ pub struct Node { wallet: Arc, chain_source: Arc, tx_broadcaster: Arc, - event_queue: Arc>>, + event_queue: Arc>>, channel_manager: Arc, chain_monitor: Arc, output_sweeper: Arc, peer_manager: Arc, onion_messenger: Arc, - connection_manager: Arc>>, + connection_manager: Arc>>, keys_manager: Arc, network_graph: Arc, gossip_source: Arc, - liquidity_source: Option>>>, + liquidity_source: Option>>>, kv_store: Arc, - logger: Arc, + logger: Arc, _router: Arc, scorer: Arc>, - peer_store: Arc>>, - payment_store: Arc>>, + peer_store: Arc>>, + payment_store: Arc>>, is_listening: Arc, node_metrics: Arc>, } diff --git a/src/liquidity.rs b/src/liquidity.rs index 0188b939b..9e9450f8f 100644 --- a/src/liquidity.rs +++ b/src/liquidity.rs @@ -5,7 +5,7 @@ // http://opensource.org/licenses/MIT>, at your option. You may not use this file except in // accordance with one or both of these licenses. -use crate::logger::{log_debug, log_error, log_info, Logger}; +use crate::logger::{log_debug, log_error, log_info, LdkLogger}; use crate::types::{ChannelManager, KeysManager, LiquidityManager, PeerManager}; use crate::{Config, Error}; @@ -41,7 +41,7 @@ struct LSPS2Service { pub(crate) struct LiquiditySource where - L::Target: Logger, + L::Target: LdkLogger, { lsps2_service: Option, channel_manager: Arc, @@ -53,7 +53,7 @@ where impl LiquiditySource where - L::Target: Logger, + L::Target: LdkLogger, { pub(crate) fn new_lsps2( address: SocketAddress, node_id: PublicKey, token: Option, diff --git a/src/logger.rs b/src/logger.rs index bde4faff0..904aeee98 100644 --- a/src/logger.rs +++ b/src/logger.rs @@ -5,10 +5,11 @@ // http://opensource.org/licenses/MIT>, at your option. You may not use this file except in // accordance with one or both of these licenses. -pub(crate) use lightning::util::logger::Logger; +pub(crate) use lightning::util::logger::Logger as LdkLogger; pub(crate) use lightning::{log_bytes, log_debug, log_error, log_info, log_trace}; -use lightning::util::logger::{Level, Record}; +pub use lightning::util::logger::Level as LdkLevel; +use lightning::util::logger::Record; use chrono::Utc; @@ -18,12 +19,24 @@ use std::path::Path; pub(crate) struct FilesystemLogger { file_path: String, - level: Level, + level: LdkLevel, } -impl FilesystemLogger { - /// Creates a new filesystem logger given the path to the log file and the log level. - pub(crate) fn new(log_file_path: String, level: Level) -> Result { +/// Defines a writer for [`Logger`]. +pub(crate) enum Writer { + /// Writes logs to the file system. + FileWriter(FilesystemLogger), +} + +pub(crate) struct Logger { + /// Specifies the logger's writer. + writer: Writer, +} + +impl Logger { + /// Creates a new logger with a filesystem writer. The parameters to this function + /// are the path to the log file, and the log level. + pub fn new_fs_writer(log_file_path: String, level: LdkLevel) -> Result { if let Some(parent_dir) = Path::new(&log_file_path).parent() { fs::create_dir_all(parent_dir) .map_err(|e| eprintln!("ERROR: Failed to create log parent directory: {}", e))?; @@ -36,14 +49,14 @@ impl FilesystemLogger { .map_err(|e| eprintln!("ERROR: Failed to open log file: {}", e))?; } - Ok(Self { file_path: log_file_path, level }) + let fs_writer = FilesystemLogger { file_path: log_file_path, level }; + + Ok(Self { writer: Writer::FileWriter(fs_writer) }) } } -impl Logger for FilesystemLogger { + +impl LdkLogger for Logger { fn log(&self, record: Record) { - if record.level < self.level { - return; - } let raw_log = record.args.to_string(); let log = format!( "{} {:<5} [{}:{}] {}\n", @@ -53,12 +66,21 @@ impl Logger for FilesystemLogger { record.line, raw_log ); - fs::OpenOptions::new() - .create(true) - .append(true) - .open(self.file_path.clone()) - .expect("Failed to open log file") - .write_all(log.as_bytes()) - .expect("Failed to write to log file") + + match &self.writer { + Writer::FileWriter(fs_logger) => { + if record.level < fs_logger.level { + return; + } + + fs::OpenOptions::new() + .create(true) + .append(true) + .open(fs_logger.file_path.clone()) + .expect("Failed to open log file") + .write_all(log.as_bytes()) + .expect("Failed to write to log file") + }, + } } } diff --git a/src/payment/bolt11.rs b/src/payment/bolt11.rs index 4c89ca261..386da30df 100644 --- a/src/payment/bolt11.rs +++ b/src/payment/bolt11.rs @@ -13,7 +13,7 @@ use crate::config::{Config, LDK_PAYMENT_RETRY_TIMEOUT}; use crate::connection::ConnectionManager; use crate::error::Error; use crate::liquidity::LiquiditySource; -use crate::logger::{log_error, log_info, FilesystemLogger, Logger}; +use crate::logger::{log_error, log_info, LdkLogger, Logger}; use crate::payment::store::{ LSPFeeLimits, PaymentDetails, PaymentDetailsUpdate, PaymentDirection, PaymentKind, PaymentStatus, PaymentStore, @@ -65,23 +65,22 @@ macro_rules! maybe_convert_description { pub struct Bolt11Payment { runtime: Arc>>>, channel_manager: Arc, - connection_manager: Arc>>, - liquidity_source: Option>>>, - payment_store: Arc>>, - peer_store: Arc>>, + connection_manager: Arc>>, + liquidity_source: Option>>>, + payment_store: Arc>>, + peer_store: Arc>>, config: Arc, - logger: Arc, + logger: Arc, } impl Bolt11Payment { pub(crate) fn new( runtime: Arc>>>, channel_manager: Arc, - connection_manager: Arc>>, - liquidity_source: Option>>>, - payment_store: Arc>>, - peer_store: Arc>>, config: Arc, - logger: Arc, + connection_manager: Arc>>, + liquidity_source: Option>>>, + payment_store: Arc>>, peer_store: Arc>>, + config: Arc, logger: Arc, ) -> Self { Self { runtime, diff --git a/src/payment/bolt12.rs b/src/payment/bolt12.rs index c32b1a1a8..1ff8739be 100644 --- a/src/payment/bolt12.rs +++ b/src/payment/bolt12.rs @@ -11,7 +11,7 @@ use crate::config::LDK_PAYMENT_RETRY_TIMEOUT; use crate::error::Error; -use crate::logger::{log_error, log_info, FilesystemLogger, Logger}; +use crate::logger::{log_error, log_info, LdkLogger, Logger}; use crate::payment::store::{ PaymentDetails, PaymentDirection, PaymentKind, PaymentStatus, PaymentStore, }; @@ -39,15 +39,15 @@ use std::time::{Duration, SystemTime, UNIX_EPOCH}; pub struct Bolt12Payment { runtime: Arc>>>, channel_manager: Arc, - payment_store: Arc>>, - logger: Arc, + payment_store: Arc>>, + logger: Arc, } impl Bolt12Payment { pub(crate) fn new( runtime: Arc>>>, - channel_manager: Arc, - payment_store: Arc>>, logger: Arc, + channel_manager: Arc, payment_store: Arc>>, + logger: Arc, ) -> Self { Self { runtime, channel_manager, payment_store, logger } } diff --git a/src/payment/onchain.rs b/src/payment/onchain.rs index 23d6b6a0b..046d66c69 100644 --- a/src/payment/onchain.rs +++ b/src/payment/onchain.rs @@ -9,7 +9,7 @@ use crate::config::Config; use crate::error::Error; -use crate::logger::{log_info, FilesystemLogger, Logger}; +use crate::logger::{log_info, LdkLogger, Logger}; use crate::types::{ChannelManager, Wallet}; use crate::wallet::OnchainSendAmount; @@ -45,13 +45,13 @@ pub struct OnchainPayment { wallet: Arc, channel_manager: Arc, config: Arc, - logger: Arc, + logger: Arc, } impl OnchainPayment { pub(crate) fn new( runtime: Arc>>>, wallet: Arc, - channel_manager: Arc, config: Arc, logger: Arc, + channel_manager: Arc, config: Arc, logger: Arc, ) -> Self { Self { runtime, wallet, channel_manager, config, logger } } diff --git a/src/payment/spontaneous.rs b/src/payment/spontaneous.rs index cff630781..984619855 100644 --- a/src/payment/spontaneous.rs +++ b/src/payment/spontaneous.rs @@ -9,7 +9,7 @@ use crate::config::{Config, LDK_PAYMENT_RETRY_TIMEOUT}; use crate::error::Error; -use crate::logger::{log_error, log_info, FilesystemLogger, Logger}; +use crate::logger::{log_error, log_info, LdkLogger, Logger}; use crate::payment::store::{ PaymentDetails, PaymentDirection, PaymentKind, PaymentStatus, PaymentStore, }; @@ -38,17 +38,16 @@ pub struct SpontaneousPayment { runtime: Arc>>>, channel_manager: Arc, keys_manager: Arc, - payment_store: Arc>>, + payment_store: Arc>>, config: Arc, - logger: Arc, + logger: Arc, } impl SpontaneousPayment { pub(crate) fn new( runtime: Arc>>>, channel_manager: Arc, keys_manager: Arc, - payment_store: Arc>>, config: Arc, - logger: Arc, + payment_store: Arc>>, config: Arc, logger: Arc, ) -> Self { Self { runtime, channel_manager, keys_manager, payment_store, config, logger } } diff --git a/src/payment/store.rs b/src/payment/store.rs index fbeba669b..9ae137de9 100644 --- a/src/payment/store.rs +++ b/src/payment/store.rs @@ -9,7 +9,7 @@ use crate::hex_utils; use crate::io::{ PAYMENT_INFO_PERSISTENCE_PRIMARY_NAMESPACE, PAYMENT_INFO_PERSISTENCE_SECONDARY_NAMESPACE, }; -use crate::logger::{log_error, Logger}; +use crate::logger::{log_error, LdkLogger}; use crate::types::DynStore; use crate::Error; @@ -344,7 +344,7 @@ impl PaymentDetailsUpdate { pub(crate) struct PaymentStore where - L::Target: Logger, + L::Target: LdkLogger, { payments: Mutex>, kv_store: Arc, @@ -353,7 +353,7 @@ where impl PaymentStore where - L::Target: Logger, + L::Target: LdkLogger, { pub(crate) fn new(payments: Vec, kv_store: Arc, logger: L) -> Self { let payments = Mutex::new(HashMap::from_iter( diff --git a/src/payment/unified_qr.rs b/src/payment/unified_qr.rs index 1651358e5..92c405056 100644 --- a/src/payment/unified_qr.rs +++ b/src/payment/unified_qr.rs @@ -12,7 +12,7 @@ //! [BOLT 11]: https://github.com/lightning/bolts/blob/master/11-payment-encoding.md //! [BOLT 12]: https://github.com/lightning/bolts/blob/master/12-offer-encoding.md use crate::error::Error; -use crate::logger::{log_error, FilesystemLogger, Logger}; +use crate::logger::{log_error, LdkLogger, Logger}; use crate::payment::{Bolt11Payment, Bolt12Payment, OnchainPayment}; use crate::Config; @@ -50,13 +50,13 @@ pub struct UnifiedQrPayment { bolt11_invoice: Arc, bolt12_payment: Arc, config: Arc, - logger: Arc, + logger: Arc, } impl UnifiedQrPayment { pub(crate) fn new( onchain_payment: Arc, bolt11_invoice: Arc, - bolt12_payment: Arc, config: Arc, logger: Arc, + bolt12_payment: Arc, config: Arc, logger: Arc, ) -> Self { Self { onchain_payment, bolt11_invoice, bolt12_payment, config, logger } } diff --git a/src/peer_store.rs b/src/peer_store.rs index d4d6bbb97..4d1c65157 100644 --- a/src/peer_store.rs +++ b/src/peer_store.rs @@ -9,7 +9,7 @@ use crate::io::{ PEER_INFO_PERSISTENCE_KEY, PEER_INFO_PERSISTENCE_PRIMARY_NAMESPACE, PEER_INFO_PERSISTENCE_SECONDARY_NAMESPACE, }; -use crate::logger::{log_error, Logger}; +use crate::logger::{log_error, LdkLogger}; use crate::types::DynStore; use crate::{Error, SocketAddress}; @@ -24,7 +24,7 @@ use std::sync::{Arc, RwLock}; pub struct PeerStore where - L::Target: Logger, + L::Target: LdkLogger, { peers: RwLock>, kv_store: Arc, @@ -33,7 +33,7 @@ where impl PeerStore where - L::Target: Logger, + L::Target: LdkLogger, { pub(crate) fn new(kv_store: Arc, logger: L) -> Self { let peers = RwLock::new(HashMap::new()); @@ -92,7 +92,7 @@ where impl ReadableArgs<(Arc, L)> for PeerStore where - L::Target: Logger, + L::Target: LdkLogger, { #[inline] fn read( diff --git a/src/tx_broadcaster.rs b/src/tx_broadcaster.rs index 5aded03c6..09189b137 100644 --- a/src/tx_broadcaster.rs +++ b/src/tx_broadcaster.rs @@ -5,7 +5,7 @@ // http://opensource.org/licenses/MIT>, at your option. You may not use this file except in // accordance with one or both of these licenses. -use crate::logger::{log_error, Logger}; +use crate::logger::{log_error, LdkLogger}; use lightning::chain::chaininterface::BroadcasterInterface; @@ -20,7 +20,7 @@ const BCAST_PACKAGE_QUEUE_SIZE: usize = 50; pub(crate) struct TransactionBroadcaster where - L::Target: Logger, + L::Target: LdkLogger, { queue_sender: mpsc::Sender>, queue_receiver: Mutex>>, @@ -29,7 +29,7 @@ where impl TransactionBroadcaster where - L::Target: Logger, + L::Target: LdkLogger, { pub(crate) fn new(logger: L) -> Self { let (queue_sender, queue_receiver) = mpsc::channel(BCAST_PACKAGE_QUEUE_SIZE); @@ -43,7 +43,7 @@ where impl BroadcasterInterface for TransactionBroadcaster where - L::Target: Logger, + L::Target: LdkLogger, { fn broadcast_transactions(&self, txs: &[&Transaction]) { let package = txs.iter().map(|&t| t.clone()).collect::>(); diff --git a/src/types.rs b/src/types.rs index 2b350be63..1c9ab64b9 100644 --- a/src/types.rs +++ b/src/types.rs @@ -9,7 +9,7 @@ use crate::chain::ChainSource; use crate::config::ChannelConfig; use crate::fee_estimator::OnchainFeeEstimator; use crate::gossip::RuntimeSpawner; -use crate::logger::FilesystemLogger; +use crate::logger::Logger; use crate::message_handler::NodeCustomMessageHandler; use lightning::chain::chainmonitor; @@ -43,7 +43,7 @@ pub(crate) type ChainMonitor = chainmonitor::ChainMonitor< Arc, Arc, Arc, - Arc, + Arc, Arc, >; @@ -52,8 +52,8 @@ pub(crate) type PeerManager = lightning::ln::peer_handler::PeerManager< Arc, Arc, Arc, - Arc, - Arc>>, + Arc, + Arc>>, Arc, >; @@ -69,52 +69,48 @@ pub(crate) type ChannelManager = lightning::ln::channelmanager::ChannelManager< Arc, Arc, Arc, - Arc, + Arc, >; -pub(crate) type Broadcaster = crate::tx_broadcaster::TransactionBroadcaster>; +pub(crate) type Broadcaster = crate::tx_broadcaster::TransactionBroadcaster>; pub(crate) type Wallet = - crate::wallet::Wallet, Arc, Arc>; + crate::wallet::Wallet, Arc, Arc>; -pub(crate) type KeysManager = crate::wallet::WalletKeysManager< - Arc, - Arc, - Arc, ->; +pub(crate) type KeysManager = + crate::wallet::WalletKeysManager, Arc, Arc>; pub(crate) type Router = DefaultRouter< Arc, - Arc, + Arc, Arc, Arc>, ProbabilisticScoringFeeParameters, Scorer, >; -pub(crate) type Scorer = ProbabilisticScorer, Arc>; +pub(crate) type Scorer = ProbabilisticScorer, Arc>; -pub(crate) type Graph = gossip::NetworkGraph>; +pub(crate) type Graph = gossip::NetworkGraph>; -pub(crate) type UtxoLookup = - GossipVerifier, Arc>; +pub(crate) type UtxoLookup = GossipVerifier, Arc>; pub(crate) type P2PGossipSync = - lightning::routing::gossip::P2PGossipSync, Arc, Arc>; + lightning::routing::gossip::P2PGossipSync, Arc, Arc>; pub(crate) type RapidGossipSync = - lightning_rapid_gossip_sync::RapidGossipSync, Arc>; + lightning_rapid_gossip_sync::RapidGossipSync, Arc>; pub(crate) type GossipSync = lightning_background_processor::GossipSync< Arc, Arc, Arc, Arc, - Arc, + Arc, >; pub(crate) type OnionMessenger = lightning::onion_message::messenger::OnionMessenger< Arc, Arc, - Arc, + Arc, Arc, Arc, Arc, @@ -125,7 +121,7 @@ pub(crate) type OnionMessenger = lightning::onion_message::messenger::OnionMesse pub(crate) type MessageRouter = lightning::onion_message::messenger::DefaultMessageRouter< Arc, - Arc, + Arc, Arc, >; @@ -135,16 +131,16 @@ pub(crate) type Sweeper = OutputSweeper< Arc, Arc, Arc, - Arc, + Arc, Arc, >; pub(crate) type BumpTransactionEventHandler = lightning::events::bump_transaction::BumpTransactionEventHandler< Arc, - Arc, Arc>>, + Arc, Arc>>, Arc, - Arc, + Arc, >; /// A local, potentially user-provided, identifier of a channel. diff --git a/src/wallet/mod.rs b/src/wallet/mod.rs index b52d937a3..755328379 100644 --- a/src/wallet/mod.rs +++ b/src/wallet/mod.rs @@ -7,7 +7,7 @@ use persist::KVStoreWalletPersister; -use crate::logger::{log_debug, log_error, log_info, log_trace, Logger}; +use crate::logger::{log_debug, log_error, log_info, log_trace, LdkLogger}; use crate::fee_estimator::{ConfirmationTarget, FeeEstimator}; use crate::Error; @@ -59,7 +59,7 @@ pub(crate) struct Wallet where B::Target: BroadcasterInterface, E::Target: FeeEstimator, - L::Target: Logger, + L::Target: LdkLogger, { // A BDK on-chain wallet. inner: Mutex>, @@ -73,7 +73,7 @@ impl Wallet where B::Target: BroadcasterInterface, E::Target: FeeEstimator, - L::Target: Logger, + L::Target: LdkLogger, { pub(crate) fn new( wallet: bdk_wallet::PersistedWallet, @@ -450,7 +450,7 @@ impl Listen for Wallet where B::Target: BroadcasterInterface, E::Target: FeeEstimator, - L::Target: Logger, + L::Target: LdkLogger, { fn filtered_block_connected( &self, _header: &bitcoin::block::Header, @@ -510,7 +510,7 @@ impl WalletSource for Wallet where B::Target: BroadcasterInterface, E::Target: FeeEstimator, - L::Target: Logger, + L::Target: LdkLogger, { fn list_confirmed_utxos(&self) -> Result, ()> { let locked_wallet = self.inner.lock().unwrap(); @@ -652,7 +652,7 @@ pub(crate) struct WalletKeysManager where B::Target: BroadcasterInterface, E::Target: FeeEstimator, - L::Target: Logger, + L::Target: LdkLogger, { inner: KeysManager, wallet: Arc>, @@ -663,7 +663,7 @@ impl WalletKeysManager where B::Target: BroadcasterInterface, E::Target: FeeEstimator, - L::Target: Logger, + L::Target: LdkLogger, { /// Constructs a `WalletKeysManager` that overrides the destination and shutdown scripts. /// @@ -694,7 +694,7 @@ impl NodeSigner for WalletKeysManager where B::Target: BroadcasterInterface, E::Target: FeeEstimator, - L::Target: Logger, + L::Target: LdkLogger, { fn get_node_id(&self, recipient: Recipient) -> Result { self.inner.get_node_id(recipient) @@ -731,7 +731,7 @@ impl OutputSpender for WalletKeysManager where B::Target: BroadcasterInterface, E::Target: FeeEstimator, - L::Target: Logger, + L::Target: LdkLogger, { /// See [`KeysManager::spend_spendable_outputs`] for documentation on this method. fn spend_spendable_outputs( @@ -754,7 +754,7 @@ impl EntropySource for WalletKeysManager where B::Target: BroadcasterInterface, E::Target: FeeEstimator, - L::Target: Logger, + L::Target: LdkLogger, { fn get_secure_random_bytes(&self) -> [u8; 32] { self.inner.get_secure_random_bytes() @@ -765,7 +765,7 @@ impl SignerProvider for WalletKeysManager where B::Target: BroadcasterInterface, E::Target: FeeEstimator, - L::Target: Logger, + L::Target: LdkLogger, { type EcdsaSigner = InMemorySigner; @@ -816,7 +816,7 @@ impl ChangeDestinationSource for WalletKeysManager where B::Target: BroadcasterInterface, E::Target: FeeEstimator, - L::Target: Logger, + L::Target: LdkLogger, { fn get_change_destination_script(&self) -> Result { let address = self.wallet.get_new_internal_address().map_err(|e| { diff --git a/src/wallet/persist.rs b/src/wallet/persist.rs index 06af541a2..d9e4e7135 100644 --- a/src/wallet/persist.rs +++ b/src/wallet/persist.rs @@ -10,11 +10,9 @@ use crate::io::utils::{ write_bdk_wallet_indexer, write_bdk_wallet_local_chain, write_bdk_wallet_network, write_bdk_wallet_tx_graph, }; -use crate::logger::{log_error, FilesystemLogger}; +use crate::logger::{log_error, LdkLogger, Logger}; use crate::types::DynStore; -use lightning::util::logger::Logger; - use bdk_chain::Merge; use bdk_wallet::{ChangeSet, WalletPersister}; @@ -22,11 +20,11 @@ use std::sync::Arc; pub(crate) struct KVStoreWalletPersister { latest_change_set: Option, kv_store: Arc, - logger: Arc, + logger: Arc, } impl KVStoreWalletPersister { - pub(crate) fn new(kv_store: Arc, logger: Arc) -> Self { + pub(crate) fn new(kv_store: Arc, logger: Arc) -> Self { Self { latest_change_set: None, kv_store, logger } } } diff --git a/tests/common/mod.rs b/tests/common/mod.rs index 1e82fc60e..206fc59cd 100644 --- a/tests/common/mod.rs +++ b/tests/common/mod.rs @@ -12,8 +12,7 @@ use ldk_node::config::{Config, EsploraSyncConfig}; use ldk_node::io::sqlite_store::SqliteStore; use ldk_node::payment::{PaymentDirection, PaymentKind, PaymentStatus}; use ldk_node::{ - Builder, CustomTlvRecord, Event, LightningBalance, LogLevel, Node, NodeError, - PendingSweepBalance, + Builder, CustomTlvRecord, Event, LightningBalance, Node, NodeError, PendingSweepBalance, }; use lightning::ln::msgs::SocketAddress; @@ -237,8 +236,6 @@ pub(crate) fn random_config(anchor_channels: bool) -> Config { println!("Setting random LDK node alias: {:?}", alias); config.node_alias = alias; - config.log_level = LogLevel::Gossip; - config } diff --git a/tests/integration_tests_rust.rs b/tests/integration_tests_rust.rs index 7645747b3..9944ea4dc 100644 --- a/tests/integration_tests_rust.rs +++ b/tests/integration_tests_rust.rs @@ -14,11 +14,12 @@ use common::{ setup_node, setup_two_nodes, wait_for_tx, TestChainSource, TestSyncStore, }; -use ldk_node::config::EsploraSyncConfig; +use ldk_node::config::{EsploraSyncConfig, FilesystemLoggerConfig}; use ldk_node::payment::{PaymentKind, QrPaymentResult, SendingParameters}; use ldk_node::{Builder, Event, NodeError}; use lightning::ln::channelmanager::PaymentId; +use lightning::util::logger::Level; use lightning::util::persist::KVStore; use bitcoincore_rpc::RpcApi; @@ -221,6 +222,10 @@ fn start_stop_reinit() { sync_config.lightning_wallet_sync_interval_secs = 100000; setup_builder!(builder, config); builder.set_chain_source_esplora(esplora_url.clone(), Some(sync_config)); + builder.set_filesystem_logger(FilesystemLoggerConfig { + log_file_path: format!("{}/{}", config.storage_dir_path, "ldk_node.log"), + level: Level::Debug, + }); let node = builder.build_with_store(Arc::clone(&test_sync_store)).unwrap(); node.start().unwrap(); From 6d8c65efb18d46058f479d4d8314f57f3d574d0f Mon Sep 17 00:00:00 2001 From: Enigbe Ochekliye Date: Tue, 19 Nov 2024 00:01:37 +0100 Subject: [PATCH 2/5] feat(logger): add & impl LogWriter for Writer --- .../lightningdevkit/ldknode/AndroidLibTest.kt | 2 - .../lightningdevkit/ldknode/LibraryTest.kt | 2 - bindings/ldk_node.udl | 12 +++ src/lib.rs | 2 +- src/logger.rs | 90 +++++++++++++------ 5 files changed, 76 insertions(+), 32 deletions(-) diff --git a/bindings/kotlin/ldk-node-android/lib/src/androidTest/kotlin/org/lightningdevkit/ldknode/AndroidLibTest.kt b/bindings/kotlin/ldk-node-android/lib/src/androidTest/kotlin/org/lightningdevkit/ldknode/AndroidLibTest.kt index 763862a33..03c4b88a7 100644 --- a/bindings/kotlin/ldk-node-android/lib/src/androidTest/kotlin/org/lightningdevkit/ldknode/AndroidLibTest.kt +++ b/bindings/kotlin/ldk-node-android/lib/src/androidTest/kotlin/org/lightningdevkit/ldknode/AndroidLibTest.kt @@ -28,13 +28,11 @@ class AndroidLibTest { config1.storageDirPath = tmpDir1 config1.listeningAddresses = listOf(listenAddress1) config1.network = Network.REGTEST - config1.logLevel = LogLevel.TRACE val config2 = defaultConfig() config2.storageDirPath = tmpDir2 config2.listeningAddresses = listOf(listenAddress2) config2.network = Network.REGTEST - config2.logLevel = LogLevel.TRACE val builder1 = Builder.fromConfig(config1) val builder2 = Builder.fromConfig(config2) diff --git a/bindings/kotlin/ldk-node-jvm/lib/src/test/kotlin/org/lightningdevkit/ldknode/LibraryTest.kt b/bindings/kotlin/ldk-node-jvm/lib/src/test/kotlin/org/lightningdevkit/ldknode/LibraryTest.kt index 0feaccf1d..a0e9f7fbb 100644 --- a/bindings/kotlin/ldk-node-jvm/lib/src/test/kotlin/org/lightningdevkit/ldknode/LibraryTest.kt +++ b/bindings/kotlin/ldk-node-jvm/lib/src/test/kotlin/org/lightningdevkit/ldknode/LibraryTest.kt @@ -118,7 +118,6 @@ class LibraryTest { config1.storageDirPath = tmpDir1 config1.listeningAddresses = listOf(listenAddress1) config1.network = Network.REGTEST - config1.logLevel = LogLevel.TRACE println("Config 1: $config1") @@ -126,7 +125,6 @@ class LibraryTest { config2.storageDirPath = tmpDir2 config2.listeningAddresses = listOf(listenAddress2) config2.network = Network.REGTEST - config2.logLevel = LogLevel.TRACE println("Config 2: $config2") val builder1 = Builder.fromConfig(config1) diff --git a/bindings/ldk_node.udl b/bindings/ldk_node.udl index c85cd483f..971fb23d1 100644 --- a/bindings/ldk_node.udl +++ b/bindings/ldk_node.udl @@ -588,6 +588,18 @@ dictionary NodeAnnouncementInfo { sequence addresses; }; +dictionary LogRecord { + LdkLevel level; + string args; + string module_path; + u32 line; +}; + +[Trait] +interface LogWriter { + void log(LogRecord record); +}; + [Custom] typedef string Txid; diff --git a/src/lib.rs b/src/lib.rs index 3f51e0087..4dfff8aab 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -112,7 +112,7 @@ pub use event::Event; pub use io::utils::generate_entropy_mnemonic; pub use config::FilesystemLoggerConfig; -pub use logger::LdkLevel; +pub use logger::{LdkLevel, LogRecord, LogWriter}; #[cfg(feature = "uniffi")] use uniffi_types::*; diff --git a/src/logger.rs b/src/logger.rs index 904aeee98..69a71a5b7 100644 --- a/src/logger.rs +++ b/src/logger.rs @@ -5,11 +5,10 @@ // http://opensource.org/licenses/MIT>, at your option. You may not use this file except in // accordance with one or both of these licenses. -pub(crate) use lightning::util::logger::Logger as LdkLogger; +pub(crate) use lightning::util::logger::{Logger as LdkLogger, Record}; pub(crate) use lightning::{log_bytes, log_debug, log_error, log_info, log_trace}; pub use lightning::util::logger::Level as LdkLevel; -use lightning::util::logger::Record; use chrono::Utc; @@ -17,6 +16,37 @@ use std::fs; use std::io::Write; use std::path::Path; +/// A unit of logging output with Metadata to enable filtering Module_path, +/// file, line to inform on log's source. +pub struct LogRecord { + /// The verbosity level of the message. + pub level: LdkLevel, + /// The message body. + pub args: String, + /// The module path of the message. + pub module_path: String, + /// The line containing the message. + pub line: u32, +} + +impl<'a> From> for LogRecord { + fn from(record: Record) -> Self { + Self { + level: record.level, + args: record.args.to_string(), + module_path: record.module_path.to_string(), + line: record.line, + } + } +} + +/// LogWriter trait encapsulating the operations required of a +/// logger's writer. +pub trait LogWriter: Send + Sync { + /// Log the record. + fn log(&self, record: LogRecord); +} + pub(crate) struct FilesystemLogger { file_path: String, level: LdkLevel, @@ -28,6 +58,36 @@ pub(crate) enum Writer { FileWriter(FilesystemLogger), } +impl LogWriter for Writer { + fn log(&self, record: LogRecord) { + let raw_log = record.args.to_string(); + let log = format!( + "{} {:<5} [{}:{}] {}\n", + Utc::now().format("%Y-%m-%d %H:%M:%S"), + record.level.to_string(), + record.module_path, + record.line, + raw_log + ); + + match self { + Writer::FileWriter(fs_logger) => { + if record.level < fs_logger.level { + return; + } + + fs::OpenOptions::new() + .create(true) + .append(true) + .open(fs_logger.file_path.clone()) + .expect("Failed to open log file") + .write_all(log.as_bytes()) + .expect("Failed to write to log file") + }, + } + } +} + pub(crate) struct Logger { /// Specifies the logger's writer. writer: Writer, @@ -57,30 +117,6 @@ impl Logger { impl LdkLogger for Logger { fn log(&self, record: Record) { - let raw_log = record.args.to_string(); - let log = format!( - "{} {:<5} [{}:{}] {}\n", - Utc::now().format("%Y-%m-%d %H:%M:%S"), - record.level.to_string(), - record.module_path, - record.line, - raw_log - ); - - match &self.writer { - Writer::FileWriter(fs_logger) => { - if record.level < fs_logger.level { - return; - } - - fs::OpenOptions::new() - .create(true) - .append(true) - .open(fs_logger.file_path.clone()) - .expect("Failed to open log file") - .write_all(log.as_bytes()) - .expect("Failed to write to log file") - }, - } + self.writer.log(record.into()); } } From fe0f5473ea8505008d0b968876d63e9a559d4256 Mon Sep 17 00:00:00 2001 From: Enigbe Ochekliye Date: Tue, 19 Nov 2024 14:58:22 +0100 Subject: [PATCH 3/5] feat(facade): forward logs to `log` facade - modify tests to forward logs to mock in-memory `log` logger - correct "Forwards" spelling error --- Cargo.toml | 1 + bindings/ldk_node.udl | 5 +++ src/builder.rs | 19 +++++++++- src/config.rs | 9 ++++- src/lib.rs | 2 +- src/logger.rs | 21 +++++++++++ tests/common/mod.rs | 64 ++++++++++++++++++++++++++++++--- tests/integration_tests_rust.rs | 64 +++++++++++++++++++++++---------- 8 files changed, 158 insertions(+), 27 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 55ffdc5bf..c7a38896b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -80,6 +80,7 @@ libc = "0.2" uniffi = { version = "0.27.3", features = ["build"], optional = true } serde = { version = "1.0.210", default-features = false, features = ["std", "derive"] } serde_json = { version = "1.0.128", default-features = false, features = ["std"] } +log = { version = "0.4.22", features = ["std"]} vss-client = "0.3" prost = { version = "0.11.6", default-features = false} diff --git a/bindings/ldk_node.udl b/bindings/ldk_node.udl index 971fb23d1..78b6c297a 100644 --- a/bindings/ldk_node.udl +++ b/bindings/ldk_node.udl @@ -39,6 +39,10 @@ dictionary FilesystemLoggerConfig { LdkLevel level; }; +dictionary LogFacadeLoggerConfig { + LdkLevel level; +}; + interface Builder { constructor(); [Name=from_config] @@ -54,6 +58,7 @@ interface Builder { void set_liquidity_source_lsps2(SocketAddress address, PublicKey node_id, string? token); void set_storage_dir_path(string storage_dir_path); void set_filesystem_logger(FilesystemLoggerConfig fs_config); + void set_log_facade_logger(LogFacadeLoggerConfig lf_config); void set_network(Network network); [Throws=BuildError] void set_listening_addresses(sequence listening_addresses); diff --git a/src/builder.rs b/src/builder.rs index cc95ec0c6..e3d2bb56f 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -7,7 +7,8 @@ use crate::chain::{ChainSource, DEFAULT_ESPLORA_SERVER_URL}; use crate::config::{ - default_user_config, Config, EsploraSyncConfig, FilesystemLoggerConfig, WALLET_KEYS_SEED_LEN, + default_user_config, Config, EsploraSyncConfig, FilesystemLoggerConfig, LogFacadeLoggerConfig, + WALLET_KEYS_SEED_LEN, }; use crate::connection::ConnectionManager; @@ -111,6 +112,7 @@ impl Default for LiquiditySourceConfig { #[derive(Debug)] enum LogWriterConfig { File(FilesystemLoggerConfig), + Log(LogFacadeLoggerConfig), } impl Default for LogWriterConfig { @@ -320,6 +322,12 @@ impl NodeBuilder { self } + /// Configures the [`Node`] instance to write logs to the `log` facade. + pub fn set_log_facade_logger(&mut self, lf_config: LogFacadeLoggerConfig) -> &mut Self { + self.log_writer_config = Some(LogWriterConfig::Log(lf_config)); + self + } + /// Sets the Bitcoin network used. pub fn set_network(&mut self, network: Network) -> &mut Self { self.config.network = network; @@ -635,6 +643,11 @@ impl ArcedNodeBuilder { self.inner.write().unwrap().set_filesystem_logger(fs_config); } + /// Configures the [`Node`] instance to write logs to the `log` facade. + pub fn set_log_facade_logger(&self, lf_config: LogFacadeLoggerConfig) { + self.inner.write().unwrap().set_log_facade_logger(lf_config); + } + /// Sets the Bitcoin network used. pub fn set_network(&self, network: Network) { self.inner.write().unwrap().set_network(network); @@ -1268,6 +1281,10 @@ fn setup_logger(config: &LogWriterConfig) -> Result, BuildError> { .map_err(|_| BuildError::LoggerSetupFailed)?, )) }, + LogWriterConfig::Log(log_facade_logger_config) => Ok(Arc::new( + Logger::new_log_facade(log_facade_logger_config.level) + .map_err(|_| BuildError::LoggerSetupFailed)?, + )), } } diff --git a/src/config.rs b/src/config.rs index 1820a577a..9708bcdf4 100644 --- a/src/config.rs +++ b/src/config.rs @@ -430,7 +430,7 @@ impl From for LdkMaxDustHTLCExposure { } /// Configuration options for logging to the filesystem. -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct FilesystemLoggerConfig { /// The log file path. /// @@ -441,6 +441,13 @@ pub struct FilesystemLoggerConfig { pub level: LdkLevel, } +/// Configuration options for logging to the `log` facade. +#[derive(Debug, Clone)] +pub struct LogFacadeLoggerConfig { + /// This specifies the log level. + pub level: LdkLevel, +} + impl Default for FilesystemLoggerConfig { fn default() -> Self { let log_file_path = format!("{}/{}", DEFAULT_STORAGE_DIR_PATH, DEFAULT_LOG_FILE_PATH); diff --git a/src/lib.rs b/src/lib.rs index 4dfff8aab..9b8085efd 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -111,7 +111,7 @@ pub use event::Event; pub use io::utils::generate_entropy_mnemonic; -pub use config::FilesystemLoggerConfig; +pub use config::{FilesystemLoggerConfig, LogFacadeLoggerConfig}; pub use logger::{LdkLevel, LogRecord, LogWriter}; #[cfg(feature = "uniffi")] diff --git a/src/logger.rs b/src/logger.rs index 69a71a5b7..0e81b6a9e 100644 --- a/src/logger.rs +++ b/src/logger.rs @@ -11,6 +11,7 @@ pub(crate) use lightning::{log_bytes, log_debug, log_error, log_info, log_trace} pub use lightning::util::logger::Level as LdkLevel; use chrono::Utc; +use log::{debug, error, info, trace, warn}; use std::fs; use std::io::Write; @@ -52,10 +53,16 @@ pub(crate) struct FilesystemLogger { level: LdkLevel, } +pub(crate) struct LogFacadeLogger { + level: LdkLevel, +} + /// Defines a writer for [`Logger`]. pub(crate) enum Writer { /// Writes logs to the file system. FileWriter(FilesystemLogger), + /// Forwards logs to the `log` facade. + LogFacadeWriter(LogFacadeLogger), } impl LogWriter for Writer { @@ -84,6 +91,14 @@ impl LogWriter for Writer { .write_all(log.as_bytes()) .expect("Failed to write to log file") }, + Writer::LogFacadeWriter(log_facade_logger) => match log_facade_logger.level { + LdkLevel::Gossip => trace!("{}", log), + LdkLevel::Trace => trace!("{}", log), + LdkLevel::Debug => debug!("{}", log), + LdkLevel::Info => info!("{}", log), + LdkLevel::Warn => warn!("{}", log), + LdkLevel::Error => error!("{}", log), + }, } } } @@ -113,6 +128,12 @@ impl Logger { Ok(Self { writer: Writer::FileWriter(fs_writer) }) } + + pub fn new_log_facade(level: LdkLevel) -> Result { + let log_facade_writer = LogFacadeLogger { level }; + + Ok(Self { writer: Writer::LogFacadeWriter(log_facade_writer) }) + } } impl LdkLogger for Logger { diff --git a/tests/common/mod.rs b/tests/common/mod.rs index 206fc59cd..b800465d6 100644 --- a/tests/common/mod.rs +++ b/tests/common/mod.rs @@ -12,7 +12,8 @@ use ldk_node::config::{Config, EsploraSyncConfig}; use ldk_node::io::sqlite_store::SqliteStore; use ldk_node::payment::{PaymentDirection, PaymentKind, PaymentStatus}; use ldk_node::{ - Builder, CustomTlvRecord, Event, LightningBalance, Node, NodeError, PendingSweepBalance, + Builder, CustomTlvRecord, Event, FilesystemLoggerConfig, LightningBalance, + LogFacadeLoggerConfig, Node, NodeError, PendingSweepBalance, }; use lightning::ln::msgs::SocketAddress; @@ -39,9 +40,11 @@ use electrum_client::ElectrumApi; use rand::distributions::Alphanumeric; use rand::{thread_rng, Rng}; +use log::{LevelFilter, Log}; + use std::env; use std::path::PathBuf; -use std::sync::{Arc, RwLock}; +use std::sync::{Arc, Mutex, RwLock}; use std::time::Duration; macro_rules! expect_event { @@ -250,6 +253,47 @@ pub(crate) enum TestChainSource<'a> { BitcoindRpc(&'a BitcoinD), } +#[derive(Clone)] +pub(crate) enum TestLogWriter { + File(FilesystemLoggerConfig), + LogFacade(LogFacadeLoggerConfig), +} + +/// Simple in-memory mock `log` logger for tests. +pub(crate) struct MockLogger { + logs: Arc>>, +} + +impl MockLogger { + pub fn new() -> Self { + Self { logs: Arc::new(Mutex::new(Vec::new())) } + } + + pub fn retrieve_logs(&self) -> Vec { + self.logs.lock().unwrap().clone() + } +} + +impl Log for MockLogger { + fn log(&self, record: &log::Record) { + let message = format!("[{}] {}", record.level(), record.args()); + self.logs.lock().unwrap().push(message); + } + + fn enabled(&self, _metadata: &log::Metadata) -> bool { + true + } + + fn flush(&self) {} +} + +pub(crate) fn init_mock_logger(level: LevelFilter) -> Arc { + let logger = Arc::new(MockLogger::new()); + log::set_boxed_logger(Box::new(logger.clone())).unwrap(); + log::set_max_level(level); + logger +} + macro_rules! setup_builder { ($builder: ident, $config: expr) => { #[cfg(feature = "uniffi")] @@ -263,11 +307,11 @@ pub(crate) use setup_builder; pub(crate) fn setup_two_nodes( chain_source: &TestChainSource, allow_0conf: bool, anchor_channels: bool, - anchors_trusted_no_reserve: bool, + anchors_trusted_no_reserve: bool, log_writer: TestLogWriter, ) -> (TestNode, TestNode) { println!("== Node A =="); let config_a = random_config(anchor_channels); - let node_a = setup_node(chain_source, config_a, None); + let node_a = setup_node(chain_source, config_a, None, log_writer.clone()); println!("\n== Node B =="); let mut config_b = random_config(anchor_channels); @@ -282,12 +326,13 @@ pub(crate) fn setup_two_nodes( .trusted_peers_no_reserve .push(node_a.node_id()); } - let node_b = setup_node(chain_source, config_b, None); + let node_b = setup_node(chain_source, config_b, None, log_writer); (node_a, node_b) } pub(crate) fn setup_node( chain_source: &TestChainSource, config: Config, seed_bytes: Option>, + log_writer: TestLogWriter, ) -> TestNode { setup_builder!(builder, config); match chain_source { @@ -308,6 +353,15 @@ pub(crate) fn setup_node( }, } + match log_writer { + TestLogWriter::File(fs_config) => { + builder.set_filesystem_logger(fs_config); + }, + TestLogWriter::LogFacade(lf_config) => { + builder.set_log_facade_logger(lf_config); + }, + } + if let Some(seed) = seed_bytes { builder.set_entropy_seed_bytes(seed).unwrap(); } diff --git a/tests/integration_tests_rust.rs b/tests/integration_tests_rust.rs index 9944ea4dc..8a02c6b5c 100644 --- a/tests/integration_tests_rust.rs +++ b/tests/integration_tests_rust.rs @@ -9,14 +9,15 @@ mod common; use common::{ do_channel_full_cycle, expect_channel_ready_event, expect_event, expect_payment_received_event, - expect_payment_successful_event, generate_blocks_and_wait, open_channel, + expect_payment_successful_event, generate_blocks_and_wait, init_mock_logger, open_channel, premine_and_distribute_funds, random_config, setup_bitcoind_and_electrsd, setup_builder, - setup_node, setup_two_nodes, wait_for_tx, TestChainSource, TestSyncStore, + setup_node, setup_two_nodes, wait_for_tx, TestChainSource, TestLogWriter, TestSyncStore, }; use ldk_node::config::{EsploraSyncConfig, FilesystemLoggerConfig}; use ldk_node::payment::{PaymentKind, QrPaymentResult, SendingParameters}; -use ldk_node::{Builder, Event, NodeError}; +use ldk_node::LdkLevel; +use ldk_node::{Builder, Event, LogFacadeLoggerConfig, NodeError}; use lightning::ln::channelmanager::PaymentId; use lightning::util::logger::Level; @@ -33,7 +34,8 @@ use std::sync::Arc; fn channel_full_cycle() { let (bitcoind, electrsd) = setup_bitcoind_and_electrsd(); let chain_source = TestChainSource::Esplora(&electrsd); - let (node_a, node_b) = setup_two_nodes(&chain_source, false, true, false); + let log_writer = TestLogWriter::File(FilesystemLoggerConfig::default()); + let (node_a, node_b) = setup_two_nodes(&chain_source, false, true, false, log_writer); do_channel_full_cycle(node_a, node_b, &bitcoind.client, &electrsd.client, false, true, false); } @@ -41,7 +43,8 @@ fn channel_full_cycle() { fn channel_full_cycle_bitcoind() { let (bitcoind, electrsd) = setup_bitcoind_and_electrsd(); let chain_source = TestChainSource::BitcoindRpc(&bitcoind); - let (node_a, node_b) = setup_two_nodes(&chain_source, false, true, false); + let log_writer = TestLogWriter::File(FilesystemLoggerConfig::default()); + let (node_a, node_b) = setup_two_nodes(&chain_source, false, true, false, log_writer); do_channel_full_cycle(node_a, node_b, &bitcoind.client, &electrsd.client, false, true, false); } @@ -49,7 +52,8 @@ fn channel_full_cycle_bitcoind() { fn channel_full_cycle_force_close() { let (bitcoind, electrsd) = setup_bitcoind_and_electrsd(); let chain_source = TestChainSource::Esplora(&electrsd); - let (node_a, node_b) = setup_two_nodes(&chain_source, false, true, false); + let log_writer = TestLogWriter::File(FilesystemLoggerConfig::default()); + let (node_a, node_b) = setup_two_nodes(&chain_source, false, true, false, log_writer); do_channel_full_cycle(node_a, node_b, &bitcoind.client, &electrsd.client, false, true, true); } @@ -57,7 +61,8 @@ fn channel_full_cycle_force_close() { fn channel_full_cycle_force_close_trusted_no_reserve() { let (bitcoind, electrsd) = setup_bitcoind_and_electrsd(); let chain_source = TestChainSource::Esplora(&electrsd); - let (node_a, node_b) = setup_two_nodes(&chain_source, false, true, true); + let log_writer = TestLogWriter::File(FilesystemLoggerConfig::default()); + let (node_a, node_b) = setup_two_nodes(&chain_source, false, true, true, log_writer); do_channel_full_cycle(node_a, node_b, &bitcoind.client, &electrsd.client, false, true, true); } @@ -65,7 +70,8 @@ fn channel_full_cycle_force_close_trusted_no_reserve() { fn channel_full_cycle_0conf() { let (bitcoind, electrsd) = setup_bitcoind_and_electrsd(); let chain_source = TestChainSource::Esplora(&electrsd); - let (node_a, node_b) = setup_two_nodes(&chain_source, true, true, false); + let log_writer = TestLogWriter::File(FilesystemLoggerConfig::default()); + let (node_a, node_b) = setup_two_nodes(&chain_source, true, true, false, log_writer); do_channel_full_cycle(node_a, node_b, &bitcoind.client, &electrsd.client, true, true, false) } @@ -73,7 +79,8 @@ fn channel_full_cycle_0conf() { fn channel_full_cycle_legacy_staticremotekey() { let (bitcoind, electrsd) = setup_bitcoind_and_electrsd(); let chain_source = TestChainSource::Esplora(&electrsd); - let (node_a, node_b) = setup_two_nodes(&chain_source, false, false, false); + let log_writer = TestLogWriter::File(FilesystemLoggerConfig::default()); + let (node_a, node_b) = setup_two_nodes(&chain_source, false, false, false, log_writer); do_channel_full_cycle(node_a, node_b, &bitcoind.client, &electrsd.client, false, false, false); } @@ -81,7 +88,8 @@ fn channel_full_cycle_legacy_staticremotekey() { fn channel_open_fails_when_funds_insufficient() { let (bitcoind, electrsd) = setup_bitcoind_and_electrsd(); let chain_source = TestChainSource::Esplora(&electrsd); - let (node_a, node_b) = setup_two_nodes(&chain_source, false, true, false); + let log_writer = TestLogWriter::File(FilesystemLoggerConfig::default()); + let (node_a, node_b) = setup_two_nodes(&chain_source, false, true, false, log_writer); let addr_a = node_a.onchain_payment().new_address().unwrap(); let addr_b = node_b.onchain_payment().new_address().unwrap(); @@ -286,7 +294,8 @@ fn start_stop_reinit() { fn onchain_spend_receive() { let (bitcoind, electrsd) = setup_bitcoind_and_electrsd(); let chain_source = TestChainSource::Esplora(&electrsd); - let (node_a, node_b) = setup_two_nodes(&chain_source, false, true, false); + let log_writer = TestLogWriter::File(FilesystemLoggerConfig::default()); + let (node_a, node_b) = setup_two_nodes(&chain_source, false, true, false, log_writer); let addr_a = node_a.onchain_payment().new_address().unwrap(); let addr_b = node_b.onchain_payment().new_address().unwrap(); @@ -388,7 +397,9 @@ fn onchain_wallet_recovery() { let seed_bytes = vec![42u8; 64]; let original_config = random_config(true); - let original_node = setup_node(&chain_source, original_config, Some(seed_bytes.clone())); + let log_writer = TestLogWriter::File(FilesystemLoggerConfig::default()); + let original_node = + setup_node(&chain_source, original_config, Some(seed_bytes.clone()), log_writer); let premine_amount_sat = 100_000; @@ -433,7 +444,8 @@ fn onchain_wallet_recovery() { // Now we start from scratch, only the seed remains the same. let recovered_config = random_config(true); - let recovered_node = setup_node(&chain_source, recovered_config, Some(seed_bytes)); + let log_writer = TestLogWriter::File(FilesystemLoggerConfig::default()); + let recovered_node = setup_node(&chain_source, recovered_config, Some(seed_bytes), log_writer); recovered_node.sync_wallets().unwrap(); assert_eq!( @@ -476,7 +488,8 @@ fn sign_verify_msg() { let (_bitcoind, electrsd) = setup_bitcoind_and_electrsd(); let config = random_config(true); let chain_source = TestChainSource::Esplora(&electrsd); - let node = setup_node(&chain_source, config, None); + let log_writer = TestLogWriter::File(FilesystemLoggerConfig::default()); + let node = setup_node(&chain_source, config, None, log_writer); // Tests arbitrary message signing and later verification let msg = "OK computer".as_bytes(); @@ -494,7 +507,8 @@ fn connection_restart_behavior() { fn do_connection_restart_behavior(persist: bool) { let (_bitcoind, electrsd) = setup_bitcoind_and_electrsd(); let chain_source = TestChainSource::Esplora(&electrsd); - let (node_a, node_b) = setup_two_nodes(&chain_source, false, false, false); + let log_writer = TestLogWriter::File(FilesystemLoggerConfig::default()); + let (node_a, node_b) = setup_two_nodes(&chain_source, false, false, false, log_writer); let node_id_a = node_a.node_id(); let node_id_b = node_b.node_id(); @@ -546,7 +560,8 @@ fn do_connection_restart_behavior(persist: bool) { fn concurrent_connections_succeed() { let (_bitcoind, electrsd) = setup_bitcoind_and_electrsd(); let chain_source = TestChainSource::Esplora(&electrsd); - let (node_a, node_b) = setup_two_nodes(&chain_source, false, true, false); + let log_writer = TestLogWriter::File(FilesystemLoggerConfig::default()); + let (node_a, node_b) = setup_two_nodes(&chain_source, false, true, false, log_writer); let node_a = Arc::new(node_a); let node_b = Arc::new(node_b); @@ -577,7 +592,8 @@ fn concurrent_connections_succeed() { fn simple_bolt12_send_receive() { let (bitcoind, electrsd) = setup_bitcoind_and_electrsd(); let chain_source = TestChainSource::Esplora(&electrsd); - let (node_a, node_b) = setup_two_nodes(&chain_source, false, true, false); + let log_writer = TestLogWriter::File(FilesystemLoggerConfig::default()); + let (node_a, node_b) = setup_two_nodes(&chain_source, false, true, false, log_writer); let address_a = node_a.onchain_payment().new_address().unwrap(); let premine_amount_sat = 5_000_000; @@ -785,7 +801,8 @@ fn simple_bolt12_send_receive() { fn generate_bip21_uri() { let (bitcoind, electrsd) = setup_bitcoind_and_electrsd(); let chain_source = TestChainSource::Esplora(&electrsd); - let (node_a, node_b) = setup_two_nodes(&chain_source, false, true, false); + let log_writer = TestLogWriter::File(FilesystemLoggerConfig::default()); + let (node_a, node_b) = setup_two_nodes(&chain_source, false, true, false, log_writer); let address_a = node_a.onchain_payment().new_address().unwrap(); let premined_sats = 5_000_000; @@ -827,7 +844,10 @@ fn generate_bip21_uri() { fn unified_qr_send_receive() { let (bitcoind, electrsd) = setup_bitcoind_and_electrsd(); let chain_source = TestChainSource::Esplora(&electrsd); - let (node_a, node_b) = setup_two_nodes(&chain_source, false, true, false); + + let mock_logger = init_mock_logger(log::LevelFilter::Trace); + let log_writer = TestLogWriter::LogFacade(LogFacadeLoggerConfig { level: LdkLevel::Trace }); + let (node_a, node_b) = setup_two_nodes(&chain_source, false, true, false, log_writer); let address_a = node_a.onchain_payment().new_address().unwrap(); let premined_sats = 5_000_000; @@ -930,4 +950,10 @@ fn unified_qr_send_receive() { assert_eq!(node_b.list_balances().total_onchain_balance_sats, 800_000); assert_eq!(node_b.list_balances().total_lightning_balance_sats, 200_000); + + assert!(mock_logger + .retrieve_logs() + .last() + .unwrap() + .contains("Incremental sync of on-chain wallet finished"),); } From 63f08b2a95a12ed321e0df7c042fcecd18e058c9 Mon Sep 17 00:00:00 2001 From: Enigbe Ochekliye Date: Wed, 20 Nov 2024 12:17:38 +0100 Subject: [PATCH 4/5] feat(custom): forward logs to a custom logger * Add support for user-provided custom logger to write logs to, allowing users to provide any logger that implements LogWriter * Add test to cover this use case, implementing Log- Writer for the mock, in-memory MockLogger. * Fix setting log's global logger twice. * Revert the renaming of LogLevel to LdkLevel. --- bindings/ldk_node.udl | 8 +++---- src/builder.rs | 18 +++++++++++++- src/config.rs | 8 +++---- src/lib.rs | 2 +- src/logger.rs | 38 +++++++++++++++++++---------- tests/common/mod.rs | 42 ++++++++++++++++++++++++++++++--- tests/integration_tests_rust.rs | 23 ++++++++++++------ 7 files changed, 106 insertions(+), 33 deletions(-) diff --git a/bindings/ldk_node.udl b/bindings/ldk_node.udl index 78b6c297a..f1f34395f 100644 --- a/bindings/ldk_node.udl +++ b/bindings/ldk_node.udl @@ -25,7 +25,7 @@ dictionary EsploraSyncConfig { u64 fee_rate_cache_update_interval_secs; }; -enum LdkLevel { +enum LogLevel { "Gossip", "Trace", "Debug", @@ -36,11 +36,11 @@ enum LdkLevel { dictionary FilesystemLoggerConfig { string log_file_path; - LdkLevel level; + LogLevel level; }; dictionary LogFacadeLoggerConfig { - LdkLevel level; + LogLevel level; }; interface Builder { @@ -594,7 +594,7 @@ dictionary NodeAnnouncementInfo { }; dictionary LogRecord { - LdkLevel level; + LogLevel level; string args; string module_path; u32 line; diff --git a/src/builder.rs b/src/builder.rs index e3d2bb56f..a8bd78110 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -19,7 +19,7 @@ use crate::io::sqlite_store::SqliteStore; use crate::io::utils::{read_node_metrics, write_node_metrics}; use crate::io::vss_store::VssStore; use crate::liquidity::LiquiditySource; -use crate::logger::{log_error, log_info, LdkLogger, Logger}; +use crate::logger::{log_error, log_info, LdkLogger, LogWriter, Logger}; use crate::message_handler::NodeCustomMessageHandler; use crate::payment::store::PaymentStore; use crate::peer_store::PeerStore; @@ -113,6 +113,7 @@ impl Default for LiquiditySourceConfig { enum LogWriterConfig { File(FilesystemLoggerConfig), Log(LogFacadeLoggerConfig), + Custom(Arc), } impl Default for LogWriterConfig { @@ -328,6 +329,12 @@ impl NodeBuilder { self } + /// Configures the [`Node`] instance to write logs to the provided custom log writer. + pub fn set_custom_logger(&mut self, log_writer: Arc) -> &mut Self { + self.log_writer_config = Some(LogWriterConfig::Custom(log_writer)); + self + } + /// Sets the Bitcoin network used. pub fn set_network(&mut self, network: Network) -> &mut Self { self.config.network = network; @@ -648,6 +655,11 @@ impl ArcedNodeBuilder { self.inner.write().unwrap().set_log_facade_logger(lf_config); } + /// Configures the [`Node`] instance to write logs to the provided custom log writer. + pub fn set_custom_logger(&self, log_writer: Arc) { + self.inner.write().unwrap().set_custom_logger(log_writer); + } + /// Sets the Bitcoin network used. pub fn set_network(&self, network: Network) { self.inner.write().unwrap().set_network(network); @@ -1285,6 +1297,10 @@ fn setup_logger(config: &LogWriterConfig) -> Result, BuildError> { Logger::new_log_facade(log_facade_logger_config.level) .map_err(|_| BuildError::LoggerSetupFailed)?, )), + LogWriterConfig::Custom(custom_log_writer) => Ok(Arc::new( + Logger::new_custom_writer(custom_log_writer.clone()) + .map_err(|_| BuildError::LoggerSetupFailed)?, + )), } } diff --git a/src/config.rs b/src/config.rs index 9708bcdf4..e89644994 100644 --- a/src/config.rs +++ b/src/config.rs @@ -7,7 +7,7 @@ //! Objects for configuring the node. -use crate::logger::LdkLevel; +use crate::logger::LogLevel; use crate::payment::SendingParameters; use lightning::ln::msgs::SocketAddress; @@ -30,7 +30,7 @@ const DEFAULT_FEE_RATE_CACHE_UPDATE_INTERVAL_SECS: u64 = 60 * 10; const DEFAULT_PROBING_LIQUIDITY_LIMIT_MULTIPLIER: u64 = 3; const DEFAULT_ANCHOR_PER_CHANNEL_RESERVE_SATS: u64 = 25_000; const DEFAULT_LOG_FILE_PATH: &'static str = "ldk_node.log"; -const DEFAULT_LOG_LEVEL: LdkLevel = LdkLevel::Debug; +const DEFAULT_LOG_LEVEL: LogLevel = LogLevel::Debug; // The 'stop gap' parameter used by BDK's wallet sync. This seems to configure the threshold // number of derivation indexes after which BDK stops looking for new scripts belonging to the wallet. @@ -438,14 +438,14 @@ pub struct FilesystemLoggerConfig { /// directory, i.e. [`Config::storage_dir_path`], is preferred. pub log_file_path: String, /// This specifies the log level. - pub level: LdkLevel, + pub level: LogLevel, } /// Configuration options for logging to the `log` facade. #[derive(Debug, Clone)] pub struct LogFacadeLoggerConfig { /// This specifies the log level. - pub level: LdkLevel, + pub level: LogLevel, } impl Default for FilesystemLoggerConfig { diff --git a/src/lib.rs b/src/lib.rs index 9b8085efd..8e99165f7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -112,7 +112,7 @@ pub use event::Event; pub use io::utils::generate_entropy_mnemonic; pub use config::{FilesystemLoggerConfig, LogFacadeLoggerConfig}; -pub use logger::{LdkLevel, LogRecord, LogWriter}; +pub use logger::{LogLevel, LogRecord, LogWriter}; #[cfg(feature = "uniffi")] use uniffi_types::*; diff --git a/src/logger.rs b/src/logger.rs index 0e81b6a9e..9916fdd93 100644 --- a/src/logger.rs +++ b/src/logger.rs @@ -8,20 +8,22 @@ pub(crate) use lightning::util::logger::{Logger as LdkLogger, Record}; pub(crate) use lightning::{log_bytes, log_debug, log_error, log_info, log_trace}; -pub use lightning::util::logger::Level as LdkLevel; +pub use lightning::util::logger::Level as LogLevel; use chrono::Utc; use log::{debug, error, info, trace, warn}; +use std::fmt::Debug; use std::fs; use std::io::Write; use std::path::Path; +use std::sync::Arc; /// A unit of logging output with Metadata to enable filtering Module_path, /// file, line to inform on log's source. pub struct LogRecord { /// The verbosity level of the message. - pub level: LdkLevel, + pub level: LogLevel, /// The message body. pub args: String, /// The module path of the message. @@ -43,26 +45,31 @@ impl<'a> From> for LogRecord { /// LogWriter trait encapsulating the operations required of a /// logger's writer. -pub trait LogWriter: Send + Sync { +pub trait LogWriter: Send + Sync + Debug { /// Log the record. fn log(&self, record: LogRecord); } +#[derive(Debug)] pub(crate) struct FilesystemLogger { file_path: String, - level: LdkLevel, + level: LogLevel, } +#[derive(Debug)] pub(crate) struct LogFacadeLogger { - level: LdkLevel, + level: LogLevel, } /// Defines a writer for [`Logger`]. +#[derive(Debug)] pub(crate) enum Writer { /// Writes logs to the file system. FileWriter(FilesystemLogger), /// Forwards logs to the `log` facade. LogFacadeWriter(LogFacadeLogger), + /// Forwards logs to custom writer. + CustomWriter(Arc), } impl LogWriter for Writer { @@ -92,13 +99,14 @@ impl LogWriter for Writer { .expect("Failed to write to log file") }, Writer::LogFacadeWriter(log_facade_logger) => match log_facade_logger.level { - LdkLevel::Gossip => trace!("{}", log), - LdkLevel::Trace => trace!("{}", log), - LdkLevel::Debug => debug!("{}", log), - LdkLevel::Info => info!("{}", log), - LdkLevel::Warn => warn!("{}", log), - LdkLevel::Error => error!("{}", log), + LogLevel::Gossip => trace!("{}", log), + LogLevel::Trace => trace!("{}", log), + LogLevel::Debug => debug!("{}", log), + LogLevel::Info => info!("{}", log), + LogLevel::Warn => warn!("{}", log), + LogLevel::Error => error!("{}", log), }, + Writer::CustomWriter(custom_logger) => custom_logger.log(record), } } } @@ -111,7 +119,7 @@ pub(crate) struct Logger { impl Logger { /// Creates a new logger with a filesystem writer. The parameters to this function /// are the path to the log file, and the log level. - pub fn new_fs_writer(log_file_path: String, level: LdkLevel) -> Result { + pub fn new_fs_writer(log_file_path: String, level: LogLevel) -> Result { if let Some(parent_dir) = Path::new(&log_file_path).parent() { fs::create_dir_all(parent_dir) .map_err(|e| eprintln!("ERROR: Failed to create log parent directory: {}", e))?; @@ -129,11 +137,15 @@ impl Logger { Ok(Self { writer: Writer::FileWriter(fs_writer) }) } - pub fn new_log_facade(level: LdkLevel) -> Result { + pub fn new_log_facade(level: LogLevel) -> Result { let log_facade_writer = LogFacadeLogger { level }; Ok(Self { writer: Writer::LogFacadeWriter(log_facade_writer) }) } + + pub fn new_custom_writer(log_writer: Arc) -> Result { + Ok(Self { writer: Writer::CustomWriter(log_writer) }) + } } impl LdkLogger for Logger { diff --git a/tests/common/mod.rs b/tests/common/mod.rs index b800465d6..8116da54a 100644 --- a/tests/common/mod.rs +++ b/tests/common/mod.rs @@ -8,12 +8,13 @@ #![cfg(any(test, cln_test, vss_test))] #![allow(dead_code)] +use chrono::Utc; use ldk_node::config::{Config, EsploraSyncConfig}; use ldk_node::io::sqlite_store::SqliteStore; use ldk_node::payment::{PaymentDirection, PaymentKind, PaymentStatus}; use ldk_node::{ Builder, CustomTlvRecord, Event, FilesystemLoggerConfig, LightningBalance, - LogFacadeLoggerConfig, Node, NodeError, PendingSweepBalance, + LogFacadeLoggerConfig, LogRecord, LogWriter, Node, NodeError, PendingSweepBalance, }; use lightning::ln::msgs::SocketAddress; @@ -257,9 +258,11 @@ pub(crate) enum TestChainSource<'a> { pub(crate) enum TestLogWriter { File(FilesystemLoggerConfig), LogFacade(LogFacadeLoggerConfig), + Custom(Arc), } /// Simple in-memory mock `log` logger for tests. +#[derive(Debug)] pub(crate) struct MockLogger { logs: Arc>>, } @@ -274,9 +277,18 @@ impl MockLogger { } } +/// [`MockLogger`] as `log` logger - destination for [`Writer::LogFacadeWriter`] +/// to write logs to. +/// +/// [`Writer::LogFacadeWriter`]: ldk_node::logger::Writer::LogFacadeWriter impl Log for MockLogger { fn log(&self, record: &log::Record) { - let message = format!("[{}] {}", record.level(), record.args()); + let message = format!( + "{} [{}] {}", + Utc::now().format("%Y-%m-%d %H:%M:%S"), + record.level(), + record.args() + ); self.logs.lock().unwrap().push(message); } @@ -287,13 +299,34 @@ impl Log for MockLogger { fn flush(&self) {} } -pub(crate) fn init_mock_logger(level: LevelFilter) -> Arc { +/// [`MockLogger`] as custom logger - a destination for [`Writer::CustomWriter`] +/// to write logs to. +/// +/// [`Writer::CustomWriter`]: ldk_node::logger::Writer::CustomWriter +impl LogWriter for MockLogger { + fn log(&self, record: LogRecord) { + let message = format!( + "{} [{}] {}", + Utc::now().format("%Y-%m-%d %H:%M:%S"), + record.level, + record.args + ); + self.logs.lock().unwrap().push(message); + } +} + +pub(crate) fn init_log_logger(level: LevelFilter) -> Arc { let logger = Arc::new(MockLogger::new()); log::set_boxed_logger(Box::new(logger.clone())).unwrap(); log::set_max_level(level); logger } +pub(crate) fn init_custom_logger() -> Arc { + let logger = Arc::new(MockLogger::new()); + logger +} + macro_rules! setup_builder { ($builder: ident, $config: expr) => { #[cfg(feature = "uniffi")] @@ -360,6 +393,9 @@ pub(crate) fn setup_node( TestLogWriter::LogFacade(lf_config) => { builder.set_log_facade_logger(lf_config); }, + TestLogWriter::Custom(log_writer) => { + builder.set_custom_logger(log_writer); + }, } if let Some(seed) = seed_bytes { diff --git a/tests/integration_tests_rust.rs b/tests/integration_tests_rust.rs index 8a02c6b5c..0d25f0b16 100644 --- a/tests/integration_tests_rust.rs +++ b/tests/integration_tests_rust.rs @@ -9,14 +9,15 @@ mod common; use common::{ do_channel_full_cycle, expect_channel_ready_event, expect_event, expect_payment_received_event, - expect_payment_successful_event, generate_blocks_and_wait, init_mock_logger, open_channel, - premine_and_distribute_funds, random_config, setup_bitcoind_and_electrsd, setup_builder, - setup_node, setup_two_nodes, wait_for_tx, TestChainSource, TestLogWriter, TestSyncStore, + expect_payment_successful_event, generate_blocks_and_wait, init_custom_logger, init_log_logger, + open_channel, premine_and_distribute_funds, random_config, setup_bitcoind_and_electrsd, + setup_builder, setup_node, setup_two_nodes, wait_for_tx, TestChainSource, TestLogWriter, + TestSyncStore, }; use ldk_node::config::{EsploraSyncConfig, FilesystemLoggerConfig}; use ldk_node::payment::{PaymentKind, QrPaymentResult, SendingParameters}; -use ldk_node::LdkLevel; +use ldk_node::LogLevel; use ldk_node::{Builder, Event, LogFacadeLoggerConfig, NodeError}; use lightning::ln::channelmanager::PaymentId; @@ -801,7 +802,10 @@ fn simple_bolt12_send_receive() { fn generate_bip21_uri() { let (bitcoind, electrsd) = setup_bitcoind_and_electrsd(); let chain_source = TestChainSource::Esplora(&electrsd); - let log_writer = TestLogWriter::File(FilesystemLoggerConfig::default()); + + // Setup custom logger. + let mock_logger = init_custom_logger(); + let log_writer = TestLogWriter::Custom(mock_logger.clone()); let (node_a, node_b) = setup_two_nodes(&chain_source, false, true, false, log_writer); let address_a = node_a.onchain_payment().new_address().unwrap(); @@ -838,6 +842,10 @@ fn generate_bip21_uri() { }, Err(e) => panic!("Failed to generate URI: {:?}", e), } + + let logs = mock_logger.retrieve_logs(); + let last_log_entry = logs.last().unwrap(); + assert!(last_log_entry.contains("[INFO] Invoice created:")); } #[test] @@ -845,8 +853,9 @@ fn unified_qr_send_receive() { let (bitcoind, electrsd) = setup_bitcoind_and_electrsd(); let chain_source = TestChainSource::Esplora(&electrsd); - let mock_logger = init_mock_logger(log::LevelFilter::Trace); - let log_writer = TestLogWriter::LogFacade(LogFacadeLoggerConfig { level: LdkLevel::Trace }); + // Setup `log` facade logger. + let mock_logger = init_log_logger(log::LevelFilter::Trace); + let log_writer = TestLogWriter::LogFacade(LogFacadeLoggerConfig { level: LogLevel::Trace }); let (node_a, node_b) = setup_two_nodes(&chain_source, false, true, false, log_writer); let address_a = node_a.onchain_payment().new_address().unwrap(); From e509cf8e64d785a714bea5b7230d379ca44e4852 Mon Sep 17 00:00:00 2001 From: Enigbe Date: Thu, 12 Dec 2024 22:34:07 +0100 Subject: [PATCH 5/5] refactor: optimize logging system This commit addresses a series of fixes, refactors, and documentation changes meant to optimize the new logging system to be better with: - Improved memory usage by eliminating unnecessary data allocations and improving log record handling. - Enhanced logging system with support for foreign implementations (as tested with Kotlin). - Improved documentation across board. - Cleaner refactors and simpler code that improve clarity. - Streamlined logging configuration. - Pre-existing bug fixes. --- Cargo.toml | 2 +- .../lightningdevkit/ldknode/AndroidLibTest.kt | 13 +- .../lightningdevkit/ldknode/LibraryTest.kt | 64 +++++++ bindings/ldk_node.udl | 28 +-- src/builder.rs | 149 +++++++++----- src/config.rs | 38 +--- src/io/utils.rs | 11 ++ src/lib.rs | 6 +- src/logger.rs | 181 ++++++++++++------ tests/common/mod.rs | 102 +--------- tests/integration_tests_rust.rs | 80 ++------ 11 files changed, 352 insertions(+), 322 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index c7a38896b..d89ad28e2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -80,7 +80,7 @@ libc = "0.2" uniffi = { version = "0.27.3", features = ["build"], optional = true } serde = { version = "1.0.210", default-features = false, features = ["std", "derive"] } serde_json = { version = "1.0.128", default-features = false, features = ["std"] } -log = { version = "0.4.22", features = ["std"]} +log = { version = "0.4.22", default-features = false, features = ["std"]} vss-client = "0.3" prost = { version = "0.11.6", default-features = false} diff --git a/bindings/kotlin/ldk-node-android/lib/src/androidTest/kotlin/org/lightningdevkit/ldknode/AndroidLibTest.kt b/bindings/kotlin/ldk-node-android/lib/src/androidTest/kotlin/org/lightningdevkit/ldknode/AndroidLibTest.kt index 03c4b88a7..fb29d3219 100644 --- a/bindings/kotlin/ldk-node-android/lib/src/androidTest/kotlin/org/lightningdevkit/ldknode/AndroidLibTest.kt +++ b/bindings/kotlin/ldk-node-android/lib/src/androidTest/kotlin/org/lightningdevkit/ldknode/AndroidLibTest.kt @@ -3,19 +3,16 @@ */ package org.lightningdevkit.ldknode -import kotlin.UInt -import kotlin.test.Test -import kotlin.test.assertEquals +import androidx.test.ext.junit.runners.AndroidJUnit4 import kotlin.io.path.createTempDirectory +import kotlin.test.Test import org.junit.runner.RunWith -import org.lightningdevkit.ldknode.*; -import android.content.Context.MODE_PRIVATE -import androidx.test.core.app.ApplicationProvider -import androidx.test.ext.junit.runners.AndroidJUnit4 +import org.lightningdevkit.ldknode.* @RunWith(AndroidJUnit4::class) class AndroidLibTest { - @Test fun node_start_stop() { + @Test + fun node_start_stop() { val tmpDir1 = createTempDirectory("ldk_node").toString() println("Random dir 1: $tmpDir1") val tmpDir2 = createTempDirectory("ldk_node").toString() diff --git a/bindings/kotlin/ldk-node-jvm/lib/src/test/kotlin/org/lightningdevkit/ldknode/LibraryTest.kt b/bindings/kotlin/ldk-node-jvm/lib/src/test/kotlin/org/lightningdevkit/ldknode/LibraryTest.kt index a0e9f7fbb..c82a5d92e 100644 --- a/bindings/kotlin/ldk-node-jvm/lib/src/test/kotlin/org/lightningdevkit/ldknode/LibraryTest.kt +++ b/bindings/kotlin/ldk-node-jvm/lib/src/test/kotlin/org/lightningdevkit/ldknode/LibraryTest.kt @@ -9,6 +9,7 @@ import java.net.http.HttpRequest import java.net.http.HttpResponse import kotlin.io.path.createTempDirectory import kotlin.test.assertEquals +import kotlin.test.assertTrue fun runCommandAndWait(vararg cmd: String): String { println("Running command \"${cmd.joinToString(" ")}\"") @@ -92,6 +93,60 @@ fun waitForBlock(esploraEndpoint: String, blockHash: String) { } } +class CustomLogWriter(private var currentLogLevel: LogLevel = LogLevel.INFO) : + LogWriter { + enum class LogLevel { + ERROR, WARN, INFO, DEBUG, TRACE, GOSSIP + } + + private val logMessages = mutableListOf() + + fun setLogLevel(level: LogLevel) { + currentLogLevel = level + } + + fun getLogMessages(): List { + return logMessages.toList() + } + + override fun log(record: LogRecord) { + val recordLevel = + when (record.level.toString().lowercase()) { + "error" -> LogLevel.ERROR + "warn" -> LogLevel.WARN + "info" -> LogLevel.INFO + "debug" -> LogLevel.DEBUG + "trace" -> LogLevel.TRACE + "gossip" -> LogLevel.GOSSIP + else -> LogLevel.INFO + } + + if (isLevelEnabled(recordLevel)) { + val logMessage = formatRecord(record) + logMessages.add(logMessage) + println("$logMessage") + } + } + + private fun formatRecord(record: LogRecord): String { + val timestamp = + java.time.LocalDateTime.now() + .format(java.time.format.DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")) + return String.format( + "%s %-6s [%s:%s] %s\n", + timestamp, + record.level, + record.modulePath, + record.line, + record.args + ) + } + + private fun isLevelEnabled(level: LogLevel): Boolean { + return level.ordinal <= currentLogLevel.ordinal + } +} + @TestInstance(TestInstance.Lifecycle.PER_CLASS) class LibraryTest { @@ -106,6 +161,9 @@ class LibraryTest { } @Test fun fullCycle() { + val logWriter1 = CustomLogWriter(CustomLogWriter.LogLevel.GOSSIP) + val logWriter2 = CustomLogWriter(CustomLogWriter.LogLevel.GOSSIP) + val tmpDir1 = createTempDirectory("ldk_node").toString() println("Random dir 1: $tmpDir1") val tmpDir2 = createTempDirectory("ldk_node").toString() @@ -129,8 +187,11 @@ class LibraryTest { val builder1 = Builder.fromConfig(config1) builder1.setChainSourceEsplora(esploraEndpoint, null) + builder1.setCustomLogger(logWriter1) + val builder2 = Builder.fromConfig(config2) builder2.setChainSourceEsplora(esploraEndpoint, null) + builder2.setCustomLogger(logWriter2) val node1 = builder1.build() val node2 = builder2.build() @@ -263,6 +324,9 @@ class LibraryTest { assert(spendableBalance1AfterClose < 100000u) assertEquals(102500uL, spendableBalance2AfterClose) + assertTrue(logWriter1.getLogMessages().isNotEmpty()) + assertTrue(logWriter2.getLogMessages().isNotEmpty()) + node1.stop() node2.stop() } diff --git a/bindings/ldk_node.udl b/bindings/ldk_node.udl index f1f34395f..b0ff44b0a 100644 --- a/bindings/ldk_node.udl +++ b/bindings/ldk_node.udl @@ -34,13 +34,16 @@ enum LogLevel { "Error", }; -dictionary FilesystemLoggerConfig { - string log_file_path; +dictionary LogRecord { LogLevel level; + string args; + string module_path; + u32 line; }; -dictionary LogFacadeLoggerConfig { - LogLevel level; +[Trait, WithForeign] +interface LogWriter { + void log(LogRecord record); }; interface Builder { @@ -57,8 +60,9 @@ interface Builder { void set_gossip_source_rgs(string rgs_server_url); void set_liquidity_source_lsps2(SocketAddress address, PublicKey node_id, string? token); void set_storage_dir_path(string storage_dir_path); - void set_filesystem_logger(FilesystemLoggerConfig fs_config); - void set_log_facade_logger(LogFacadeLoggerConfig lf_config); + void set_filesystem_logger(string? log_file_path, LogLevel? log_level); + void set_log_facade_logger(LogLevel log_level); + void set_custom_logger(LogWriter log_writer); void set_network(Network network); [Throws=BuildError] void set_listening_addresses(sequence listening_addresses); @@ -593,18 +597,6 @@ dictionary NodeAnnouncementInfo { sequence addresses; }; -dictionary LogRecord { - LogLevel level; - string args; - string module_path; - u32 line; -}; - -[Trait] -interface LogWriter { - void log(LogRecord record); -}; - [Custom] typedef string Txid; diff --git a/src/builder.rs b/src/builder.rs index a8bd78110..bcd91eeb8 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -7,8 +7,8 @@ use crate::chain::{ChainSource, DEFAULT_ESPLORA_SERVER_URL}; use crate::config::{ - default_user_config, Config, EsploraSyncConfig, FilesystemLoggerConfig, LogFacadeLoggerConfig, - WALLET_KEYS_SEED_LEN, + default_user_config, Config, EsploraSyncConfig, DEFAULT_LOG_FILE_PATH, DEFAULT_LOG_LEVEL, + DEFAULT_STORAGE_DIR_PATH, WALLET_KEYS_SEED_LEN, }; use crate::connection::ConnectionManager; @@ -19,7 +19,7 @@ use crate::io::sqlite_store::SqliteStore; use crate::io::utils::{read_node_metrics, write_node_metrics}; use crate::io::vss_store::VssStore; use crate::liquidity::LiquiditySource; -use crate::logger::{log_error, log_info, LdkLogger, LogWriter, Logger}; +use crate::logger::{log_error, log_info, LdkLogger, LogLevel, LogWriter, Logger}; use crate::message_handler::NodeCustomMessageHandler; use crate::payment::store::PaymentStore; use crate::peer_store::PeerStore; @@ -109,16 +109,46 @@ impl Default for LiquiditySourceConfig { } } -#[derive(Debug)] +#[derive(Clone)] enum LogWriterConfig { - File(FilesystemLoggerConfig), - Log(LogFacadeLoggerConfig), - Custom(Arc), + File { + /// The log file path. + /// + /// This specifies the log file path if a destination other than the storage + /// directory, i.e. [`Config::storage_dir_path`], is preferred. If unconfigured, + /// defaults to [`DEFAULT_LOG_FILE_PATH`] in default storage directory. + log_file_path: Option, + /// This specifies the log level. + /// + /// If unconfigured, defaults to `Debug`. + log_level: Option, + }, + Log(LogLevel), + Custom(Arc), +} + +impl std::fmt::Debug for LogWriterConfig { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + LogWriterConfig::File { log_level, log_file_path } => f + .debug_struct("LogWriterConfig") + .field("log_level", log_level) + .field("log_file_path", log_file_path) + .finish(), + LogWriterConfig::Log(level) => f.debug_tuple("Log").field(level).finish(), + LogWriterConfig::Custom(_) => { + f.debug_tuple("Custom").field(&"").finish() + }, + } + } } impl Default for LogWriterConfig { fn default() -> Self { - Self::File(FilesystemLoggerConfig::default()) + Self::File { + log_file_path: Some(DEFAULT_LOG_FILE_PATH.to_string()), + log_level: Some(DEFAULT_LOG_LEVEL), + } } } @@ -318,19 +348,25 @@ impl NodeBuilder { } /// Configures the [`Node`] instance to write logs to the filesystem. - pub fn set_filesystem_logger(&mut self, fs_config: FilesystemLoggerConfig) -> &mut Self { - self.log_writer_config = Some(LogWriterConfig::File(fs_config)); + /// + /// The `log_file_path` defaults to the [`DEFAULT_LOG_FILE_PATH`] in the default + /// storage directory if set to None. + /// The `log_level` defaults to [`DEFAULT_LOG_LEVEL`] if set to None. + pub fn set_filesystem_logger( + &mut self, log_file_path: Option, log_level: Option, + ) -> &mut Self { + self.log_writer_config = Some(LogWriterConfig::File { log_file_path, log_level }); self } - /// Configures the [`Node`] instance to write logs to the `log` facade. - pub fn set_log_facade_logger(&mut self, lf_config: LogFacadeLoggerConfig) -> &mut Self { - self.log_writer_config = Some(LogWriterConfig::Log(lf_config)); + /// Configures the [`Node`] instance to write logs to the [`log`](https://crates.io/crates/log) facade. + pub fn set_log_facade_logger(&mut self, log_level: LogLevel) -> &mut Self { + self.log_writer_config = Some(LogWriterConfig::Log(log_level)); self } - /// Configures the [`Node`] instance to write logs to the provided custom log writer. - pub fn set_custom_logger(&mut self, log_writer: Arc) -> &mut Self { + /// Configures the [`Node`] instance to write logs to the provided custom [`LogWriter`]. + pub fn set_custom_logger(&mut self, log_writer: Arc) -> &mut Self { self.log_writer_config = Some(LogWriterConfig::Custom(log_writer)); self } @@ -416,10 +452,7 @@ impl NodeBuilder { ) -> Result { use bitcoin::key::Secp256k1; - let writer = LogWriterConfig::default(); - let log_writer_config = - if let Some(config) = &self.log_writer_config { config } else { &writer }; - let logger = setup_logger(&log_writer_config)?; + let logger = setup_logger(&self.log_writer_config, &self.config)?; let seed_bytes = seed_bytes_from_config( &self.config, @@ -484,10 +517,7 @@ impl NodeBuilder { pub fn build_with_vss_store_and_header_provider( &self, vss_url: String, store_id: String, header_provider: Arc, ) -> Result { - let writer = LogWriterConfig::default(); - let log_writer_config = - if let Some(config) = &self.log_writer_config { config } else { &writer }; - let logger = setup_logger(&log_writer_config)?; + let logger = setup_logger(&self.log_writer_config, &self.config)?; let seed_bytes = seed_bytes_from_config( &self.config, @@ -519,10 +549,7 @@ impl NodeBuilder { /// Builds a [`Node`] instance according to the options previously configured. pub fn build_with_store(&self, kv_store: Arc) -> Result { - let writer = LogWriterConfig::default(); - let log_writer_config = - if let Some(config) = &self.log_writer_config { config } else { &writer }; - let logger = setup_logger(&log_writer_config)?; + let logger = setup_logger(&self.log_writer_config, &self.config)?; let seed_bytes = seed_bytes_from_config( &self.config, @@ -646,17 +673,23 @@ impl ArcedNodeBuilder { } /// Configures the [`Node`] instance to write logs to the filesystem. - pub fn set_filesystem_logger(&self, fs_config: FilesystemLoggerConfig) { - self.inner.write().unwrap().set_filesystem_logger(fs_config); + /// + /// The `log_file_path` defaults to the [`DEFAULT_LOG_FILENAME`] in the default + /// storage directory if set to None. + /// The `log_level` defaults to [`DEFAULT_LOG_LEVEL`] if set to None. + pub fn set_filesystem_logger( + &self, log_file_path: Option, log_level: Option, + ) { + self.inner.write().unwrap().set_filesystem_logger(log_file_path, log_level); } - /// Configures the [`Node`] instance to write logs to the `log` facade. - pub fn set_log_facade_logger(&self, lf_config: LogFacadeLoggerConfig) { - self.inner.write().unwrap().set_log_facade_logger(lf_config); + /// Configures the [`Node`] instance to write logs to the [`log`](https://crates.io/crates/log) facade. + pub fn set_log_facade_logger(&self, log_level: LogLevel) { + self.inner.write().unwrap().set_log_facade_logger(log_level); } - /// Configures the [`Node`] instance to write logs to the provided custom log writer. - pub fn set_custom_logger(&self, log_writer: Arc) { + /// Configures the [`Node`] instance to write logs to the provided custom [`LogWriter`]. + pub fn set_custom_logger(&self, log_writer: Arc) { self.inner.write().unwrap().set_custom_logger(log_writer); } @@ -1283,25 +1316,37 @@ fn build_with_store_internal( } /// Sets up the node logger. -fn setup_logger(config: &LogWriterConfig) -> Result, BuildError> { - match config { - LogWriterConfig::File(fs_logger_config) => { - let log_file_path = &fs_logger_config.log_file_path; - - Ok(Arc::new( - Logger::new_fs_writer(log_file_path.to_string(), fs_logger_config.level) - .map_err(|_| BuildError::LoggerSetupFailed)?, - )) +/// +/// If `log_writer_conf` is set to None, uses [`LogWriterConfig::default()`]. +/// The `node_conf` is provided to access the configured storage directory. +fn setup_logger( + log_writer_conf: &Option, node_conf: &Config, +) -> Result, BuildError> { + let is_default = log_writer_conf.is_none(); + let default_lw_config = LogWriterConfig::default(); + let log_writer_config = + if let Some(conf) = log_writer_conf { conf } else { &default_lw_config }; + + let logger = match log_writer_config { + LogWriterConfig::File { log_file_path, log_level } => { + let fp = DEFAULT_LOG_FILE_PATH + .replace(DEFAULT_STORAGE_DIR_PATH, &node_conf.storage_dir_path); + let log_file_path = + if is_default { &fp } else { log_file_path.as_ref().map(|p| p).unwrap_or(&fp) }; + + let log_level = log_level.unwrap_or(DEFAULT_LOG_LEVEL); + + Logger::new_fs_writer(log_file_path, log_level) + .map_err(|_| BuildError::LoggerSetupFailed)? }, - LogWriterConfig::Log(log_facade_logger_config) => Ok(Arc::new( - Logger::new_log_facade(log_facade_logger_config.level) - .map_err(|_| BuildError::LoggerSetupFailed)?, - )), - LogWriterConfig::Custom(custom_log_writer) => Ok(Arc::new( - Logger::new_custom_writer(custom_log_writer.clone()) - .map_err(|_| BuildError::LoggerSetupFailed)?, - )), - } + LogWriterConfig::Log(log_level) => Logger::new_log_facade(*log_level), + + LogWriterConfig::Custom(custom_log_writer) => { + Logger::new_custom_writer(Arc::clone(&custom_log_writer)) + }, + }; + + Ok(Arc::new(logger)) } fn seed_bytes_from_config( diff --git a/src/config.rs b/src/config.rs index e89644994..f38d7f63d 100644 --- a/src/config.rs +++ b/src/config.rs @@ -22,15 +22,21 @@ use bitcoin::Network; use std::time::Duration; // Config defaults -const DEFAULT_STORAGE_DIR_PATH: &str = "/tmp/ldk_node"; const DEFAULT_NETWORK: Network = Network::Bitcoin; const DEFAULT_BDK_WALLET_SYNC_INTERVAL_SECS: u64 = 80; const DEFAULT_LDK_WALLET_SYNC_INTERVAL_SECS: u64 = 30; const DEFAULT_FEE_RATE_CACHE_UPDATE_INTERVAL_SECS: u64 = 60 * 10; const DEFAULT_PROBING_LIQUIDITY_LIMIT_MULTIPLIER: u64 = 3; const DEFAULT_ANCHOR_PER_CHANNEL_RESERVE_SATS: u64 = 25_000; -const DEFAULT_LOG_FILE_PATH: &'static str = "ldk_node.log"; -const DEFAULT_LOG_LEVEL: LogLevel = LogLevel::Debug; + +/// The default log level. +pub const DEFAULT_LOG_LEVEL: LogLevel = LogLevel::Debug; + +/// The default log file path. +pub const DEFAULT_LOG_FILE_PATH: &'static str = "/tmp/ldk_node/ldk_node.log"; + +/// The default storage directory. +pub const DEFAULT_STORAGE_DIR_PATH: &str = "/tmp/ldk_node"; // The 'stop gap' parameter used by BDK's wallet sync. This seems to configure the threshold // number of derivation indexes after which BDK stops looking for new scripts belonging to the wallet. @@ -429,32 +435,6 @@ impl From for LdkMaxDustHTLCExposure { } } -/// Configuration options for logging to the filesystem. -#[derive(Debug, Clone)] -pub struct FilesystemLoggerConfig { - /// The log file path. - /// - /// This specifies the log file path if a destination other than the storage - /// directory, i.e. [`Config::storage_dir_path`], is preferred. - pub log_file_path: String, - /// This specifies the log level. - pub level: LogLevel, -} - -/// Configuration options for logging to the `log` facade. -#[derive(Debug, Clone)] -pub struct LogFacadeLoggerConfig { - /// This specifies the log level. - pub level: LogLevel, -} - -impl Default for FilesystemLoggerConfig { - fn default() -> Self { - let log_file_path = format!("{}/{}", DEFAULT_STORAGE_DIR_PATH, DEFAULT_LOG_FILE_PATH); - Self { log_file_path, level: DEFAULT_LOG_LEVEL } - } -} - #[cfg(test)] mod tests { use std::str::FromStr; diff --git a/src/io/utils.rs b/src/io/utils.rs index 8b2d3b77a..b5537ed7d 100644 --- a/src/io/utils.rs +++ b/src/io/utils.rs @@ -98,6 +98,17 @@ where let mut key = [0; WALLET_KEYS_SEED_LEN]; thread_rng().fill_bytes(&mut key); + if let Some(parent_dir) = Path::new(&keys_seed_path).parent() { + fs::create_dir_all(parent_dir).map_err(|e| { + log_error!( + logger, + "Failed to create parent directory for key seed file: {}.", + keys_seed_path + ); + e + })?; + } + let mut f = fs::File::create(keys_seed_path).map_err(|e| { log_error!(logger, "Failed to create keys seed file: {}", keys_seed_path); e diff --git a/src/lib.rs b/src/lib.rs index 8e99165f7..3df6e4234 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -85,7 +85,7 @@ pub mod graph; mod hex_utils; pub mod io; mod liquidity; -mod logger; +pub mod logger; mod message_handler; pub mod payment; mod peer_store; @@ -111,9 +111,6 @@ pub use event::Event; pub use io::utils::generate_entropy_mnemonic; -pub use config::{FilesystemLoggerConfig, LogFacadeLoggerConfig}; -pub use logger::{LogLevel, LogRecord, LogWriter}; - #[cfg(feature = "uniffi")] use uniffi_types::*; @@ -147,6 +144,7 @@ use types::{ pub use types::{ChannelDetails, CustomTlvRecord, PeerDetails, UserChannelId}; use logger::{log_error, log_info, log_trace, LdkLogger, Logger}; +pub use logger::{LogLevel, LogRecord, LogWriter}; use lightning::chain::BestBlock; use lightning::events::bump_transaction::Wallet as LdkWallet; diff --git a/src/logger.rs b/src/logger.rs index 9916fdd93..a850a4e05 100644 --- a/src/logger.rs +++ b/src/logger.rs @@ -5,7 +5,9 @@ // http://opensource.org/licenses/MIT>, at your option. You may not use this file except in // accordance with one or both of these licenses. -pub(crate) use lightning::util::logger::{Logger as LdkLogger, Record}; +//! Logging-related objects. + +pub(crate) use lightning::util::logger::{Logger as LdkLogger, Record as LdkRecord}; pub(crate) use lightning::{log_bytes, log_debug, log_error, log_info, log_trace}; pub use lightning::util::logger::Level as LogLevel; @@ -13,14 +15,34 @@ pub use lightning::util::logger::Level as LogLevel; use chrono::Utc; use log::{debug, error, info, trace, warn}; -use std::fmt::Debug; +#[cfg(not(feature = "uniffi"))] +use core::fmt; use std::fs; use std::io::Write; use std::path::Path; use std::sync::Arc; -/// A unit of logging output with Metadata to enable filtering Module_path, -/// file, line to inform on log's source. +/// A unit of logging output with metadata to enable filtering `module_path`, +/// `file`, and `line` to inform on log's source. +#[cfg(not(feature = "uniffi"))] +pub struct LogRecord<'a> { + /// The verbosity level of the message. + pub level: LogLevel, + /// The message body. + pub args: fmt::Arguments<'a>, + /// The module path of the message. + pub module_path: &'a str, + /// The line containing the message. + pub line: u32, +} + +/// A unit of logging output with metadata to enable filtering `module_path`, +/// `file`, and `line` to inform on log's source. +/// +/// This version is used when the `uniffi` feature is enabled. +/// It is similar to the non-`uniffi` version, but it omits the lifetime parameter +/// for the `LogRecord`, as the Uniffi-exposed interface cannot handle lifetimes. +#[cfg(feature = "uniffi")] pub struct LogRecord { /// The verbosity level of the message. pub level: LogLevel, @@ -32,8 +54,9 @@ pub struct LogRecord { pub line: u32, } -impl<'a> From> for LogRecord { - fn from(record: Record) -> Self { +#[cfg(feature = "uniffi")] +impl<'a> From> for LogRecord { + fn from(record: LdkRecord) -> Self { Self { level: record.level, args: record.args.to_string(), @@ -43,68 +66,100 @@ impl<'a> From> for LogRecord { } } -/// LogWriter trait encapsulating the operations required of a -/// logger's writer. -pub trait LogWriter: Send + Sync + Debug { - /// Log the record. - fn log(&self, record: LogRecord); +#[cfg(not(feature = "uniffi"))] +impl<'a> From> for LogRecord<'a> { + fn from(record: LdkRecord<'a>) -> Self { + Self { + level: record.level, + args: record.args, + module_path: record.module_path, + line: record.line, + } + } } -#[derive(Debug)] -pub(crate) struct FilesystemLogger { - file_path: String, - level: LogLevel, +/// Defines the behavior required for writing log records. +/// +/// Implementors of this trait are responsible for handling log messages, +/// which may involve formatting, filtering, and forwarding them to specific +/// outputs. +#[cfg(not(feature = "uniffi"))] +pub trait LogWriter: Send + Sync { + /// Log the record. + fn log<'a>(&self, record: LogRecord<'a>); } -#[derive(Debug)] -pub(crate) struct LogFacadeLogger { - level: LogLevel, +/// Defines the behavior required for writing log records. +/// +/// Implementors of this trait are responsible for handling log messages, +/// which may involve formatting, filtering, and forwarding them to specific +/// outputs. +/// This version is used when the `uniffi` feature is enabled. +/// It is similar to the non-`uniffi` version, but it omits the lifetime parameter +/// for the `LogRecord`, as the Uniffi-exposed interface cannot handle lifetimes. +#[cfg(feature = "uniffi")] +pub trait LogWriter: Send + Sync { + /// Log the record. + fn log(&self, record: LogRecord); } /// Defines a writer for [`Logger`]. -#[derive(Debug)] pub(crate) enum Writer { /// Writes logs to the file system. - FileWriter(FilesystemLogger), + FileWriter { file_path: String, level: LogLevel }, /// Forwards logs to the `log` facade. - LogFacadeWriter(LogFacadeLogger), - /// Forwards logs to custom writer. - CustomWriter(Arc), + LogFacadeWriter { level: LogLevel }, + /// Forwards logs to a custom writer. + CustomWriter(Arc), } impl LogWriter for Writer { fn log(&self, record: LogRecord) { - let raw_log = record.args.to_string(); - let log = format!( - "{} {:<5} [{}:{}] {}\n", - Utc::now().format("%Y-%m-%d %H:%M:%S"), - record.level.to_string(), - record.module_path, - record.line, - raw_log - ); - match self { - Writer::FileWriter(fs_logger) => { - if record.level < fs_logger.level { + Writer::FileWriter { file_path, level } => { + if record.level < *level { return; } + let log = format!( + "{} {:<5} [{}:{}] {}\n", + Utc::now().format("%Y-%m-%d %H:%M:%S"), + record.level.to_string(), + record.module_path, + record.line, + record.args + ); + fs::OpenOptions::new() .create(true) .append(true) - .open(fs_logger.file_path.clone()) + .open(file_path) .expect("Failed to open log file") .write_all(log.as_bytes()) .expect("Failed to write to log file") }, - Writer::LogFacadeWriter(log_facade_logger) => match log_facade_logger.level { - LogLevel::Gossip => trace!("{}", log), - LogLevel::Trace => trace!("{}", log), - LogLevel::Debug => debug!("{}", log), - LogLevel::Info => info!("{}", log), - LogLevel::Warn => warn!("{}", log), - LogLevel::Error => error!("{}", log), + Writer::LogFacadeWriter { level } => { + macro_rules! log_with_level { + ($log_level:expr, $($args:tt)*) => { + match $log_level { + LogLevel::Gossip | LogLevel::Trace => trace!($($args)*), + LogLevel::Debug => debug!($($args)*), + LogLevel::Info => info!($($args)*), + LogLevel::Warn => warn!($($args)*), + LogLevel::Error => error!($($args)*), + } + }; + } + + log_with_level!( + level, + "{} {:<5} [{}:{}] {}", + Utc::now().format("%Y-%m-%d %H:%M:%S"), + record.level, + record.module_path, + record.line, + record.args + ) }, Writer::CustomWriter(custom_logger) => custom_logger.log(record), } @@ -119,8 +174,8 @@ pub(crate) struct Logger { impl Logger { /// Creates a new logger with a filesystem writer. The parameters to this function /// are the path to the log file, and the log level. - pub fn new_fs_writer(log_file_path: String, level: LogLevel) -> Result { - if let Some(parent_dir) = Path::new(&log_file_path).parent() { + pub fn new_fs_writer(file_path: &str, level: LogLevel) -> Result { + if let Some(parent_dir) = Path::new(&file_path).parent() { fs::create_dir_all(parent_dir) .map_err(|e| eprintln!("ERROR: Failed to create log parent directory: {}", e))?; @@ -128,28 +183,40 @@ impl Logger { fs::OpenOptions::new() .create(true) .append(true) - .open(&log_file_path) + .open(&file_path) .map_err(|e| eprintln!("ERROR: Failed to open log file: {}", e))?; } - let fs_writer = FilesystemLogger { file_path: log_file_path, level }; - - Ok(Self { writer: Writer::FileWriter(fs_writer) }) + Ok(Self { writer: Writer::FileWriter { file_path: file_path.to_string(), level } }) } - pub fn new_log_facade(level: LogLevel) -> Result { - let log_facade_writer = LogFacadeLogger { level }; - - Ok(Self { writer: Writer::LogFacadeWriter(log_facade_writer) }) + pub fn new_log_facade(level: LogLevel) -> Self { + Self { writer: Writer::LogFacadeWriter { level } } } - pub fn new_custom_writer(log_writer: Arc) -> Result { - Ok(Self { writer: Writer::CustomWriter(log_writer) }) + pub fn new_custom_writer(log_writer: Arc) -> Self { + Self { writer: Writer::CustomWriter(log_writer) } } } impl LdkLogger for Logger { - fn log(&self, record: Record) { - self.writer.log(record.into()); + fn log(&self, record: LdkRecord) { + match &self.writer { + Writer::FileWriter { file_path: _, level } => { + if record.level < *level { + return; + } + self.writer.log(record.into()); + }, + Writer::LogFacadeWriter { level } => { + if record.level < *level { + return; + } + self.writer.log(record.into()); + }, + Writer::CustomWriter(_arc) => { + self.writer.log(record.into()); + }, + } } } diff --git a/tests/common/mod.rs b/tests/common/mod.rs index 8116da54a..cb58a28cd 100644 --- a/tests/common/mod.rs +++ b/tests/common/mod.rs @@ -8,13 +8,12 @@ #![cfg(any(test, cln_test, vss_test))] #![allow(dead_code)] -use chrono::Utc; use ldk_node::config::{Config, EsploraSyncConfig}; use ldk_node::io::sqlite_store::SqliteStore; +use ldk_node::logger::LogLevel; use ldk_node::payment::{PaymentDirection, PaymentKind, PaymentStatus}; use ldk_node::{ - Builder, CustomTlvRecord, Event, FilesystemLoggerConfig, LightningBalance, - LogFacadeLoggerConfig, LogRecord, LogWriter, Node, NodeError, PendingSweepBalance, + Builder, CustomTlvRecord, Event, LightningBalance, Node, NodeError, PendingSweepBalance, }; use lightning::ln::msgs::SocketAddress; @@ -41,11 +40,9 @@ use electrum_client::ElectrumApi; use rand::distributions::Alphanumeric; use rand::{thread_rng, Rng}; -use log::{LevelFilter, Log}; - use std::env; use std::path::PathBuf; -use std::sync::{Arc, Mutex, RwLock}; +use std::sync::{Arc, RwLock}; use std::time::Duration; macro_rules! expect_event { @@ -254,79 +251,6 @@ pub(crate) enum TestChainSource<'a> { BitcoindRpc(&'a BitcoinD), } -#[derive(Clone)] -pub(crate) enum TestLogWriter { - File(FilesystemLoggerConfig), - LogFacade(LogFacadeLoggerConfig), - Custom(Arc), -} - -/// Simple in-memory mock `log` logger for tests. -#[derive(Debug)] -pub(crate) struct MockLogger { - logs: Arc>>, -} - -impl MockLogger { - pub fn new() -> Self { - Self { logs: Arc::new(Mutex::new(Vec::new())) } - } - - pub fn retrieve_logs(&self) -> Vec { - self.logs.lock().unwrap().clone() - } -} - -/// [`MockLogger`] as `log` logger - destination for [`Writer::LogFacadeWriter`] -/// to write logs to. -/// -/// [`Writer::LogFacadeWriter`]: ldk_node::logger::Writer::LogFacadeWriter -impl Log for MockLogger { - fn log(&self, record: &log::Record) { - let message = format!( - "{} [{}] {}", - Utc::now().format("%Y-%m-%d %H:%M:%S"), - record.level(), - record.args() - ); - self.logs.lock().unwrap().push(message); - } - - fn enabled(&self, _metadata: &log::Metadata) -> bool { - true - } - - fn flush(&self) {} -} - -/// [`MockLogger`] as custom logger - a destination for [`Writer::CustomWriter`] -/// to write logs to. -/// -/// [`Writer::CustomWriter`]: ldk_node::logger::Writer::CustomWriter -impl LogWriter for MockLogger { - fn log(&self, record: LogRecord) { - let message = format!( - "{} [{}] {}", - Utc::now().format("%Y-%m-%d %H:%M:%S"), - record.level, - record.args - ); - self.logs.lock().unwrap().push(message); - } -} - -pub(crate) fn init_log_logger(level: LevelFilter) -> Arc { - let logger = Arc::new(MockLogger::new()); - log::set_boxed_logger(Box::new(logger.clone())).unwrap(); - log::set_max_level(level); - logger -} - -pub(crate) fn init_custom_logger() -> Arc { - let logger = Arc::new(MockLogger::new()); - logger -} - macro_rules! setup_builder { ($builder: ident, $config: expr) => { #[cfg(feature = "uniffi")] @@ -340,11 +264,11 @@ pub(crate) use setup_builder; pub(crate) fn setup_two_nodes( chain_source: &TestChainSource, allow_0conf: bool, anchor_channels: bool, - anchors_trusted_no_reserve: bool, log_writer: TestLogWriter, + anchors_trusted_no_reserve: bool, ) -> (TestNode, TestNode) { println!("== Node A =="); let config_a = random_config(anchor_channels); - let node_a = setup_node(chain_source, config_a, None, log_writer.clone()); + let node_a = setup_node(chain_source, config_a, None); println!("\n== Node B =="); let mut config_b = random_config(anchor_channels); @@ -359,13 +283,12 @@ pub(crate) fn setup_two_nodes( .trusted_peers_no_reserve .push(node_a.node_id()); } - let node_b = setup_node(chain_source, config_b, None, log_writer); + let node_b = setup_node(chain_source, config_b, None); (node_a, node_b) } pub(crate) fn setup_node( chain_source: &TestChainSource, config: Config, seed_bytes: Option>, - log_writer: TestLogWriter, ) -> TestNode { setup_builder!(builder, config); match chain_source { @@ -386,17 +309,8 @@ pub(crate) fn setup_node( }, } - match log_writer { - TestLogWriter::File(fs_config) => { - builder.set_filesystem_logger(fs_config); - }, - TestLogWriter::LogFacade(lf_config) => { - builder.set_log_facade_logger(lf_config); - }, - TestLogWriter::Custom(log_writer) => { - builder.set_custom_logger(log_writer); - }, - } + let log_file_path = format!("{}/{}", config.storage_dir_path, "ldk_node.log"); + builder.set_filesystem_logger(Some(log_file_path), Some(LogLevel::Gossip)); if let Some(seed) = seed_bytes { builder.set_entropy_seed_bytes(seed).unwrap(); diff --git a/tests/integration_tests_rust.rs b/tests/integration_tests_rust.rs index 0d25f0b16..8dd39133b 100644 --- a/tests/integration_tests_rust.rs +++ b/tests/integration_tests_rust.rs @@ -9,19 +9,16 @@ mod common; use common::{ do_channel_full_cycle, expect_channel_ready_event, expect_event, expect_payment_received_event, - expect_payment_successful_event, generate_blocks_and_wait, init_custom_logger, init_log_logger, - open_channel, premine_and_distribute_funds, random_config, setup_bitcoind_and_electrsd, - setup_builder, setup_node, setup_two_nodes, wait_for_tx, TestChainSource, TestLogWriter, - TestSyncStore, + expect_payment_successful_event, generate_blocks_and_wait, open_channel, + premine_and_distribute_funds, random_config, setup_bitcoind_and_electrsd, setup_builder, + setup_node, setup_two_nodes, wait_for_tx, TestChainSource, TestSyncStore, }; -use ldk_node::config::{EsploraSyncConfig, FilesystemLoggerConfig}; +use ldk_node::config::EsploraSyncConfig; use ldk_node::payment::{PaymentKind, QrPaymentResult, SendingParameters}; -use ldk_node::LogLevel; -use ldk_node::{Builder, Event, LogFacadeLoggerConfig, NodeError}; +use ldk_node::{Builder, Event, NodeError}; use lightning::ln::channelmanager::PaymentId; -use lightning::util::logger::Level; use lightning::util::persist::KVStore; use bitcoincore_rpc::RpcApi; @@ -35,8 +32,7 @@ use std::sync::Arc; fn channel_full_cycle() { let (bitcoind, electrsd) = setup_bitcoind_and_electrsd(); let chain_source = TestChainSource::Esplora(&electrsd); - let log_writer = TestLogWriter::File(FilesystemLoggerConfig::default()); - let (node_a, node_b) = setup_two_nodes(&chain_source, false, true, false, log_writer); + let (node_a, node_b) = setup_two_nodes(&chain_source, false, true, false); do_channel_full_cycle(node_a, node_b, &bitcoind.client, &electrsd.client, false, true, false); } @@ -44,8 +40,7 @@ fn channel_full_cycle() { fn channel_full_cycle_bitcoind() { let (bitcoind, electrsd) = setup_bitcoind_and_electrsd(); let chain_source = TestChainSource::BitcoindRpc(&bitcoind); - let log_writer = TestLogWriter::File(FilesystemLoggerConfig::default()); - let (node_a, node_b) = setup_two_nodes(&chain_source, false, true, false, log_writer); + let (node_a, node_b) = setup_two_nodes(&chain_source, false, true, false); do_channel_full_cycle(node_a, node_b, &bitcoind.client, &electrsd.client, false, true, false); } @@ -53,8 +48,7 @@ fn channel_full_cycle_bitcoind() { fn channel_full_cycle_force_close() { let (bitcoind, electrsd) = setup_bitcoind_and_electrsd(); let chain_source = TestChainSource::Esplora(&electrsd); - let log_writer = TestLogWriter::File(FilesystemLoggerConfig::default()); - let (node_a, node_b) = setup_two_nodes(&chain_source, false, true, false, log_writer); + let (node_a, node_b) = setup_two_nodes(&chain_source, false, true, false); do_channel_full_cycle(node_a, node_b, &bitcoind.client, &electrsd.client, false, true, true); } @@ -62,8 +56,7 @@ fn channel_full_cycle_force_close() { fn channel_full_cycle_force_close_trusted_no_reserve() { let (bitcoind, electrsd) = setup_bitcoind_and_electrsd(); let chain_source = TestChainSource::Esplora(&electrsd); - let log_writer = TestLogWriter::File(FilesystemLoggerConfig::default()); - let (node_a, node_b) = setup_two_nodes(&chain_source, false, true, true, log_writer); + let (node_a, node_b) = setup_two_nodes(&chain_source, false, true, true); do_channel_full_cycle(node_a, node_b, &bitcoind.client, &electrsd.client, false, true, true); } @@ -71,8 +64,7 @@ fn channel_full_cycle_force_close_trusted_no_reserve() { fn channel_full_cycle_0conf() { let (bitcoind, electrsd) = setup_bitcoind_and_electrsd(); let chain_source = TestChainSource::Esplora(&electrsd); - let log_writer = TestLogWriter::File(FilesystemLoggerConfig::default()); - let (node_a, node_b) = setup_two_nodes(&chain_source, true, true, false, log_writer); + let (node_a, node_b) = setup_two_nodes(&chain_source, true, true, false); do_channel_full_cycle(node_a, node_b, &bitcoind.client, &electrsd.client, true, true, false) } @@ -80,8 +72,7 @@ fn channel_full_cycle_0conf() { fn channel_full_cycle_legacy_staticremotekey() { let (bitcoind, electrsd) = setup_bitcoind_and_electrsd(); let chain_source = TestChainSource::Esplora(&electrsd); - let log_writer = TestLogWriter::File(FilesystemLoggerConfig::default()); - let (node_a, node_b) = setup_two_nodes(&chain_source, false, false, false, log_writer); + let (node_a, node_b) = setup_two_nodes(&chain_source, false, false, false); do_channel_full_cycle(node_a, node_b, &bitcoind.client, &electrsd.client, false, false, false); } @@ -89,8 +80,7 @@ fn channel_full_cycle_legacy_staticremotekey() { fn channel_open_fails_when_funds_insufficient() { let (bitcoind, electrsd) = setup_bitcoind_and_electrsd(); let chain_source = TestChainSource::Esplora(&electrsd); - let log_writer = TestLogWriter::File(FilesystemLoggerConfig::default()); - let (node_a, node_b) = setup_two_nodes(&chain_source, false, true, false, log_writer); + let (node_a, node_b) = setup_two_nodes(&chain_source, false, true, false); let addr_a = node_a.onchain_payment().new_address().unwrap(); let addr_b = node_b.onchain_payment().new_address().unwrap(); @@ -231,10 +221,6 @@ fn start_stop_reinit() { sync_config.lightning_wallet_sync_interval_secs = 100000; setup_builder!(builder, config); builder.set_chain_source_esplora(esplora_url.clone(), Some(sync_config)); - builder.set_filesystem_logger(FilesystemLoggerConfig { - log_file_path: format!("{}/{}", config.storage_dir_path, "ldk_node.log"), - level: Level::Debug, - }); let node = builder.build_with_store(Arc::clone(&test_sync_store)).unwrap(); node.start().unwrap(); @@ -295,8 +281,7 @@ fn start_stop_reinit() { fn onchain_spend_receive() { let (bitcoind, electrsd) = setup_bitcoind_and_electrsd(); let chain_source = TestChainSource::Esplora(&electrsd); - let log_writer = TestLogWriter::File(FilesystemLoggerConfig::default()); - let (node_a, node_b) = setup_two_nodes(&chain_source, false, true, false, log_writer); + let (node_a, node_b) = setup_two_nodes(&chain_source, false, true, false); let addr_a = node_a.onchain_payment().new_address().unwrap(); let addr_b = node_b.onchain_payment().new_address().unwrap(); @@ -398,9 +383,7 @@ fn onchain_wallet_recovery() { let seed_bytes = vec![42u8; 64]; let original_config = random_config(true); - let log_writer = TestLogWriter::File(FilesystemLoggerConfig::default()); - let original_node = - setup_node(&chain_source, original_config, Some(seed_bytes.clone()), log_writer); + let original_node = setup_node(&chain_source, original_config, Some(seed_bytes.clone())); let premine_amount_sat = 100_000; @@ -445,8 +428,7 @@ fn onchain_wallet_recovery() { // Now we start from scratch, only the seed remains the same. let recovered_config = random_config(true); - let log_writer = TestLogWriter::File(FilesystemLoggerConfig::default()); - let recovered_node = setup_node(&chain_source, recovered_config, Some(seed_bytes), log_writer); + let recovered_node = setup_node(&chain_source, recovered_config, Some(seed_bytes)); recovered_node.sync_wallets().unwrap(); assert_eq!( @@ -489,8 +471,7 @@ fn sign_verify_msg() { let (_bitcoind, electrsd) = setup_bitcoind_and_electrsd(); let config = random_config(true); let chain_source = TestChainSource::Esplora(&electrsd); - let log_writer = TestLogWriter::File(FilesystemLoggerConfig::default()); - let node = setup_node(&chain_source, config, None, log_writer); + let node = setup_node(&chain_source, config, None); // Tests arbitrary message signing and later verification let msg = "OK computer".as_bytes(); @@ -508,8 +489,7 @@ fn connection_restart_behavior() { fn do_connection_restart_behavior(persist: bool) { let (_bitcoind, electrsd) = setup_bitcoind_and_electrsd(); let chain_source = TestChainSource::Esplora(&electrsd); - let log_writer = TestLogWriter::File(FilesystemLoggerConfig::default()); - let (node_a, node_b) = setup_two_nodes(&chain_source, false, false, false, log_writer); + let (node_a, node_b) = setup_two_nodes(&chain_source, false, false, false); let node_id_a = node_a.node_id(); let node_id_b = node_b.node_id(); @@ -561,8 +541,7 @@ fn do_connection_restart_behavior(persist: bool) { fn concurrent_connections_succeed() { let (_bitcoind, electrsd) = setup_bitcoind_and_electrsd(); let chain_source = TestChainSource::Esplora(&electrsd); - let log_writer = TestLogWriter::File(FilesystemLoggerConfig::default()); - let (node_a, node_b) = setup_two_nodes(&chain_source, false, true, false, log_writer); + let (node_a, node_b) = setup_two_nodes(&chain_source, false, true, false); let node_a = Arc::new(node_a); let node_b = Arc::new(node_b); @@ -593,8 +572,7 @@ fn concurrent_connections_succeed() { fn simple_bolt12_send_receive() { let (bitcoind, electrsd) = setup_bitcoind_and_electrsd(); let chain_source = TestChainSource::Esplora(&electrsd); - let log_writer = TestLogWriter::File(FilesystemLoggerConfig::default()); - let (node_a, node_b) = setup_two_nodes(&chain_source, false, true, false, log_writer); + let (node_a, node_b) = setup_two_nodes(&chain_source, false, true, false); let address_a = node_a.onchain_payment().new_address().unwrap(); let premine_amount_sat = 5_000_000; @@ -803,10 +781,7 @@ fn generate_bip21_uri() { let (bitcoind, electrsd) = setup_bitcoind_and_electrsd(); let chain_source = TestChainSource::Esplora(&electrsd); - // Setup custom logger. - let mock_logger = init_custom_logger(); - let log_writer = TestLogWriter::Custom(mock_logger.clone()); - let (node_a, node_b) = setup_two_nodes(&chain_source, false, true, false, log_writer); + let (node_a, node_b) = setup_two_nodes(&chain_source, false, true, false); let address_a = node_a.onchain_payment().new_address().unwrap(); let premined_sats = 5_000_000; @@ -842,10 +817,6 @@ fn generate_bip21_uri() { }, Err(e) => panic!("Failed to generate URI: {:?}", e), } - - let logs = mock_logger.retrieve_logs(); - let last_log_entry = logs.last().unwrap(); - assert!(last_log_entry.contains("[INFO] Invoice created:")); } #[test] @@ -853,10 +824,7 @@ fn unified_qr_send_receive() { let (bitcoind, electrsd) = setup_bitcoind_and_electrsd(); let chain_source = TestChainSource::Esplora(&electrsd); - // Setup `log` facade logger. - let mock_logger = init_log_logger(log::LevelFilter::Trace); - let log_writer = TestLogWriter::LogFacade(LogFacadeLoggerConfig { level: LogLevel::Trace }); - let (node_a, node_b) = setup_two_nodes(&chain_source, false, true, false, log_writer); + let (node_a, node_b) = setup_two_nodes(&chain_source, false, true, false); let address_a = node_a.onchain_payment().new_address().unwrap(); let premined_sats = 5_000_000; @@ -959,10 +927,4 @@ fn unified_qr_send_receive() { assert_eq!(node_b.list_balances().total_onchain_balance_sats, 800_000); assert_eq!(node_b.list_balances().total_lightning_balance_sats, 200_000); - - assert!(mock_logger - .retrieve_logs() - .last() - .unwrap() - .contains("Incremental sync of on-chain wallet finished"),); }