Skip to content

Commit 144f17a

Browse files
committed
Add log spans using thread local storage in std
1 parent 62c5849 commit 144f17a

File tree

3 files changed

+135
-7
lines changed

3 files changed

+135
-7
lines changed

lightning-macros/src/lib.rs

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,13 @@
2020

2121
extern crate alloc;
2222

23+
use alloc::string::String;
2324
use alloc::string::ToString;
2425
use proc_macro::{Delimiter, Group, TokenStream, TokenTree};
2526
use proc_macro2::TokenStream as TokenStream2;
2627
use quote::quote;
2728
use syn::spanned::Spanned;
28-
use syn::{parse, ImplItemFn, Token};
29+
use syn::{parse, parse2, Expr, ExprLit, ImplItemFn, Lit, Token};
2930
use syn::{parse_macro_input, Item};
3031

3132
fn add_async_method(mut parsed: ImplItemFn) -> TokenStream {
@@ -400,3 +401,44 @@ pub fn xtest_inventory(_input: TokenStream) -> TokenStream {
400401

401402
TokenStream::from(expanded)
402403
}
404+
405+
/// Adds a logging scope at the top of a method.
406+
#[proc_macro_attribute]
407+
pub fn log_scope(attrs: TokenStream, meth: TokenStream) -> TokenStream {
408+
let attrs: TokenStream2 = parse_macro_input!(attrs as TokenStream2);
409+
let mut name_attr: Option<String> = None;
410+
if !attrs.is_empty() {
411+
// Expect something like `name = "foo"`
412+
let expr: Expr = parse2(attrs.clone()).expect("invalid attribute syntax");
413+
414+
if let Expr::Assign(assign) = expr {
415+
// Check left-hand side is `name`.
416+
if let Expr::Path(path) = *assign.left {
417+
if path.path.is_ident("name") {
418+
if let Expr::Lit(ExprLit { lit: Lit::Str(s), .. }) = *assign.right {
419+
name_attr = Some(s.value());
420+
}
421+
}
422+
}
423+
}
424+
}
425+
426+
let mut meth = if let Ok(parsed) = parse::<syn::ItemFn>(meth) {
427+
parsed
428+
} else {
429+
return (quote! {
430+
compile_error!("log_scope can only be set on methods")
431+
})
432+
.into();
433+
};
434+
435+
// Use the attribute name if present, otherwise fall back to the function name
436+
let name = name_attr.unwrap_or_else(|| meth.sig.ident.to_string());
437+
438+
let stmt = quote! {
439+
let _logging_context = crate::util::logger::LoggerScope::new(#name);
440+
};
441+
442+
meth.block.stmts.insert(0, parse(stmt.into()).unwrap());
443+
quote! { #meth }.into()
444+
}

lightning/src/util/logger.rs

Lines changed: 81 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
1616
use bitcoin::secp256k1::PublicKey;
1717

18+
use core::cell::RefCell;
1819
use core::cmp;
1920
use core::fmt;
2021
use core::fmt::Display;
@@ -130,6 +131,9 @@ pub struct Record<$($args)?> {
130131
/// Note that this is only filled in for logs pertaining to a specific payment, and will be
131132
/// `None` for logs which are not directly related to a payment.
132133
pub payment_hash: Option<PaymentHash>,
134+
/// The names of the surrounding spans, if any.
135+
#[cfg(feature = "std")]
136+
pub spans: Vec<&'static str>,
133137
}
134138

135139
impl<$($args)?> Record<$($args)?> {
@@ -138,7 +142,10 @@ impl<$($args)?> Record<$($args)?> {
138142
/// This is not exported to bindings users as fmt can't be used in C
139143
#[inline]
140144
pub fn new<$($nonstruct_args)?>(
141-
level: Level, peer_id: Option<PublicKey>, channel_id: Option<ChannelId>,
145+
level: Level,
146+
#[cfg(feature = "std")]
147+
spans: Vec<&'static str>,
148+
peer_id: Option<PublicKey>, channel_id: Option<ChannelId>,
142149
args: fmt::Arguments<'a>, module_path: &'static str, file: &'static str, line: u32,
143150
payment_hash: Option<PaymentHash>
144151
) -> Record<$($args)?> {
@@ -154,6 +161,8 @@ impl<$($args)?> Record<$($args)?> {
154161
file,
155162
line,
156163
payment_hash,
164+
#[cfg(feature = "std")]
165+
spans,
157166
}
158167
}
159168
}
@@ -189,7 +198,20 @@ impl<$($args)?> Display for Record<$($args)?> {
189198

190199
#[cfg(test)]
191200
{
192-
write!(f, " {}", self.args)?;
201+
write!(f, " ")?;
202+
#[cfg(feature = "std")]
203+
if !self.spans.is_empty() {
204+
write!(f, "[")?;
205+
for (i, span) in self.spans.iter().enumerate() {
206+
if i > 0 {
207+
write!(f, "->")?;
208+
}
209+
write!(f, "{}", span)?;
210+
}
211+
write!(f, "] ")?;
212+
}
213+
214+
write!(f, "{}", self.args)?;
193215

194216
let mut open_bracket_written = false;
195217
if let Some(peer_id) = self.peer_id {
@@ -384,14 +406,70 @@ impl<T: fmt::Display, I: core::iter::Iterator<Item = T> + Clone> fmt::Display fo
384406
}
385407
}
386408

409+
#[cfg(feature = "std")]
410+
thread_local! {
411+
/// The thread-local stack of loggers.
412+
pub static TLS_LOGGER: RefCell<Vec<&'static str>> = RefCell::new(Vec::new());
413+
}
414+
415+
/// A scope which pushes a logger on a thread-local stack for the duration of the scope.
416+
pub struct LoggerScope<'a> {
417+
_marker: core::marker::PhantomData<&'a ()>,
418+
}
419+
420+
impl<'a> LoggerScope<'a> {
421+
/// Pushes a logger onto the thread-local logger stack.
422+
pub fn new(span: &'static str) -> Self {
423+
#[cfg(feature = "std")]
424+
TLS_LOGGER.with(|cell| {
425+
let mut stack = cell.borrow_mut();
426+
stack.push(span);
427+
});
428+
429+
LoggerScope { _marker: core::marker::PhantomData }
430+
}
431+
}
432+
433+
impl<'a> Drop for LoggerScope<'a> {
434+
fn drop(&mut self) {
435+
#[cfg(feature = "std")]
436+
TLS_LOGGER.with(|cell| {
437+
let mut stack = cell.borrow_mut();
438+
stack.pop();
439+
});
440+
}
441+
}
442+
387443
#[cfg(test)]
388444
mod tests {
389445
use crate::ln::types::ChannelId;
390446
use crate::sync::Arc;
391447
use crate::types::payment::PaymentHash;
392-
use crate::util::logger::{Level, Logger, WithContext};
448+
use crate::util::logger::{Level, Logger, LoggerScope, WithContext};
393449
use crate::util::test_utils::TestLogger;
394450
use bitcoin::secp256k1::{PublicKey, Secp256k1, SecretKey};
451+
use lightning_macros::log_scope;
452+
453+
#[test]
454+
fn logger_scope() {
455+
let logger = TestLogger::new();
456+
let _scope = LoggerScope::new("test_logger_scope");
457+
log_info!(logger, "Info");
458+
}
459+
460+
#[test]
461+
#[log_scope(name = "test_logger_scope_proc_macro")]
462+
fn logger_scope_proc_macro() {
463+
let logger = TestLogger::new();
464+
log_info!(logger, "Info");
465+
}
466+
467+
#[test]
468+
#[log_scope]
469+
fn logger_scope_proc_macro_no_name() {
470+
let logger = TestLogger::new();
471+
log_info!(logger, "Info");
472+
}
395473

396474
#[test]
397475
fn test_level_show() {

lightning/src/util/macro_logger.rs

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -174,9 +174,17 @@ macro_rules! log_spendable {
174174
#[doc(hidden)]
175175
#[macro_export]
176176
macro_rules! log_given_level {
177-
($logger: expr, $lvl:expr, $($arg:tt)+) => (
178-
$logger.log($crate::util::logger::Record::new($lvl, None, None, format_args!($($arg)+), module_path!(), file!(), line!(), None))
179-
);
177+
($logger: expr, $lvl:expr, $($arg:tt)+) => {{
178+
#[cfg(feature = "std")]
179+
let spans = $crate::util::logger::TLS_LOGGER.with(|cell| {
180+
cell.borrow().iter().map(|span| *span).collect()
181+
});
182+
$logger.log($crate::util::logger::Record::new($lvl,
183+
#[cfg(feature = "std")]
184+
spans,
185+
None, None, format_args!($($arg)+), module_path!(), file!(), line!(), None
186+
));
187+
}};
180188
}
181189

182190
/// Log at the `ERROR` level.

0 commit comments

Comments
 (0)