diff --git a/tests/common/mod.rs b/tests/common/mod.rs index 461fc2e47..20a1d3479 100644 --- a/tests/common/mod.rs +++ b/tests/common/mod.rs @@ -8,11 +8,12 @@ #![cfg(any(test, cln_test, vss_test))] #![allow(dead_code)] +use chrono::Utc; use ldk_node::config::{ Config, EsploraSyncConfig, DEFAULT_LOG_FILENAME, DEFAULT_LOG_LEVEL, DEFAULT_STORAGE_DIR_PATH, }; use ldk_node::io::sqlite_store::SqliteStore; -use ldk_node::logger::{LogLevel, LogWriter}; +use ldk_node::logger::{LogLevel, LogRecord, LogWriter}; use ldk_node::payment::{PaymentDirection, PaymentKind, PaymentStatus}; use ldk_node::{ Builder, CustomTlvRecord, Event, LightningBalance, Node, NodeError, PendingSweepBalance, @@ -39,12 +40,13 @@ use bitcoincore_rpc::RpcApi; use electrsd::{bitcoind, bitcoind::BitcoinD, ElectrsD}; use electrum_client::ElectrumApi; +use log::{Level, LevelFilter, Log, Record}; use rand::distributions::Alphanumeric; use rand::{thread_rng, Rng}; 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 { @@ -1207,3 +1209,90 @@ impl KVStore for TestSyncStore { self.do_list(primary_namespace, secondary_namespace) } } + +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().to_vec() + } +} + +impl Log for MockLogger { + fn enabled(&self, _metadata: &log::Metadata) -> bool { + true + } + + fn log(&self, record: &log::Record) { + let message = format!( + "{} {:<5} [{}:{}] {}", + Utc::now().format("%Y-%m-%d %H:%M:%S"), + record.level().to_string(), + record.module_path().unwrap(), + record.line().unwrap(), + record.args() + ); + println!("{message}"); + self.logs.lock().unwrap().push(message); + } + + fn flush(&self) {} +} + +impl LogWriter for MockLogger { + fn log<'a>(&self, record: LogRecord) { + let record = MockLogRecord(record).into(); + Log::log(self, &record); + } +} + +struct MockLogRecord<'a>(LogRecord<'a>); +struct MockLogLevel(LogLevel); + +impl From for Level { + fn from(level: MockLogLevel) -> Self { + match level.0 { + LogLevel::Gossip | LogLevel::Trace => Level::Trace, + LogLevel::Debug => Level::Debug, + LogLevel::Info => Level::Info, + LogLevel::Warn => Level::Warn, + LogLevel::Error => Level::Error, + } + } +} + +impl<'a> From> for Record<'a> { + fn from(log_record: MockLogRecord<'a>) -> Self { + let log_record = log_record.0; + let level = MockLogLevel(log_record.level).into(); + + let mut record_builder = Record::builder(); + let record = record_builder + .level(level) + .module_path(Some(log_record.module_path)) + .line(Some(log_record.line)) + .args(log_record.args); + + record.build() + } +} + +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 +} diff --git a/tests/integration_tests_rust.rs b/tests/integration_tests_rust.rs index 2fd805b7a..eefc4b48c 100644 --- a/tests/integration_tests_rust.rs +++ b/tests/integration_tests_rust.rs @@ -9,12 +9,14 @@ 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, - premine_and_distribute_funds, random_config, setup_bitcoind_and_electrsd, setup_builder, - setup_node, setup_two_nodes, wait_for_tx, TestChainSource, 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; +use ldk_node::logger::LogLevel; use ldk_node::payment::{ ConfirmationStatus, PaymentDirection, PaymentKind, PaymentStatus, QrPaymentResult, SendingParameters, @@ -28,6 +30,7 @@ use bitcoincore_rpc::RpcApi; use bitcoin::Amount; use lightning_invoice::{Bolt11InvoiceDescription, Description}; +use log::LevelFilter; use std::sync::Arc; @@ -990,3 +993,34 @@ 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); } + +#[test] +fn facade_logging() { + let (_bitcoind, electrsd) = setup_bitcoind_and_electrsd(); + let chain_source = TestChainSource::Esplora(&electrsd); + + let logger = init_log_logger(LevelFilter::Trace); + let mut config = random_config(false); + config.log_writer = TestLogWriter::LogFacade { max_log_level: LogLevel::Gossip }; + + println!("== Facade logging start =="); + let _node = setup_node(&chain_source, config, None); + println!("== Facade logging end =="); + + assert!(!logger.retrieve_logs().is_empty()); +} + +#[test] +fn custom_logging() { + let (_bitcoind, electrsd) = setup_bitcoind_and_electrsd(); + let chain_source = TestChainSource::Esplora(&electrsd); + let logger = init_custom_logger(); + let mut config = random_config(false); + config.log_writer = TestLogWriter::Custom(logger.clone()); + + println!("== Custom logging start =="); + let _node = setup_node(&chain_source, config, None); + println!("== Custom logging end =="); + + assert!(!logger.retrieve_logs().is_empty()); +}