Skip to content

Commit 320676e

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 implements a logger for the log facade that writes logs to the console as well as to the log file. We also add to the config options for setting the log file and log level.
1 parent 281b5e8 commit 320676e

7 files changed

Lines changed: 274 additions & 23 deletions

File tree

Cargo.lock

Lines changed: 2 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+
chrono = { version = "0.4", default-features = false, features = ["clock"] }
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: 58 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,15 @@ use crate::io::persist::{
2525
PAYMENTS_PERSISTENCE_SECONDARY_NAMESPACE,
2626
};
2727
use crate::util::config::{load_config, ChainSource};
28+
use crate::util::logger::ServerLogger;
2829
use crate::util::proto_adapter::{forwarded_payment_to_proto, payment_to_proto};
2930
use hex::DisplayHex;
3031
use ldk_node::config::Config;
3132
use ldk_node::lightning::ln::channelmanager::PaymentId;
3233
use ldk_server_protos::events;
3334
use ldk_server_protos::events::{event_envelope, EventEnvelope};
3435
use ldk_server_protos::types::Payment;
36+
use log::{error, info};
3537
use prost::Message;
3638
use rand::Rng;
3739
use std::fs;
@@ -70,6 +72,25 @@ 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+
default_log_path
79+
});
80+
81+
if log_file_path == PathBuf::from(&config_file.storage_dir_path) {
82+
eprintln!("Log file path cannot be the same as storage directory path.");
83+
std::process::exit(-1);
84+
}
85+
86+
let logger = match ServerLogger::init(config_file.log_level, &log_file_path) {
87+
Ok(logger) => logger,
88+
Err(e) => {
89+
eprintln!("Failed to initialize logger: {e}");
90+
std::process::exit(-1);
91+
},
92+
};
93+
7394
ldk_node_config.storage_dir_path = config_file.storage_dir_path.clone();
7495
ldk_node_config.listening_addresses = Some(vec![config_file.listening_addr]);
7596
ldk_node_config.network = config_file.network;
@@ -142,7 +163,7 @@ fn main() {
142163
Arc::new(RabbitMqEventPublisher::new(rabbitmq_config))
143164
};
144165

