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