Skip to content

Commit 63f08b2

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. * Fix setting log's global logger twice. * Revert the renaming of LogLevel to LdkLevel.
1 parent fe0f547 commit 63f08b2

File tree

7 files changed

+106
-33
lines changed

7 files changed

+106
-33
lines changed

bindings/ldk_node.udl

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ dictionary EsploraSyncConfig {
2525
u64 fee_rate_cache_update_interval_secs;
2626
};
2727

28-
enum LdkLevel {
28+
enum LogLevel {
2929
"Gossip",
3030
"Trace",
3131
"Debug",
@@ -36,11 +36,11 @@ enum LdkLevel {
3636

3737
dictionary FilesystemLoggerConfig {
3838
string log_file_path;
39-
LdkLevel level;
39+
LogLevel level;
4040
};
4141

4242
dictionary LogFacadeLoggerConfig {
43-
LdkLevel level;
43+
LogLevel level;
4444
};
4545

4646
interface Builder {
@@ -594,7 +594,7 @@ dictionary NodeAnnouncementInfo {
594594
};
595595

596596
dictionary LogRecord {
597-
LdkLevel level;
597+
LogLevel level;
598598
string args;
599599
string module_path;
600600
u32 line;

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);
@@ -1285,6 +1297,10 @@ fn setup_logger(config: &LogWriterConfig) -> Result<Arc<Logger>, BuildError> {
12851297
Logger::new_log_facade(log_facade_logger_config.level)
12861298
.map_err(|_| BuildError::LoggerSetupFailed)?,
12871299
)),
1300+
LogWriterConfig::Custom(custom_log_writer) => Ok(Arc::new(
1301+
Logger::new_custom_writer(custom_log_writer.clone())
1302+
.map_err(|_| BuildError::LoggerSetupFailed)?,
1303+
)),
12881304
}
12891305
}
12901306

src/config.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
//! Objects for configuring the node.
99
10-
use crate::logger::LdkLevel;
10+
use crate::logger::LogLevel;
1111
use crate::payment::SendingParameters;
1212

1313
use lightning::ln::msgs::SocketAddress;
@@ -30,7 +30,7 @@ const DEFAULT_FEE_RATE_CACHE_UPDATE_INTERVAL_SECS: u64 = 60 * 10;
3030
const DEFAULT_PROBING_LIQUIDITY_LIMIT_MULTIPLIER: u64 = 3;
3131
const DEFAULT_ANCHOR_PER_CHANNEL_RESERVE_SATS: u64 = 25_000;
3232
const DEFAULT_LOG_FILE_PATH: &'static str = "ldk_node.log";
33-
const DEFAULT_LOG_LEVEL: LdkLevel = LdkLevel::Debug;
33+
const DEFAULT_LOG_LEVEL: LogLevel = LogLevel::Debug;
3434

3535
// The 'stop gap' parameter used by BDK's wallet sync. This seems to configure the threshold
3636
// number of derivation indexes after which BDK stops looking for new scripts belonging to the wallet.
@@ -438,14 +438,14 @@ pub struct FilesystemLoggerConfig {
438438
/// directory, i.e. [`Config::storage_dir_path`], is preferred.
439439
pub log_file_path: String,
440440
/// This specifies the log level.
441-
pub level: LdkLevel,
441+
pub level: LogLevel,
442442
}
443443

444444
/// Configuration options for logging to the `log` facade.
445445
#[derive(Debug, Clone)]
446446
pub struct LogFacadeLoggerConfig {
447447
/// This specifies the log level.
448-
pub level: LdkLevel,
448+
pub level: LogLevel,
449449
}
450450

451451
impl Default for FilesystemLoggerConfig {

src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ pub use event::Event;
112112
pub use io::utils::generate_entropy_mnemonic;
113113

114114
pub use config::{FilesystemLoggerConfig, LogFacadeLoggerConfig};
115-
pub use logger::{LdkLevel, LogRecord, LogWriter};
115+
pub use logger::{LogLevel, LogRecord, LogWriter};
116116

117117
#[cfg(feature = "uniffi")]
118118
use uniffi_types::*;

src/logger.rs

Lines changed: 25 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -8,20 +8,22 @@
88
pub(crate) use lightning::util::logger::{Logger as LdkLogger, Record};
99
pub(crate) use lightning::{log_bytes, log_debug, log_error, log_info, log_trace};
1010

11-
pub use lightning::util::logger::Level as LdkLevel;
11+
pub use lightning::util::logger::Level as LogLevel;
1212

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.
2224
pub struct LogRecord {
2325
/// The verbosity level of the message.
24-
pub level: LdkLevel,
26+
pub level: LogLevel,
2527
/// The message body.
2628
pub args: String,
2729
/// The module path of the message.
@@ -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,
53-
level: LdkLevel,
56+
level: LogLevel,
5457
}
5558

59+
#[derive(Debug)]
5660
pub(crate) struct LogFacadeLogger {
57-
level: LdkLevel,
61+
level: LogLevel,
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 {
@@ -92,13 +99,14 @@ impl LogWriter for Writer {
9299
.expect("Failed to write to log file")
93100
},
94101
Writer::LogFacadeWriter(log_facade_logger) => match log_facade_logger.level {
95-
LdkLevel::Gossip => trace!("{}", log),
96-
LdkLevel::Trace => trace!("{}", log),
97-
LdkLevel::Debug => debug!("{}", log),
98-
LdkLevel::Info => info!("{}", log),
99-
LdkLevel::Warn => warn!("{}", log),
100-
LdkLevel::Error => error!("{}", log),
102+
LogLevel::Gossip => trace!("{}", log),
103+
LogLevel::Trace => trace!("{}", log),
104+
LogLevel::Debug => debug!("{}", log),
105+
LogLevel::Info => info!("{}", log),
106+
LogLevel::Warn => warn!("{}", log),
107+
LogLevel::Error => error!("{}", log),
101108
},
109+
Writer::CustomWriter(custom_logger) => custom_logger.log(record),
102110
}
103111
}
104112
}
@@ -111,7 +119,7 @@ pub(crate) struct Logger {
111119
impl Logger {
112120
/// Creates a new logger with a filesystem writer. The parameters to this function
113121
/// are the path to the log file, and the log level.
114-
pub fn new_fs_writer(log_file_path: String, level: LdkLevel) -> Result<Self, ()> {
122+
pub fn new_fs_writer(log_file_path: String, level: LogLevel) -> Result<Self, ()> {
115123
if let Some(parent_dir) = Path::new(&log_file_path).parent() {
116124
fs::create_dir_all(parent_dir)
117125
.map_err(|e| eprintln!("ERROR: Failed to create log parent directory: {}", e))?;
@@ -129,11 +137,15 @@ impl Logger {
129137
Ok(Self { writer: Writer::FileWriter(fs_writer) })
130138
}
131139

132-
pub fn new_log_facade(level: LdkLevel) -> Result<Self, ()> {
140+
pub fn new_log_facade(level: LogLevel) -> Result<Self, ()> {
133141
let log_facade_writer = LogFacadeLogger { level };
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: 39 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::{
1516
Builder, CustomTlvRecord, Event, FilesystemLoggerConfig, LightningBalance,
16-
LogFacadeLoggerConfig, Node, NodeError, PendingSweepBalance,
17+
LogFacadeLoggerConfig, LogRecord, LogWriter, Node, NodeError, PendingSweepBalance,
1718
};
1819

1920
use lightning::ln::msgs::SocketAddress;
@@ -257,9 +258,11 @@ pub(crate) enum TestChainSource<'a> {
257258
pub(crate) enum TestLogWriter {
258259
File(FilesystemLoggerConfig),
259260
LogFacade(LogFacadeLoggerConfig),
261+
Custom(Arc<dyn LogWriter + Send + Sync>),
260262
}
261263

262264
/// Simple in-memory mock `log` logger for tests.
265+
#[derive(Debug)]
263266
pub(crate) struct MockLogger {
264267
logs: Arc<Mutex<Vec<String>>>,
265268
}
@@ -274,9 +277,18 @@ impl MockLogger {
274277
}
275278
}
276279

280+
/// [`MockLogger`] as `log` logger - destination for [`Writer::LogFacadeWriter`]
281+
/// to write logs to.
282+
///
283+
/// [`Writer::LogFacadeWriter`]: ldk_node::logger::Writer::LogFacadeWriter
277284
impl Log for MockLogger {
278285
fn log(&self, record: &log::Record) {
279-
let message = format!("[{}] {}", record.level(), record.args());
286+
let message = format!(
287+
"{} [{}] {}",
288+
Utc::now().format("%Y-%m-%d %H:%M:%S"),
289+
record.level(),
290+
record.args()
291+
);
280292
self.logs.lock().unwrap().push(message);
281293
}
282294

@@ -287,13 +299,34 @@ impl Log for MockLogger {
287299
fn flush(&self) {}
288300
}
289301

290-
pub(crate) fn init_mock_logger(level: LevelFilter) -> Arc<MockLogger> {
302+
/// [`MockLogger`] as custom logger - a destination for [`Writer::CustomWriter`]
303+
/// to write logs to.
304+
///
305+
/// [`Writer::CustomWriter`]: ldk_node::logger::Writer::CustomWriter
306+
impl LogWriter for MockLogger {
307+
fn log(&self, record: LogRecord) {
308+
let message = format!(
309+
"{} [{}] {}",
310+
Utc::now().format("%Y-%m-%d %H:%M:%S"),
311+
record.level,
312+
record.args
313+
);
314+
self.logs.lock().unwrap().push(message);
315+
}
316+
}
317+
318+
pub(crate) fn init_log_logger(level: LevelFilter) -> Arc<MockLogger> {
291319
let logger = Arc::new(MockLogger::new());
292320
log::set_boxed_logger(Box::new(logger.clone())).unwrap();
293321
log::set_max_level(level);
294322
logger
295323
}
296324

325+
pub(crate) fn init_custom_logger() -> Arc<MockLogger> {
326+
let logger = Arc::new(MockLogger::new());
327+
logger
328+
}
329+
297330
macro_rules! setup_builder {
298331
($builder: ident, $config: expr) => {
299332
#[cfg(feature = "uniffi")]
@@ -360,6 +393,9 @@ pub(crate) fn setup_node(
360393
TestLogWriter::LogFacade(lf_config) => {
361394
builder.set_log_facade_logger(lf_config);
362395
},
396+
TestLogWriter::Custom(log_writer) => {
397+
builder.set_custom_logger(log_writer);
398+
},
363399
}
364400

365401
if let Some(seed) = seed_bytes {

tests/integration_tests_rust.rs

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,15 @@ mod common;
99

1010
use common::{
1111
do_channel_full_cycle, expect_channel_ready_event, expect_event, expect_payment_received_event,
12-
expect_payment_successful_event, generate_blocks_and_wait, init_mock_logger, open_channel,
13-
premine_and_distribute_funds, random_config, setup_bitcoind_and_electrsd, setup_builder,
14-
setup_node, setup_two_nodes, wait_for_tx, TestChainSource, TestLogWriter, TestSyncStore,
12+
expect_payment_successful_event, generate_blocks_and_wait, init_custom_logger, init_log_logger,
13+
open_channel, premine_and_distribute_funds, random_config, setup_bitcoind_and_electrsd,
14+
setup_builder, setup_node, setup_two_nodes, wait_for_tx, TestChainSource, TestLogWriter,
15+
TestSyncStore,
1516
};
1617

1718
use ldk_node::config::{EsploraSyncConfig, FilesystemLoggerConfig};
1819
use ldk_node::payment::{PaymentKind, QrPaymentResult, SendingParameters};
19-
use ldk_node::LdkLevel;
20+
use ldk_node::LogLevel;
2021
use ldk_node::{Builder, Event, LogFacadeLoggerConfig, NodeError};
2122

2223
use lightning::ln::channelmanager::PaymentId;
@@ -801,7 +802,10 @@ fn simple_bolt12_send_receive() {
801802
fn generate_bip21_uri() {
802803
let (bitcoind, electrsd) = setup_bitcoind_and_electrsd();
803804
let chain_source = TestChainSource::Esplora(&electrsd);
804-
let log_writer = TestLogWriter::File(FilesystemLoggerConfig::default());
805+
806+
// Setup custom logger.
807+
let mock_logger = init_custom_logger();
808+
let log_writer = TestLogWriter::Custom(mock_logger.clone());
805809
let (node_a, node_b) = setup_two_nodes(&chain_source, false, true, false, log_writer);
806810

807811
let address_a = node_a.onchain_payment().new_address().unwrap();
@@ -838,15 +842,20 @@ fn generate_bip21_uri() {
838842
},
839843
Err(e) => panic!("Failed to generate URI: {:?}", e),
840844
}
845+
846+
let logs = mock_logger.retrieve_logs();
847+
let last_log_entry = logs.last().unwrap();
848+
assert!(last_log_entry.contains("[INFO] Invoice created:"));
841849
}
842850

843851
#[test]
844852
fn unified_qr_send_receive() {
845853
let (bitcoind, electrsd) = setup_bitcoind_and_electrsd();
846854
let chain_source = TestChainSource::Esplora(&electrsd);
847855

848-
let mock_logger = init_mock_logger(log::LevelFilter::Trace);
849-
let log_writer = TestLogWriter::LogFacade(LogFacadeLoggerConfig { level: LdkLevel::Trace });
856+
// Setup `log` facade logger.
857+
let mock_logger = init_log_logger(log::LevelFilter::Trace);
858+
let log_writer = TestLogWriter::LogFacade(LogFacadeLoggerConfig { level: LogLevel::Trace });
850859
let (node_a, node_b) = setup_two_nodes(&chain_source, false, true, false, log_writer);
851860

852861
let address_a = node_a.onchain_payment().new_address().unwrap();

0 commit comments

Comments
 (0)