Skip to content

Commit c9647fe

Browse files
committed
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.
1 parent 245e499 commit c9647fe

File tree

4 files changed

+71
-6
lines changed

4 files changed

+71
-6
lines changed

src/builder.rs

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ use crate::io::sqlite_store::SqliteStore;
1919
use crate::io::utils::{read_node_metrics, write_node_metrics};
2020
use crate::io::vss_store::VssStore;
2121
use crate::liquidity::LiquiditySource;
22-
use crate::logger::{log_error, log_info, LdkLogger, Logger};
22+
use crate::logger::{log_error, log_info, LdkLogger, LogWriter, Logger};
2323
use crate::message_handler::NodeCustomMessageHandler;
2424
use crate::payment::store::PaymentStore;
2525
use crate::peer_store::PeerStore;
@@ -113,6 +113,7 @@ impl Default for LiquiditySourceConfig {
113113
enum LogWriterConfig {
114114
File(FilesystemLoggerConfig),
115115
Log(LogFacadeLoggerConfig),
116+
Custom(Arc<dyn LogWriter + Send + Sync>),
116117
}
117118

118119
impl Default for LogWriterConfig {
@@ -328,6 +329,12 @@ impl NodeBuilder {
328329
self
329330
}
330331

332+
/// Configures the [`Node`] instance to write logs to the provided custom log writer.
333+
pub fn set_custom_logger(&mut self, log_writer: Arc<dyn LogWriter + Send + Sync>) -> &mut Self {
334+
self.log_writer_config = Some(LogWriterConfig::Custom(log_writer));
335+
self
336+
}
337+
331338
/// Sets the Bitcoin network used.
332339
pub fn set_network(&mut self, network: Network) -> &mut Self {
333340
self.config.network = network;
@@ -648,6 +655,11 @@ impl ArcedNodeBuilder {
648655
self.inner.write().unwrap().set_log_facade_logger(lf_config);
649656
}
650657

658+
/// Configures the [`Node`] instance to write logs to the provided custom log writer.
659+
pub fn set_custom_logger(&self, log_writer: Arc<dyn LogWriter + Send + Sync>) {
660+
self.inner.write().unwrap().set_custom_logger(log_writer);
661+
}
662+
651663
/// Sets the Bitcoin network used.
652664
pub fn set_network(&self, network: Network) {
653665
self.inner.write().unwrap().set_network(network);
@@ -1274,6 +1286,10 @@ fn setup_logger(config: &LogWriterConfig) -> Result<Arc<Logger>, BuildError> {
12741286
Logger::new_log_facade(log_facade_logger_config.level)
12751287
.map_err(|_| BuildError::LoggerSetupFailed)?,
12761288
)),
1289+
LogWriterConfig::Custom(custom_log_writer) => Ok(Arc::new(
1290+
Logger::new_custom_writer(custom_log_writer.clone())
1291+
.map_err(|_| BuildError::LoggerSetupFailed)?,
1292+
)),
12771293
}
12781294
}
12791295

src/logger.rs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,11 @@ pub use lightning::util::logger::Level as LdkLevel;
1313
use chrono::Utc;
1414
use log::{debug, error, info, trace, warn};
1515

16+
use std::fmt::Debug;
1617
use std::fs;
1718
use std::io::Write;
1819
use std::path::Path;
20+
use std::sync::Arc;
1921

2022
/// A unit of logging output with Metadata to enable filtering Module_path,
2123
/// file, line to inform on log's source.
@@ -43,26 +45,31 @@ impl<'a> From<Record<'a>> for LogRecord {
4345

4446
/// LogWriter trait encapsulating the operations required of a
4547
/// logger's writer.
46-
pub trait LogWriter: Send + Sync {
48+
pub trait LogWriter: Send + Sync + Debug {
4749
/// Log the record.
4850
fn log(&self, record: LogRecord);
4951
}
5052

53+
#[derive(Debug)]
5154
pub(crate) struct FilesystemLogger {
5255
file_path: String,
5356
level: LdkLevel,
5457
}
5558

59+
#[derive(Debug)]
5660
pub(crate) struct LogFacadeLogger {
5761
level: LdkLevel,
5862
}
5963

6064
/// Defines a writer for [`Logger`].
65+
#[derive(Debug)]
6166
pub(crate) enum Writer {
6267
/// Writes logs to the file system.
6368
FileWriter(FilesystemLogger),
6469
/// Forwards logs to the `log` facade.
6570
LogFacadeWriter(LogFacadeLogger),
71+
/// Forwards logs to custom writer.
72+
CustomWriter(Arc<dyn LogWriter + Send + Sync>),
6673
}
6774

6875
impl LogWriter for Writer {
@@ -99,6 +106,7 @@ impl LogWriter for Writer {
99106
LdkLevel::Warn => warn!("{}", log),
100107
LdkLevel::Error => error!("{}", log),
101108
},
109+
Writer::CustomWriter(custom_logger) => custom_logger.log(record),
102110
}
103111
}
104112
}
@@ -134,6 +142,10 @@ impl Logger {
134142

135143
Ok(Self { writer: Writer::LogFacadeWriter(log_facade_writer) })
136144
}
145+
146+
pub fn new_custom_writer(log_writer: Arc<dyn LogWriter + Send + Sync>) -> Result<Self, ()> {
147+
Ok(Self { writer: Writer::CustomWriter(log_writer) })
148+
}
137149
}
138150

