Skip to content

Commit 053933b

Browse files
Log structured peer id, channel id and payment hash
Since most log messages no longer include the channel ID in their text, this commit updates the test logger to output the structured channel ID instead. The ID is truncated for readability. Similarly peer id and payment hash are logged. Co-authored-by: Matt Corallo <git@bluematt.me>
1 parent 65f7f7f commit 053933b

File tree

1 file changed

+117
-5
lines changed

1 file changed

+117
-5
lines changed

lightning/src/util/logger.rs

Lines changed: 117 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ use bitcoin::secp256k1::PublicKey;
1818
use core::cmp;
1919
use core::fmt;
2020
use core::fmt::Display;
21+
use core::fmt::Write;
2122
use core::ops::Deref;
2223

2324
use crate::ln::types::ChannelId;
@@ -107,7 +108,8 @@ pub struct Record<$($args)?> {
107108
/// generated.
108109
pub peer_id: Option<PublicKey>,
109110
/// The channel id of the channel pertaining to the logged record. May be a temporary id before
110-
/// the channel has been funded.
111+
/// the channel has been funded. Since channel_id is not repeated in the message body,
112+
/// include it in the log output so entries remain clear.
111113
pub channel_id: Option<ChannelId>,
112114
#[cfg(not(c_bindings))]
113115
/// The message body.
@@ -156,8 +158,63 @@ impl<$($args)?> Record<$($args)?> {
156158

157159
impl<$($args)?> Display for Record<$($args)?> {
158160
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
159-
let context = format!("{:<5} [{}:{}]", self.level, self.module_path, self.line);
160-
write!(f, "{:<48} {}", context, self.args)
161+
let mut context_formatter = SubstringFormatter::new(48, f);
162+
write!(&mut context_formatter, "{:<5} [{}:{}]", self.level, self.module_path, self.line)?;
163+
context_formatter.pad_remaining()?;
164+
165+
let mut channel_formatter = SubstringFormatter::new(9, f);
166+
if let Some(channel_id) = self.channel_id {
167+
write!(channel_formatter, "ch:{}", channel_id)?;
168+
}
169+
channel_formatter.pad_remaining()?;
170+
171+
#[cfg(not(test))]
172+
{
173+
let mut peer_formatter = SubstringFormatter::new(9, f);
174+
if let Some(peer_id) = self.peer_id {
175+
write!(peer_formatter, " p:{}", peer_id)?;
176+
}
177+
peer_formatter.pad_remaining()?;
178+
179+
let mut payment_formatter = SubstringFormatter::new(9, f);
180+
if let Some(payment_hash) = self.payment_hash {
181+
write!(payment_formatter, " h:{}", payment_hash)?;
182+
}
183+
payment_formatter.pad_remaining()?;
184+
185+
write!(f, " {}", self.args)
186+
}
187+
188+
#[cfg(test)]
189+
{
190+
write!(f, " {}", self.args)?;
191+
192+
let mut open_bracket_written = false;
193+
if let Some(peer_id) = self.peer_id {
194+
write!(f, " [")?;
195+
open_bracket_written = true;
196+
let mut peer_formatter = SubstringFormatter::new(8, f);
197+
write!(peer_formatter, "p:{}", peer_id)?;
198+
}
199+
200+
if let Some(payment_hash) = self.payment_hash {
201+
if !open_bracket_written {
202+
write!(f, " [")?;
203+
open_bracket_written = true;
204+
} else {
205+
write!(f, " ")?;
206+
}
207+
208+
let mut payment_formatter = SubstringFormatter::new(8, f);
209+
write!(payment_formatter, "h:{}", payment_hash)?;
210+
}
211+
212+
if open_bracket_written {
213+
write!(f, "]")?;
214+
}
215+
216+
Ok(())
217+
}
161218
}
162219
}
163220
} }
@@ -166,9 +223,64 @@ impl_record!('a, );
166223
#[cfg(c_bindings)]
167224
impl_record!(, 'a);
168225

169-
/// A trait encapsulating the operations required of a logger.
226+
// Writes only up to a certain number of unicode characters to the underlying formatter. This handles multi-byte Unicode
227+
// characters safely.
228+
struct SubstringFormatter<'fmt: 'r, 'r> {
229+
remaining_chars: usize,
230+
fmt: &'r mut fmt::Formatter<'fmt>,
231+
}
232+
233+
impl<'fmt: 'r, 'r> SubstringFormatter<'fmt, 'r> {
234+
fn new(length: usize, formatter: &'r mut fmt::Formatter<'fmt>) -> Self {
235+
debug_assert!(length <= 100);
236+
SubstringFormatter { remaining_chars: length, fmt: formatter }
237+
}
238+
239+
// Pads the underlying formatter with spaces until the remaining character count.
240+
fn pad_remaining(&mut self) -> fmt::Result {
241+
// Use a constant string to avoid allocations.
242+
const PAD100: &str = " "; // 100 spaces
243+
244+
self.fmt.write_str(&PAD100[..self.remaining_chars])?;
245+
self.remaining_chars = 0;
246+
247+
Ok(())
248+
}
249+
}
250+
251+
impl<'fmt: 'r, 'r> Write for SubstringFormatter<'fmt, 'r> {
252+
fn write_str(&mut self, s: &str) -> fmt::Result {
253+
let mut char_count = 0;
254+
let mut next_char_byte_pos = 0;
255+
256+
// Iterate over the unicode character boundaries in `s`. We take one more than the number of remaining
257+
// characters so we can find the byte boundary where we should stop writing.
258+
for (pos, _) in s.char_indices().take(self.remaining_chars + 1) {
259+
char_count += 1;
260+
next_char_byte_pos = pos;
261+
}
262+
263+
// Determine where to split the string.
264+
let at_cut_off_point = char_count == self.remaining_chars + 1;
265+
let split_pos = if at_cut_off_point {
266+
self.remaining_chars = 0;
267+
next_char_byte_pos
268+
} else {
269+
// Not enough characters in this chunk.
270+
self.remaining_chars -= char_count;
271+
s.len()
272+
};
273+
274+
// Write only the substring up to the split position into the formatter.
275+
self.fmt.write_str(&s[..split_pos])
276+
}
277+
}
278+
279+
/// A trait encapsulating the operations required of a logger. Keep in mind that log messages might not be entirely
280+
/// self-explanatory and may need accompanying context fields to be fully understood.
170281
pub trait Logger {
171-
/// Logs the [`Record`].
282+
/// Logs the [`Record`]. Since the record's [`Record::channel_id`] is not embedded in the message body, log
283+
/// implementations should print it alongside the message to keep entries clear.
172284
fn log(&self, record: Record);
173285
}
174286

0 commit comments

Comments
 (0)