pub(crate) use lightning::util::logger::{Logger as LdkLogger, Record as LdkRecord};
pub(crate) use lightning::{log_bytes, log_debug, log_error, log_info, log_trace};
pub use lightning::util::logger::Level as LogLevel;
use chrono::Utc;
use log::Level as LogFacadeLevel;
use log::Record as LogFacadeRecord;
#[cfg(not(feature = "uniffi"))]
use core::fmt;
use std::fs;
use std::io::Write;
use std::path::Path;
use std::sync::Arc;
#[cfg(not(feature = "uniffi"))]
pub struct LogRecord<'a> {
pub level: LogLevel,
pub args: fmt::Arguments<'a>,
pub module_path: &'a str,
pub line: u32,
}
#[cfg(feature = "uniffi")]
pub struct LogRecord {
pub level: LogLevel,
pub args: String,
pub module_path: String,
pub line: u32,
}
#[cfg(feature = "uniffi")]
impl<'a> From<LdkRecord<'a>> for LogRecord {
fn from(record: LdkRecord) -> Self {
Self {
level: record.level,
args: record.args.to_string(),
module_path: record.module_path.to_string(),
line: record.line,
}
}
}
#[cfg(not(feature = "uniffi"))]
impl<'a> From<LdkRecord<'a>> for LogRecord<'a> {
fn from(record: LdkRecord<'a>) -> Self {
Self {
level: record.level,
args: record.args,
module_path: record.module_path,
line: record.line,
}
}
}
#[cfg(not(feature = "uniffi"))]
pub trait LogWriter: Send + Sync {
fn log<'a>(&self, record: LogRecord<'a>);
}
#[cfg(feature = "uniffi")]
pub trait LogWriter: Send + Sync {
fn log(&self, record: LogRecord);
}
pub(crate) enum Writer {
FileWriter { file_path: String, max_log_level: LogLevel },
LogFacadeWriter,
CustomWriter(Arc<dyn LogWriter>),
}
impl LogWriter for Writer {
fn log(&self, record: LogRecord) {
match self {
Writer::FileWriter { file_path, max_log_level } => {
if record.level < *max_log_level {
return;
}
let log = format!(
"{} {:<5} [{}:{}] {}\n",
Utc::now().format("%Y-%m-%d %H:%M:%S"),
record.level.to_string(),
record.module_path,
record.line,
record.args
);
fs::OpenOptions::new()
.create(true)
.append(true)
.open(file_path)
.expect("Failed to open log file")
.write_all(log.as_bytes())
.expect("Failed to write to log file")
},
Writer::LogFacadeWriter => {
let mut builder = LogFacadeRecord::builder();
match record.level {
LogLevel::Gossip | LogLevel::Trace => builder.level(LogFacadeLevel::Trace),
LogLevel::Debug => builder.level(LogFacadeLevel::Debug),
LogLevel::Info => builder.level(LogFacadeLevel::Info),
LogLevel::Warn => builder.level(LogFacadeLevel::Warn),
LogLevel::Error => builder.level(LogFacadeLevel::Error),
};
#[cfg(not(feature = "uniffi"))]
log::logger().log(
&builder
.module_path(Some(record.module_path))
.line(Some(record.line))
.args(format_args!("{}", record.args))
.build(),
);
#[cfg(feature = "uniffi")]
log::logger().log(
&builder
.module_path(Some(&record.module_path))
.line(Some(record.line))
.args(format_args!("{}", record.args))
.build(),
);
},
Writer::CustomWriter(custom_logger) => custom_logger.log(record),
}
}
}
pub(crate) struct Logger {
writer: Writer,
}
impl Logger {
pub fn new_fs_writer(file_path: String, max_log_level: LogLevel) -> Result<Self, ()> {
if let Some(parent_dir) = Path::new(&file_path).parent() {
fs::create_dir_all(parent_dir)
.map_err(|e| eprintln!("ERROR: Failed to create log parent directory: {}", e))?;
fs::OpenOptions::new()
.create(true)
.append(true)
.open(&file_path)
.map_err(|e| eprintln!("ERROR: Failed to open log file: {}", e))?;
}
Ok(Self { writer: Writer::FileWriter { file_path, max_log_level } })
}
pub fn new_log_facade() -> Self {
Self { writer: Writer::LogFacadeWriter }
}
pub fn new_custom_writer(log_writer: Arc<dyn LogWriter>) -> Self {
Self { writer: Writer::CustomWriter(log_writer) }
}
}
impl LdkLogger for Logger {
fn log(&self, record: LdkRecord) {
match &self.writer {
Writer::FileWriter { file_path: _, max_log_level } => {
if record.level < *max_log_level {
return;
}
self.writer.log(record.into());
},
Writer::LogFacadeWriter => {
self.writer.log(record.into());
},
Writer::CustomWriter(_arc) => {
self.writer.log(record.into());
},
}
}
}