forked from lightningdevkit/ldk-node
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathlogger.rs
222 lines (199 loc) · 6.23 KB
/
logger.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
// This file is Copyright its original authors, visible in version control history.
//
// This file is licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
// http://opensource.org/licenses/MIT>, at your option. You may not use this file except in
// accordance with one or both of these licenses.
//! Logging-related objects.
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::{debug, error, info, trace, warn};
#[cfg(not(feature = "uniffi"))]
use core::fmt;
use std::fs;
use std::io::Write;
use std::path::Path;
use std::sync::Arc;
/// A unit of logging output with metadata to enable filtering `module_path`,
/// `file`, and `line` to inform on log's source.
#[cfg(not(feature = "uniffi"))]
pub struct LogRecord<'a> {
/// The verbosity level of the message.
pub level: LogLevel,
/// The message body.
pub args: fmt::Arguments<'a>,
/// The module path of the message.
pub module_path: &'a str,
/// The line containing the message.
pub line: u32,
}
/// A unit of logging output with metadata to enable filtering `module_path`,
/// `file`, and `line` to inform on log's source.
///
/// This version is used when the `uniffi` feature is enabled.
/// It is similar to the non-`uniffi` version, but it omits the lifetime parameter
/// for the `LogRecord`, as the Uniffi-exposed interface cannot handle lifetimes.
#[cfg(feature = "uniffi")]
pub struct LogRecord {
/// The verbosity level of the message.
pub level: LogLevel,
/// The message body.
pub args: String,
/// The module path of the message.
pub module_path: String,
/// The line containing the message.
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,
}
}
}
/// Defines the behavior required for writing log records.
///
/// Implementors of this trait are responsible for handling log messages,
/// which may involve formatting, filtering, and forwarding them to specific
/// outputs.
#[cfg(not(feature = "uniffi"))]
pub trait LogWriter: Send + Sync {
/// Log the record.
fn log<'a>(&self, record: LogRecord<'a>);
}
/// Defines the behavior required for writing log records.
///
/// Implementors of this trait are responsible for handling log messages,
/// which may involve formatting, filtering, and forwarding them to specific
/// outputs.
/// This version is used when the `uniffi` feature is enabled.
/// It is similar to the non-`uniffi` version, but it omits the lifetime parameter
/// for the `LogRecord`, as the Uniffi-exposed interface cannot handle lifetimes.
#[cfg(feature = "uniffi")]
pub trait LogWriter: Send + Sync {
/// Log the record.
fn log(&self, record: LogRecord);
}
/// Defines a writer for [`Logger`].
pub(crate) enum Writer {
/// Writes logs to the file system.
FileWriter { file_path: String, level: LogLevel },
/// Forwards logs to the `log` facade.
LogFacadeWriter { level: LogLevel },
/// Forwards logs to a custom writer.
CustomWriter(Arc<dyn LogWriter>),
}
impl LogWriter for Writer {
fn log(&self, record: LogRecord) {
match self {
Writer::FileWriter { file_path, level } => {
if record.level < *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 { level } => {
macro_rules! log_with_level {
($log_level:expr, $($args:tt)*) => {
match $log_level {
LogLevel::Gossip | LogLevel::Trace => trace!($($args)*),
LogLevel::Debug => debug!($($args)*),
LogLevel::Info => info!($($args)*),
LogLevel::Warn => warn!($($args)*),
LogLevel::Error => error!($($args)*),
}
};
}
log_with_level!(
level,
"{} {:<5} [{}:{}] {}",
Utc::now().format("%Y-%m-%d %H:%M:%S"),
record.level,
record.module_path,
record.line,
record.args
)
},
Writer::CustomWriter(custom_logger) => custom_logger.log(record),
}
}
}
pub(crate) struct Logger {
/// Specifies the logger's writer.
writer: Writer,
}
impl Logger {
/// Creates a new logger with a filesystem writer. The parameters to this function
/// are the path to the log file, and the log level.
pub fn new_fs_writer(file_path: String, 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))?;
// make sure the file exists.
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, level } })
}
pub fn new_log_facade(level: LogLevel) -> Self {
Self { writer: Writer::LogFacadeWriter { level } }
}
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: _, level } => {
if record.level < *level {
return;
}
self.writer.log(record.into());
},
Writer::LogFacadeWriter { level } => {
if record.level < *level {
return;
}
self.writer.log(record.into());
},
Writer::CustomWriter(_arc) => {
self.writer.log(record.into());
},
}
}
}