1pub(crate) use lightning::util::logger::{Logger as LdkLogger, Record as LdkRecord};
11pub(crate) use lightning::{log_bytes, log_debug, log_error, log_info, log_trace};
12
13pub use lightning::util::logger::Level as LogLevel;
14
15use chrono::Utc;
16use log::Level as LogFacadeLevel;
17use log::Record as LogFacadeRecord;
18
19#[cfg(not(feature = "uniffi"))]
20use core::fmt;
21use std::fs;
22use std::io::Write;
23use std::path::Path;
24use std::sync::Arc;
25
26#[cfg(not(feature = "uniffi"))]
29pub struct LogRecord<'a> {
30 pub level: LogLevel,
32 pub args: fmt::Arguments<'a>,
34 pub module_path: &'a str,
36 pub line: u32,
38}
39
40#[cfg(feature = "uniffi")]
47pub struct LogRecord {
48 pub level: LogLevel,
50 pub args: String,
52 pub module_path: String,
54 pub line: u32,
56}
57
58#[cfg(feature = "uniffi")]
59impl<'a> From<LdkRecord<'a>> for LogRecord {
60 fn from(record: LdkRecord) -> Self {
61 Self {
62 level: record.level,
63 args: record.args.to_string(),
64 module_path: record.module_path.to_string(),
65 line: record.line,
66 }
67 }
68}
69
70#[cfg(not(feature = "uniffi"))]
71impl<'a> From<LdkRecord<'a>> for LogRecord<'a> {
72 fn from(record: LdkRecord<'a>) -> Self {
73 Self {
74 level: record.level,
75 args: record.args,
76 module_path: record.module_path,
77 line: record.line,
78 }
79 }
80}
81
82#[cfg(not(feature = "uniffi"))]
88pub trait LogWriter: Send + Sync {
89 fn log<'a>(&self, record: LogRecord<'a>);
91}
92
93#[cfg(feature = "uniffi")]
102pub trait LogWriter: Send + Sync {
103 fn log(&self, record: LogRecord);
105}
106
107pub(crate) enum Writer {
109 FileWriter { file_path: String, max_log_level: LogLevel },
111 LogFacadeWriter,
113 CustomWriter(Arc<dyn LogWriter>),
115}
116
117impl LogWriter for Writer {
118 fn log(&self, record: LogRecord) {
119 match self {
120 Writer::FileWriter { file_path, max_log_level } => {
121 if record.level < *max_log_level {
122 return;
123 }
124
125 let log = format!(
126 "{} {:<5} [{}:{}] {}\n",
127 Utc::now().format("%Y-%m-%d %H:%M:%S"),
128 record.level.to_string(),
129 record.module_path,
130 record.line,
131 record.args
132 );
133
134 fs::OpenOptions::new()
135 .create(true)
136 .append(true)
137 .open(file_path)
138 .expect("Failed to open log file")
139 .write_all(log.as_bytes())
140 .expect("Failed to write to log file")
141 },
142 Writer::LogFacadeWriter => {
143 let mut builder = LogFacadeRecord::builder();
144
145 match record.level {
146 LogLevel::Gossip | LogLevel::Trace => builder.level(LogFacadeLevel::Trace),
147 LogLevel::Debug => builder.level(LogFacadeLevel::Debug),
148 LogLevel::Info => builder.level(LogFacadeLevel::Info),
149 LogLevel::Warn => builder.level(LogFacadeLevel::Warn),
150 LogLevel::Error => builder.level(LogFacadeLevel::Error),
151 };
152
153 #[cfg(not(feature = "uniffi"))]
154 log::logger().log(
155 &builder
156 .module_path(Some(record.module_path))
157 .line(Some(record.line))
158 .args(format_args!("{}", record.args))
159 .build(),
160 );
161 #[cfg(feature = "uniffi")]
162 log::logger().log(
163 &builder
164 .module_path(Some(&record.module_path))
165 .line(Some(record.line))
166 .args(format_args!("{}", record.args))
167 .build(),
168 );
169 },
170 Writer::CustomWriter(custom_logger) => custom_logger.log(record),
171 }
172 }
173}
174
175pub(crate) struct Logger {
176 writer: Writer,
178}
179
180impl Logger {
181 pub fn new_fs_writer(file_path: String, max_log_level: LogLevel) -> Result<Self, ()> {
184 if let Some(parent_dir) = Path::new(&file_path).parent() {
185 fs::create_dir_all(parent_dir)
186 .map_err(|e| eprintln!("ERROR: Failed to create log parent directory: {}", e))?;
187
188 fs::OpenOptions::new()
190 .create(true)
191 .append(true)
192 .open(&file_path)
193 .map_err(|e| eprintln!("ERROR: Failed to open log file: {}", e))?;
194 }
195
196 Ok(Self { writer: Writer::FileWriter { file_path, max_log_level } })
197 }
198
199 pub fn new_log_facade() -> Self {
200 Self { writer: Writer::LogFacadeWriter }
201 }
202
203 pub fn new_custom_writer(log_writer: Arc<dyn LogWriter>) -> Self {
204 Self { writer: Writer::CustomWriter(log_writer) }
205 }
206}
207
208impl LdkLogger for Logger {
209 fn log(&self, record: LdkRecord) {
210 match &self.writer {
211 Writer::FileWriter { file_path: _, max_log_level } => {
212 if record.level < *max_log_level {
213 return;
214 }
215 self.writer.log(record.into());
216 },
217 Writer::LogFacadeWriter => {
218 self.writer.log(record.into());
219 },
220 Writer::CustomWriter(_arc) => {
221 self.writer.log(record.into());
222 },
223 }
224 }
225}