Skip to content

Commit a5925a2

Browse files
committed
Add TLS logger
1 parent de384ff commit a5925a2

File tree

4 files changed

+197
-6
lines changed

4 files changed

+197
-6
lines changed

lightning-invoice/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -460,6 +460,7 @@ impl From<Network> for Currency {
460460
Network::Testnet4 => Currency::BitcoinTestnet,
461461
Network::Regtest => Currency::Regtest,
462462
Network::Signet => Currency::Signet,
463+
_ => todo!(),
463464
}
464465
}
465466
}

lightning-macros/src/lib.rs

Lines changed: 45 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,46 @@ 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+
let name = name_attr.unwrap();
426+
427+
let mut meth = if let Ok(parsed) = parse::<syn::ItemFn>(meth) {
428+
parsed
429+
} else {
430+
return (quote! {
431+
compile_error!("log_scope can only be set on methods")
432+
})
433+
.into();
434+
};
435+
436+
let init_stmt1 = quote! {
437+
let _logger_wrapper = crate::util::logger::LoggerWrapper(&self.logger);
438+
};
439+
let init_stmt2 = quote! {
440+
let _logging_context = crate::util::logger::LoggerScope::new(&_logger_wrapper, #name);
441+
};
442+
443+
meth.block.stmts.insert(0, parse(init_stmt2.into()).unwrap());
444+
meth.block.stmts.insert(0, parse(init_stmt1.into()).unwrap());
445+
quote! { #meth }.into()
446+
}

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+
135+
/// The names of the surrounding spans, if any.
136+
pub spans: Vec<&'static str>,
133137
}
134138

135139
impl<$($args)?> Record<$($args)?> {
@@ -138,7 +142,7 @@ 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, spans: Vec<&'static str>, peer_id: Option<PublicKey>, channel_id: Option<ChannelId>,
142146
args: fmt::Arguments<'a>, module_path: &'static str, file: &'static str, line: u32,
143147
payment_hash: Option<PaymentHash>
144148
) -> Record<$($args)?> {
@@ -154,6 +158,7 @@ impl<$($args)?> Record<$($args)?> {
154158
file,
155159
line,
156160
payment_hash,
161+
spans,
157162
}
158163
}
159164
}
@@ -189,9 +194,22 @@ impl<$($args)?> Display for Record<$($args)?> {
189194

190195
#[cfg(test)]
191196
{
192-
write!(f, " {}", self.args)?;
197+
write!(f, " ")?;
198+
if !self.spans.is_empty() {
199+
write!(f, "[")?;
200+
for (i, span) in self.spans.iter().enumerate() {
201+
if i > 0 {
202+
write!(f, "->")?;
203+
}
204+
write!(f, "{}", span)?;
205+
}
206+
write!(f, "] ")?;
207+
}
208+
209+
write!(f, "{}", self.args)?;
193210

194211
let mut open_bracket_written = false;
212+
195213
if let Some(peer_id) = self.peer_id {
196214
write!(f, " [")?;
197215
open_bracket_written = true;
@@ -384,15 +402,75 @@ impl<T: fmt::Display, I: core::iter::Iterator<Item = T> + Clone> fmt::Display fo
384402
}
385403
}
386404

405+
/// A wrapper to allow getting a dyn Logger from a deref type.
406+
pub struct LoggerWrapper<'a, L: Deref>(pub &'a L)
407+
where
408+
L::Target: Logger;
409+
410+
impl<'a, L: Deref> Logger for LoggerWrapper<'a, L>
411+
where
412+
L::Target: Logger,
413+
{
414+
fn log(&self, record: Record) {
415+
self.0.log(record)
416+
}
417+
}
418+
419+
pub struct Span {
420+
pub logger: &'static dyn Logger,
421+
pub name: &'static str,
422+
}
423+
424+
thread_local! {
425+
pub(crate) static TLS_LOGGER: RefCell<Vec<Span>> = RefCell::new(Vec::new());
426+
}
427+
428+
/// A scope which pushes a logger on a thread-local stack for the duration of the scope.
429+
pub struct LoggerScope<'a> {
430+
_marker: std::marker::PhantomData<&'a ()>,
431+
}
432+
433+
impl<'a> LoggerScope<'a> {
434+
/// Pushes a logger onto the thread-local logger stack.
435+
pub fn new(logger: &'a dyn Logger, span: &'static str) -> Self {
436+
TLS_LOGGER.with(|cell| {
437+
let mut stack = cell.borrow_mut();
438+
439+
let logger_ref_static: &'static dyn Logger =
440+
unsafe { std::mem::transmute::<&'a dyn Logger, &'static dyn Logger>(logger) };
441+
442+
stack.push(Span { logger: logger_ref_static, name: span });
443+
});
444+
445+
LoggerScope { _marker: std::marker::PhantomData }
446+
}
447+
}
448+
449+
impl<'a> Drop for LoggerScope<'a> {
450+
fn drop(&mut self) {
451+
TLS_LOGGER.with(|cell| {
452+
let mut stack = cell.borrow_mut();
453+
stack.pop();
454+
});
455+
}
456+
}
457+
387458
#[cfg(test)]
388459
mod tests {
389460
use crate::ln::types::ChannelId;
390461
use crate::sync::Arc;
391462
use crate::types::payment::PaymentHash;
392-
use crate::util::logger::{Level, Logger, WithContext};
463+
use crate::util::logger::{Level, Logger, LoggerScope, WithContext};
393464
use crate::util::test_utils::TestLogger;
394465
use bitcoin::secp256k1::{PublicKey, Secp256k1, SecretKey};
395466

467+
#[test]
468+
fn logger_scope() {
469+
let logger = TestLogger::new();
470+
let _scope = LoggerScope::new(&logger, "test_logger_scope");
471+
log_info_ctx!("Info");
472+
}
473+
396474
#[test]
397475
fn test_level_show() {
398476
assert_eq!("INFO", Level::Info.to_string());

lightning/src/util/macro_logger.rs

Lines changed: 70 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -174,8 +174,28 @@ 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))
177+
($logger: expr, $lvl:expr, $($arg:tt)+) => {{
178+
let mut spans = Vec::new();
179+
$crate::util::logger::TLS_LOGGER.with(|cell| {
180+
spans = cell.borrow().iter().map(|span| span.name).collect()
181+
});
182+
$logger.log($crate::util::logger::Record::new($lvl, spans, None, None, format_args!($($arg)+), module_path!(), file!(), line!(), None));
183+
}};
184+
}
185+
186+
/// Create a new Record and log it. You probably don't want to use this macro directly,
187+
/// but it needs to be exported so `log_trace` etc can use it in external crates.
188+
#[doc(hidden)]
189+
#[macro_export]
190+
macro_rules! log_given_level_ctx {
191+
($lvl:expr, $($arg:tt)+) => (
192+
$crate::util::logger::TLS_LOGGER.with(|cell| {
193+
let borrowed = cell.borrow();
194+
let spans = borrowed.iter().map(|span| span.name).collect();
195+
if let Some(span) = borrowed.last() {
196+
span.logger.log($crate::util::logger::Record::new($lvl, spans, None, None, format_args!($($arg)+), module_path!(), file!(), line!(), None))
197+
}
198+
})
179199
);
180200
}
181201

