Skip to content

Commit 9314fce

Browse files
committed
Add configurable log level and log file
Before we were dropping all our logs on the floor besides a few printlns in the code. This uses `simplelogger` to add a log file and have them print to the console. We also add to the config options for setting the log file and log level.
1 parent 10280f3 commit 9314fce

5 files changed

Lines changed: 124 additions & 23 deletions

File tree

Cargo.lock

Lines changed: 12 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

ldk-server/Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ rusqlite = { version = "0.31.0", features = ["bundled"] }
1818
rand = { version = "0.8.5", default-features = false }
1919
async-trait = { version = "0.1.85", default-features = false }
2020
toml = { version = "0.8.9", default-features = false, features = ["parse"] }
21+
simplelog = { version = "0.12.0", default-features = false }
22+
log = "0.4.28"
2123

2224
# Required for RabittMQ based EventPublisher. Only enabled for `events-rabbitmq` feature.
2325
lapin = { version = "2.4.0", features = ["rustls"], default-features = false, optional = true }

ldk-server/ldk-server-config.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@ rest_service_address = "127.0.0.1:3002" # LDK Server REST address
88
[storage.disk]
99
dir_path = "/tmp/ldk-server/" # Path for LDK and BDK data persistence
1010

11+
[log]
12+
level = "Debug" # Log level (Error, Warn, Info, Debug, Trace)
13+
file_path = "/tmp/ldk-server/ldk-server.log" # Log file path
1114

1215
# Must set either bitcoind or esplora settings, but not both
1316

ldk-server/src/main.rs

Lines changed: 66 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,10 @@ use ldk_node::lightning::ln::channelmanager::PaymentId;
3232
use ldk_server_protos::events;
3333
use ldk_server_protos::events::{event_envelope, EventEnvelope};
3434
use ldk_server_protos::types::Payment;
35+
use log::{error, info};
3536
use prost::Message;
3637
use rand::Rng;
38+
use simplelog::{CombinedLogger, SimpleLogger, WriteLogger};
3739
use std::fs;
3840
use std::path::{Path, PathBuf};
3941
use std::sync::Arc;
@@ -70,6 +72,47 @@ fn main() {
7072
},
7173
};
7274

75+
let log_file_path = config_file.log_file_path.map(|p| PathBuf::from(p)).unwrap_or_else(|| {
76+
let mut default_log_path = PathBuf::from(&config_file.storage_dir_path);
77+
default_log_path.push("ldk-server.log");
78+
PathBuf::from(default_log_path)
79+
});
80+
let log_file = if log_file_path.exists() {
81+
match fs::File::options().append(true).open(log_file_path) {
82+
Ok(file) => file,
83+
Err(e) => {
84+
eprintln!("Could not open log file: {}", e);
85+
std::process::exit(-1);
86+
},
87+
}
88+
} else {
89+
// make parents if they don't exist
90+
if let Some(parent) = log_file_path.parent() {
91+
if let Err(e) = fs::create_dir_all(parent) {
92+
eprintln!("Could not create log file directory: {}", e);
93+
std::process::exit(-1);
94+
}
95+
}
96+
match fs::File::create(log_file_path) {
97+
Ok(file) => file,
98+
Err(e) => {
99+
eprintln!("Could not create log file: {}", e);
100+
std::process::exit(-1);
101+
},
102+
}
103+
};
104+
105+
let log_config = simplelog::ConfigBuilder::new().set_time_format_rfc3339().build();
106+
if let Err(e) = CombinedLogger::init(vec![
107+
// add simple logger for console output
108+
SimpleLogger::new(config_file.log_level, log_config.clone()),
109+
// add write logger for file output
110+
WriteLogger::new(config_file.log_level, log_config, log_file),
111+
]) {
112+
eprintln!("Failed to initialize logger: {}", e);
113+
std::process::exit(-1);
114+
}
115+
73116
ldk_node_config.storage_dir_path = config_file.storage_dir_path.clone();
74117
ldk_node_config.listening_addresses = Some(vec![config_file.listening_addr]);
75118
ldk_node_config.network = config_file.network;
@@ -142,7 +185,7 @@ fn main() {
142185
Arc::new(RabbitMqEventPublisher::new(rabbitmq_config))
143186
};
144187