139151
impl LdkLogger for Logger {

tests/common/mod.rs

Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,13 @@
88
#![cfg(any(test, cln_test, vss_test))]
99
#![allow(dead_code)]
1010

11+
use chrono::Utc;
1112
use ldk_node::config::{Config, EsploraSyncConfig};
1213
use ldk_node::io::sqlite_store::SqliteStore;
1314
use ldk_node::payment::{PaymentDirection, PaymentKind, PaymentStatus};
1415
use ldk_node::{
15-
Builder, Event, FilesystemLoggerConfig, LightningBalance, LogFacadeLoggerConfig, Node,
16-
NodeError, PendingSweepBalance,
16+
Builder, Event, FilesystemLoggerConfig, LightningBalance, LogFacadeLoggerConfig, LogRecord,
17+
LogWriter, Node, NodeError, PendingSweepBalance,
1718
};
1819

1920
use lightning::ln::msgs::SocketAddress;
@@ -254,9 +255,11 @@ pub(crate) enum TestChainSource<'a> {
254255
pub(crate) enum TestLogWriter {
255256
File(FilesystemLoggerConfig),
256257
LogFacade(LogFacadeLoggerConfig),
258+
Custom(Arc<dyn LogWriter + Send + Sync>),
257259
}
258260

259261
/// Simple in-memory mock `log` logger for tests.
262+
#[derive(Debug)]
260263
pub(crate) struct MockLogger {
261264
logs: Arc<Mutex<Vec<String>>>,
262265
}
@@ -271,9 +274,18 @@ impl MockLogger {
271274
}
272275
}
273276

277+
/// [`MockLogger`] as `log` logger - destination for [`Writer::LogFacadeWriter`]
278+
/// to write logs to.
279+
///
280+
/// [`Writer::LogFacadeWriter`]: ldk_node::logger::Writer::LogFacadeWriter
274281
impl Log for MockLogger {
275282
fn log(&self, record: &log::Record) {
276-
let message = format!("[{}] {}", record.level(), record.args());
283+
let message = format!(
284+
"{} [{}] {}",
285+
Utc::now().format("%Y-%m-%d %H:%M:%S"),
286+
record.level(),
287+
record.args()
288+
);
277289
self.logs.lock().unwrap().push(message);
278290
}
279291

@@ -284,6 +296,22 @@ impl Log for MockLogger {
284296
fn flush(&self) {}
285297
}
286298

299+
/// [`MockLogger`] as custom logger - a destination for [`Writer::CustomWriter`]
300+
/// to write logs to.
301+
///
302+
/// [`Writer::CustomWriter`]: ldk_node::logger::Writer::CustomWriter
303+
impl LogWriter for MockLogger {
304+
fn log(&self, record: LogRecord) {
305+
let message = format!(
306+
"{} [{}] {}",
307+
Utc::now().format("%Y-%m-%d %H:%M:%S"),
308+
record.level,
309+
record.args
310+
);
311+
self.logs.lock().unwrap().push(message);
312+
}
313+
}
314+
287315
pub(crate) fn init_mock_logger(level: LevelFilter) -> Arc<MockLogger> {
288316
let logger = Arc::new(MockLogger::new());
289317
log::set_boxed_logger(Box::new(logger.clone())).unwrap();
@@ -357,6 +385,9 @@ pub(crate) fn setup_node(
357385
TestLogWriter::LogFacade(lf_config) => {
358386
builder.set_log_facade_logger(lf_config);
359387
},
388+
TestLogWriter::Custom(log_writer) => {
389+
builder.set_custom_logger(log_writer);
390+
},
360391
}
361392

362393
if let Some(seed) = seed_bytes {

tests/integration_tests_rust.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -791,7 +791,10 @@ fn simple_bolt12_send_receive() {
791791
fn generate_bip21_uri() {
792792
let (bitcoind, electrsd) = setup_bitcoind_and_electrsd();
793793
let chain_source = TestChainSource::Esplora(&electrsd);
794-
let log_writer = TestLogWriter::File(FilesystemLoggerConfig::default());
794+
795+
// Setup custom logger.
796+
let mock_logger = init_mock_logger(log::LevelFilter::Trace);
797+
let log_writer = TestLogWriter::Custom(mock_logger.clone());
795798
let (node_a, node_b) = setup_two_nodes(&chain_source, false, true, false, log_writer);
796799

797800
let address_a = node_a.onchain_payment().new_address().unwrap();
@@ -828,13 +831,16 @@ fn generate_bip21_uri() {
828831
},
829832
Err(e) => panic!("Failed to generate URI: {:?}", e),
830833
}
834+
835+
assert!(mock_logger.retrieve_logs().last().unwrap().contains("Invoice created: lnbcrt"));
831836
}
832837

833838
#[test]
834839
fn unified_qr_send_receive() {
835840
let (bitcoind, electrsd) = setup_bitcoind_and_electrsd();
836841
let chain_source = TestChainSource::Esplora(&electrsd);
837842

843+
// Setup `log` facade logger.
838844
let mock_logger = init_mock_logger(log::LevelFilter::Trace);
839845
let log_writer = TestLogWriter::LogFacade(LogFacadeLoggerConfig { level: LdkLevel::Trace });
840846
let (node_a, node_b) = setup_two_nodes(&chain_source, false, true, false, log_writer);

0 commit comments

Comments
 (0)