145-
println!("Starting up...");
166+
info!("Starting up...");
146167
match node.start() {
147168
Ok(()) => {},
148169
Err(e) => {
@@ -151,17 +172,26 @@ fn main() {
151172
},
152173
}
153174

154-
println!(
175+
info!(
155176
"CONNECTION_STRING: {}@{}",
156177
node.node_id(),
157178
node.config().listening_addresses.as_ref().unwrap().first().unwrap()
158179
);
159180

160181
runtime.block_on(async {
182+
// Register SIGHUP handler for log rotation
183+
let mut sighup_stream = match tokio::signal::unix::signal(SignalKind::hangup()) {
184+
Ok(stream) => stream,
185+
Err(e) => {
186+
eprintln!("Failed to register SIGHUP handler: {e}");
187+
std::process::exit(-1);
188+
}
189+
};
190+
161191
let mut sigterm_stream = match tokio::signal::unix::signal(SignalKind::terminate()) {
162192
Ok(stream) => stream,
163193
Err(e) => {
164-
println!("Failed to register for SIGTERM stream: {}", e);
194+
eprintln!("Failed to register for SIGTERM stream: {}", e);
165195
std::process::exit(-1);
166196
}
167197
};
@@ -174,25 +204,25 @@ fn main() {
174204
event = event_node.next_event_async() => {
175205
match event {
176206
Event::ChannelPending { channel_id, counterparty_node_id, .. } => {
177-
println!(
207+
info!(
178208
"CHANNEL_PENDING: {} from counterparty {}",
179209
channel_id, counterparty_node_id
180210
);
181211
if let Err(e) = event_node.event_handled() {
182-
eprintln!("Failed to mark event as handled: {e}");
212+
error!("Failed to mark event as handled: {e}");
183213
}
184214
},
185215
Event::ChannelReady { channel_id, counterparty_node_id, .. } => {
186-
println!(
216+
info!(
187217
"CHANNEL_READY: {} from counterparty {:?}",
188218
channel_id, counterparty_node_id
189219
);
190220
if let Err(e) = event_node.event_handled() {
191-
eprintln!("Failed to mark event as handled: {e}");
221+
error!("Failed to mark event as handled: {e}");
192222
}
193223
},
194224
Event::PaymentReceived { payment_id, payment_hash, amount_msat, .. } => {
195-
println!(
225+
info!(
196226
"PAYMENT_RECEIVED: with id {:?}, hash {}, amount_msat {}",
197227
payment_id, payment_hash, amount_msat
198228
);
@@ -233,7 +263,7 @@ fn main() {
233263
let payment = payment_to_proto(payment_details);
234264
upsert_payment_details(&event_node, Arc::clone(&paginated_store), &payment);
235265
} else {
236-
eprintln!("Unable to find payment with paymentId: {}", payment_id.to_string());
266+
error!("Unable to find payment with paymentId: {}", payment_id.to_string());
237267
}
238268
},
239269
Event::PaymentForwarded {
@@ -249,7 +279,7 @@ fn main() {
249279
outbound_amount_forwarded_msat
250280
} => {
251281

252-
println!("PAYMENT_FORWARDED: with outbound_amount_forwarded_msat {}, total_fee_earned_msat: {}, inbound channel: {}, outbound channel: {}",
282+
info!("PAYMENT_FORWARDED: with outbound_amount_forwarded_msat {}, total_fee_earned_msat: {}, inbound channel: {}, outbound channel: {}",
253283
outbound_amount_forwarded_msat.unwrap_or(0), total_fee_earned_msat.unwrap_or(0), prev_channel_id, next_channel_id
254284
);
255285

@@ -281,7 +311,7 @@ fn main() {
281311
}).await {
282312
Ok(_) => {},
283313
Err(e) => {
284-
println!("Failed to publish 'PaymentForwarded' event: {}", e);
314+
error!("Failed to publish 'PaymentForwarded' event: {}", e);
285315
continue;
286316
}
287317
};
@@ -293,17 +323,17 @@ fn main() {
293323
) {
294324
Ok(_) => {
295325
if let Err(e) = event_node.event_handled() {
296-
eprintln!("Failed to mark event as handled: {e}");
326+
error!("Failed to mark event as handled: {e}");
297327
}
298328
}
299329
Err(e) => {
300-
println!("Failed to write forwarded payment to persistence: {}", e);
330+
error!("Failed to write forwarded payment to persistence: {}", e);
301331
}
302332
}
303333
},
304334
_ => {
305335
if let Err(e) = event_node.event_handled() {
306-
eprintln!("Failed to mark event as handled: {e}");
336+
error!("Failed to mark event as handled: {e}");
307337
}
308338
},
309339
}
@@ -315,27 +345,32 @@ fn main() {
315345
let node_service = NodeService::new(Arc::clone(&node), Arc::clone(&paginated_store));
316346
runtime.spawn(async move {
317347
if let Err(err) = http1::Builder::new().serve_connection(io_stream, node_service).await {
318-
eprintln!("Failed to serve connection: {}", err);
348+
error!("Failed to serve connection: {}", err);
319349
}
320350
});
321351
},
322-
Err(e) => eprintln!("Failed to accept connection: {}", e),
352+
Err(e) => error!("Failed to accept connection: {}", e),
323353
}
324354
}
325355
_ = tokio::signal::ctrl_c() => {
326-
println!("Received CTRL-C, shutting down..");
356+
info!("Received CTRL-C, shutting down..");
327357
break;
328358
}
359+
_ = sighup_stream.recv() => {
360+
if let Err(e) = logger.reopen() {
361+
error!("Failed to reopen log file on SIGHUP: {e}");
362+
}
363+
}
329364
_ = sigterm_stream.recv() => {
330-
println!("Received SIGTERM, shutting down..");
365+
info!("Received SIGTERM, shutting down..");
331366
break;
332367
}
333368
}
334369
}
335370
});
336371

337372
node.stop().expect("Shutdown should always succeed.");
338-
println!("Shutdown complete..");
373+
info!("Shutdown complete..");
339374
}
340375

341376
async fn publish_event_and_upsert_payment(
@@ -351,14 +386,14 @@ async fn publish_event_and_upsert_payment(
351386
match event_publisher.publish(EventEnvelope { event: Some(event) }).await {
352387
Ok(_) => {},
353388
Err(e) => {
354-
println!("Failed to publish '{}' event, : {}", event_name, e);
389+
error!("Failed to publish '{event_name}' event, : {e}");
355390
return;
356391
},
357392
};
358393

359394
upsert_payment_details(event_node, Arc::clone(&paginated_store), &payment);
360395
} else {
361-
eprintln!("Unable to find payment with paymentId: {}", payment_id);
396+
error!("Unable to find payment with paymentId: {payment_id}");
362397
}
363398
}
364399

@@ -377,11 +412,11 @@ fn upsert_payment_details(
377412
) {
378413
Ok(_) => {
379414
if let Err(e) = event_node.event_handled() {
380-
eprintln!("Failed to mark event as handled: {e}");
415+
error!("Failed to mark event as handled: {e}");
381416
}
382417
},
383418
Err(e) => {
384-
eprintln!("Failed to write payment to persistence: {}", e);
419+
error!("Failed to write payment to persistence: {e}");
385420
},
386421
}
387422
}

ldk-server/src/util/config.rs

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ use ldk_node::bitcoin::Network;
22
use ldk_node::lightning::ln::msgs::SocketAddress;
33
use ldk_node::lightning::routing::gossip::NodeAlias;
44
use ldk_node::liquidity::LSPS2ServiceConfig;
5+
use log::LevelFilter;
56
use serde::{Deserialize, Serialize};
67
use std::net::SocketAddr;
78
use std::path::Path;
@@ -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)