145-
println!("Starting up...");
188+
info!("Starting up...");
146189
match node.start() {
147190
Ok(()) => {},
148191
Err(e) => {
@@ -151,7 +194,7 @@ fn main() {
151194
},
152195
}
153196

154-
println!(
197+
info!(
155198
"CONNECTION_STRING: {}@{}",
156199
node.node_id(),
157200
node.config().listening_addresses.as_ref().unwrap().first().unwrap()
@@ -161,7 +204,7 @@ fn main() {
161204
let mut sigterm_stream = match tokio::signal::unix::signal(SignalKind::terminate()) {
162205
Ok(stream) => stream,
163206
Err(e) => {
164-
println!("Failed to register for SIGTERM stream: {}", e);
207+
eprintln!("Failed to register for SIGTERM stream: {}", e);
165208
std::process::exit(-1);
166209
}
167210
};
@@ -174,25 +217,25 @@ fn main() {
174217
event = event_node.next_event_async() => {
175218
match event {
176219
Event::ChannelPending { channel_id, counterparty_node_id, .. } => {
177-
println!(
220+
info!(
178221
"CHANNEL_PENDING: {} from counterparty {}",
179222
channel_id, counterparty_node_id
180223
);
181224
if let Err(e) = event_node.event_handled() {
182-
eprintln!("Failed to mark event as handled: {e}");
225+
error!("Failed to mark event as handled: {e}");
183226
}
184227
},
185228
Event::ChannelReady { channel_id, counterparty_node_id, .. } => {
186-
println!(
229+
info!(
187230
"CHANNEL_READY: {} from counterparty {:?}",
188231
channel_id, counterparty_node_id
189232
);
190233
if let Err(e) = event_node.event_handled() {
191-
eprintln!("Failed to mark event as handled: {e}");
234+
error!("Failed to mark event as handled: {e}");
192235
}
193236
},
194237
Event::PaymentReceived { payment_id, payment_hash, amount_msat, .. } => {
195-
println!(
238+
info!(
196239
"PAYMENT_RECEIVED: with id {:?}, hash {}, amount_msat {}",
197240
payment_id, payment_hash, amount_msat
198241
);
@@ -233,7 +276,7 @@ fn main() {
233276
let payment = payment_to_proto(payment_details);
234277
upsert_payment_details(&event_node, Arc::clone(&paginated_store), &payment);
235278
} else {
236-
eprintln!("Unable to find payment with paymentId: {}", payment_id.to_string());
279+
error!("Unable to find payment with paymentId: {}", payment_id.to_string());
237280
}
238281
},
239282
Event::PaymentForwarded {
@@ -249,7 +292,7 @@ fn main() {
249292
outbound_amount_forwarded_msat
250293
} => {
251294

252-
println!("PAYMENT_FORWARDED: with outbound_amount_forwarded_msat {}, total_fee_earned_msat: {}, inbound channel: {}, outbound channel: {}",
295+
info!("PAYMENT_FORWARDED: with outbound_amount_forwarded_msat {}, total_fee_earned_msat: {}, inbound channel: {}, outbound channel: {}",
253296
outbound_amount_forwarded_msat.unwrap_or(0), total_fee_earned_msat.unwrap_or(0), prev_channel_id, next_channel_id
254297
);
255298

@@ -281,7 +324,7 @@ fn main() {
281324
}).await {
282325
Ok(_) => {},
283326
Err(e) => {
284-
println!("Failed to publish 'PaymentForwarded' event: {}", e);
327+
error!("Failed to publish 'PaymentForwarded' event: {}", e);
285328
continue;
286329
}
287330
};
@@ -293,17 +336,17 @@ fn main() {
293336
) {
294337
Ok(_) => {
295338
if let Err(e) = event_node.event_handled() {
296-
eprintln!("Failed to mark event as handled: {e}");
339+
error!("Failed to mark event as handled: {e}");
297340
}
298341
}
299342
Err(e) => {
300-
println!("Failed to write forwarded payment to persistence: {}", e);
343+
error!("Failed to write forwarded payment to persistence: {}", e);
301344
}
302345
}
303346
},
304347
_ => {
305348
if let Err(e) = event_node.event_handled() {
306-
eprintln!("Failed to mark event as handled: {e}");
349+
error!("Failed to mark event as handled: {e}");
307350
}
308351
},
309352
}
@@ -315,27 +358,27 @@ fn main() {
315358
let node_service = NodeService::new(Arc::clone(&node), Arc::clone(&paginated_store));
316359
runtime.spawn(async move {
317360
if let Err(err) = http1::Builder::new().serve_connection(io_stream, node_service).await {
318-
eprintln!("Failed to serve connection: {}", err);
361+
error!("Failed to serve connection: {}", err);
319362
}
320363
});
321364
},
322-
Err(e) => eprintln!("Failed to accept connection: {}", e),
365+
Err(e) => error!("Failed to accept connection: {}", e),
323366
}
324367
}
325368
_ = tokio::signal::ctrl_c() => {
326-
println!("Received CTRL-C, shutting down..");
369+
info!("Received CTRL-C, shutting down..");
327370
break;
328371
}
329372
_ = sigterm_stream.recv() => {
330-
println!("Received SIGTERM, shutting down..");
373+
info!("Received SIGTERM, shutting down..");
331374
break;
332375
}
333376
}
334377
}
335378
});
336379

337380
node.stop().expect("Shutdown should always succeed.");
338-
println!("Shutdown complete..");
381+
info!("Shutdown complete..");
339382
}
340383

341384
async fn publish_event_and_upsert_payment(
@@ -351,14 +394,14 @@ async fn publish_event_and_upsert_payment(
351394
match event_publisher.publish(EventEnvelope { event: Some(event) }).await {
352395
Ok(_) => {},
353396
Err(e) => {
354-
println!("Failed to publish '{}' event, : {}", event_name, e);
397+
error!("Failed to publish '{event_name}' event, : {e}");
355398
return;
356399
},
357400
};
358401

359402
upsert_payment_details(event_node, Arc::clone(&paginated_store), &payment);
360403
} else {
361-
eprintln!("Unable to find payment with paymentId: {}", payment_id);
404+
error!("Unable to find payment with paymentId: {payment_id}");
362405
}
363406
}
364407

@@ -377,11 +420,11 @@ fn upsert_payment_details(
377420
) {
378421
Ok(_) => {
379422
if let Err(e) = event_node.event_handled() {
380-
eprintln!("Failed to mark event as handled: {e}");
423+
error!("Failed to mark event as handled: {e}");
381424
}
382425
},
383426
Err(e) => {
384-
eprintln!("Failed to write payment to persistence: {}", e);
427+
error!("Failed to write payment to persistence: {e}");
385428
},
386429
}
387430
}

ldk-server/src/util/config.rs

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ use ldk_node::lightning::ln::msgs::SocketAddress;
33
use ldk_node::lightning::routing::gossip::NodeAlias;
44
use ldk_node::liquidity::LSPS2ServiceConfig;
55
use serde::{Deserialize, Serialize};
6+
use simplelog::LevelFilter;
67
use std::net::SocketAddr;
78
use std::path::Path;
89
use std::str::FromStr;
@@ -20,6 +21,8 @@ pub struct Config {
2021
pub rabbitmq_connection_string: String,
2122
pub rabbitmq_exchange_name: String,
2223
pub lsps2_service_config: Option<LSPS2ServiceConfig>,
24+
pub log_level: LevelFilter,
25+
pub log_file_path: Option<String>,
2326
}
2427

2528
#[derive(Debug)]
@@ -86,6 +89,21 @@ impl TryFrom<TomlConfig> for Config {
8689
None
8790
};
8891

92+
let log_level = toml_config
93+
.log
94+
.as_ref()
95+
.and_then(|log_config| log_config.level.as_ref())
96+
.map(|level_str| {
97+
LevelFilter::from_str(level_str).map_err(|e| {
98+
io::Error::new(
99+
io::ErrorKind::InvalidInput,
100+
format!("Invalid log level configured: {}", e),
101+
)
102+
})
103+
})
104+
.transpose()?
105+
.unwrap_or(LevelFilter::Debug);
106+
89107
let (rabbitmq_connection_string, rabbitmq_exchange_name) = {
90108
let rabbitmq = toml_config.rabbitmq.unwrap_or(RabbitmqConfig {
91109
connection_string: String::new(),
@@ -122,6 +140,8 @@ impl TryFrom<TomlConfig> for Config {
122140
rabbitmq_connection_string,
123141
rabbitmq_exchange_name,
124142
lsps2_service_config,
143+
log_level,
144+
log_file_path: toml_config.log.and_then(|l| l.file),
125145
})
126146
}
127147
}
@@ -135,6 +155,7 @@ pub struct TomlConfig {
135155
esplora: Option<EsploraConfig>,
136156
rabbitmq: Option<RabbitmqConfig>,
137157
liquidity: Option<LiquidityConfig>,
158+
log: Option<LogConfig>,
138159
}
139160

140161
#[derive(Deserialize, Serialize)]
@@ -167,6 +188,12 @@ struct EsploraConfig {
167188
server_url: String,
168189
}
169190

191+
#[derive(Deserialize, Serialize)]
192+
struct LogConfig {
193+
level: Option<String>,
194+
file: Option<String>,
195+
}
196+
170197
#[derive(Deserialize, Serialize)]
171198
struct RabbitmqConfig {
172199
connection_string: String,
@@ -260,6 +287,10 @@ mod tests {
260287
261288
[storage.disk]
262289
dir_path = "/tmp"
290+
291+
[log]
292+
level = "Trace"
293+
file = "/var/log/ldk-server.log"
263294
264295
[esplora]
265296
server_url = "https://mempool.space/api"
@@ -310,6 +341,8 @@ mod tests {
310341
max_payment_size_msat: 25000000000,
311342
client_trusts_lsp: true,
312343
}),
344+
log_level: LevelFilter::Trace,
345+
log_file_path: Some("/var/log/ldk-server.log".to_string()),
313346
};
314347

315348
assert_eq!(config.listening_addr, expected.listening_addr);
@@ -339,6 +372,10 @@ mod tests {
339372
340373
[storage.disk]
341374
dir_path = "/tmp"
375+
376+
[log]
377+
level = "Trace"
378+
file = "/var/log/ldk-server.log"
342379
343380
[bitcoind]
344381
rpc_address = "127.0.0.1:8332" # RPC endpoint
@@ -383,6 +420,10 @@ mod tests {
383420
384421
[storage.disk]
385422
dir_path = "/tmp"
423+
424+
[log]
425+
level = "Trace"
426+
file = "/var/log/ldk-server.log"
386427
387428
[bitcoind]
388429
rpc_address = "127.0.0.1:8332" # RPC endpoint

0 commit comments

Comments
 (0)