@@ -187,6 +207,14 @@ macro_rules! log_error {
187207
)
188208
}
189209

210+
// Log at the `ERROR` level using the thread-local logger.
211+
#[macro_export]
212+
macro_rules! log_error_ctx {
213+
($($arg:tt)+) => {
214+
$crate::log_given_level_ctx!($crate::util::logger::Level::Error, $($arg)*);
215+
};
216+
}
217+
190218
/// Log at the `WARN` level.
191219
#[macro_export]
192220
macro_rules! log_warn {
@@ -195,6 +223,14 @@ macro_rules! log_warn {
195223
)
196224
}
197225

226+
// Log at the `WARN` level using the thread-local logger.
227+
#[macro_export]
228+
macro_rules! log_warn_ctx {
229+
($($arg:tt)+) => {
230+
$crate::log_given_level_ctx!($crate::util::logger::Level::Warn, $($arg)*);
231+
};
232+
}
233+
198234
/// Log at the `INFO` level.
199235
#[macro_export]
200236
macro_rules! log_info {
@@ -203,6 +239,14 @@ macro_rules! log_info {
203239
)
204240
}
205241

242+
/// Log at the `INFO` level using the thread-local logger.
243+
#[macro_export]
244+
macro_rules! log_info_ctx {
245+
($($arg:tt)+) => {
246+
$crate::log_given_level_ctx!($crate::util::logger::Level::Info, $($arg)*);
247+
};
248+
}
249+
206250
/// Log at the `DEBUG` level.
207251
#[macro_export]
208252
macro_rules! log_debug {
@@ -211,6 +255,14 @@ macro_rules! log_debug {
211255
)
212256
}
213257

258+
// Log at the `DEBUG` level using the thread-local logger.
259+
#[macro_export]
260+
macro_rules! log_debug_ctx {
261+
($($arg:tt)+) => {
262+
$crate::log_given_level_ctx!($crate::util::logger::Level::Debug, $($arg)*);
263+
};
264+
}
265+
214266
/// Log at the `TRACE` level.
215267
#[macro_export]
216268
macro_rules! log_trace {
@@ -219,10 +271,26 @@ macro_rules! log_trace {
219271
)
220272
}
221273

274+
// Log at the `TRACE` level using the thread-local logger.
275+
#[macro_export]
276+
macro_rules! log_trace_ctx {
277+
($($arg:tt)+) => {
278+
$crate::log_given_level_ctx!($crate::util::logger::Level::Trace, $($arg)*);
279+
};
280+
}
281+
222282
/// Log at the `GOSSIP` level.
223283
#[macro_export]
224284
macro_rules! log_gossip {
225285
($logger: expr, $($arg:tt)*) => (
226286
$crate::log_given_level!($logger, $crate::util::logger::Level::Gossip, $($arg)*);
227287
)
228288
}
289+
290+
// Log at the `GOSSIP` level using the thread-local logger.
291+
#[macro_export]
292+
macro_rules! log_gossip_ctx {
293+
($($arg:tt)+) => {
294+
$crate::log_given_level_ctx!($crate::util::logger::Level::Gossip, $($arg)*);
295+
};
296+
}

0 commit comments

Comments
 (0)