') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ', 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ', 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ', 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); })(); Add configurable logging system by enigbe · Pull Request #407 · lightningdevkit/ldk-node · GitHub
Skip to content

Add configurable logging system - #407

Merged
tnull merged 5 commits into
lightningdevkit:mainfrom
enigbe:2024-11-configurable-logging-system
Jan 30, 2025
Merged

Add configurable logging system#407
tnull merged 5 commits into
lightningdevkit:mainfrom
enigbe:2024-11-configurable-logging-system

Conversation

@enigbe

Copy link
Copy Markdown
Contributor

Overview

This PR introduces a flexible logging system for LDK Node by implementing a LogWriter interface that supports writing logs to different destinations.

What this PR does

  • Introduces a LogWriter interface, allowing Writer variants to handle log output destinations. The supported Writer variants can now:
    • Write logs to the filesystem,
    • Forward to a log implementer,
    • Relay logs to a custom logger.
  • Exposes LogWriter to bindings.
  • Test logging to different destinations with:
    • In-memory log logger,
    • In-memory LogWriter logger.

Related Issue(s)

@enigbeenigbe mentioned this pull request Nov 20, 2024
7 tasks
@G8XSU
G8XSU requested a review from tnullNovember 21, 2024 18:37

@tnulltnull left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cool, thank you for looking into this! This already looks pretty good, but I have some comments after the first round of review.

Comment threadbindings/ldk_node.udl Outdated
Comment threadbindings/ldk_node.udl Outdated
Comment threadCargo.toml Outdated
Comment threadsrc/builder.rs Outdated
Comment threadsrc/logger.rs Outdated
Comment threadtests/common/mod.rs Outdated
}

/// Simple in-memory mock `log` logger for tests.
#[derive(Debug)]

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this Debug?

@enigbeenigbeDec 17, 2024

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NodeBuilder is Debug, and it fields an optional LogWriterConfig. The config object has to be Debug and custom log writer variant needs to implement it for this reason.

Comment threadtests/common/mod.rs Outdated
Comment threadtests/integration_tests_rust.rs Outdated
Comment threadtests/integration_tests_rust.rs Outdated
@tnull

tnull commented Dec 9, 2024

Copy link
Copy Markdown
Collaborator

@enigbe Is there any update on this? Please let me know if you're hitting any blockers.

This seems to need a minor rebase by now.

@enigbe

Copy link
Copy Markdown
ContributorAuthor

No blockers on this. I'll be pushing updates later today.

@enigbe
enigbeforce-pushed the 2024-11-configurable-logging-system branch from 1d57cab to d8eb0e1CompareDecember 11, 2024 12:18
@enigbe
enigbeforce-pushed the 2024-11-configurable-logging-system branch 2 times, most recently from 7fa2928 to 283fe85CompareDecember 16, 2024 00:32
@tnull

Copy link
Copy Markdown
Collaborator

@enigbe Please let me know if/when this is ready for the next round of review!

@enigbe
enigbeforce-pushed the 2024-11-configurable-logging-system branch 4 times, most recently from 0660dfa to 6766c60CompareDecember 19, 2024 21:54

@enigbeenigbe left a comment

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@tnull I believe this is ready for another review. I have addressed the majority of the concerns you raised in the first pass.

Regarding your concerns about testing the logging to custom loggers, I agree that the necessary refactor would be extensive and could detract from the purpose of this PR. As such, I plan to address these test-related changes in a follow-up PR.

Additionally, I encountered some challenges testing a custom logger in Kotlin and would greatly appreciate your guidance or suggestions.

@tnulltnull left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did another round of review, already looks pretty good.

I took a quick look at the Kotlin failures, but also couldn't immediately spot what's up, let me know if you want me to have a closer look though.

Btw, you could consider rebasing on #426 (or on main after it lands) which generally fixes pre-existing CI failures.

Comment threadbindings/ldk_node.udl Outdated
Comment threadsrc/config.rs Outdated
Comment threadsrc/builder.rs Outdated
Comment threadsrc/builder.rs Outdated
Comment threadsrc/builder.rs Outdated
Comment threadsrc/logger.rs Outdated
Comment threadsrc/logger.rs Outdated

impl LogWriter for Writer {
fn log(&self, record: LogRecord) {
let log = format!(

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, so format! allocates a string on the heap. As we try to avoid allocations (to reduce heap fragmentation where possible), can we a) only create this string when we're sure we need it to log (i.e, after passing the log level filtering), and b) can we avoid it altogether for the CustomWriter case, and possibly even the LogFacadeWriter case? In the latter case we should be able to give the arguments to the respective macros directly, no?

Comment threadsrc/logger.rs Outdated
Comment threadsrc/logger.rs
Comment threadsrc/logger.rs Outdated
@tnull

Copy link
Copy Markdown
Collaborator

@enigbe Sorry, this needs a rebase now that #426 landed (which however also should fix the pre-existing CI failures, finally)., Let me know if this is ready for another round of review!

@enigbe
enigbeforce-pushed the 2024-11-configurable-logging-system branch 2 times, most recently from 322f97f to 3b71223CompareJanuary 21, 2025 10:01

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Feel free to drop this commit and rebase cleanly on main.

@enigbe
enigbeforce-pushed the 2024-11-configurable-logging-system branch 2 times, most recently from f06097b to c43fe47CompareJanuary 27, 2025 10:40

@tnulltnull left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cool, basically LGTM, just a few last-minute nits/and cleanups.

Let's address them quickly and land this soon.

Comment threadsrc/builder.rs Outdated
/// Sets the log file path if the log file needs to live separate from the storage directory path.
pub fn set_log_file_path(&mut self, log_dir_path: String) -> &mut Self {
self.config.log_file_path = Some(log_dir_path);
/// Configures the [`Node`] instance to write logs to the filesystem with an optional

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Let's end the first paragraph after filesystem and give explaining the two arguments a short sentence of their own, something like:

Suggested change
/// Configures the [`Node`] instance to write logs to the filesystem with an optional
/// Configures the [`Node`] instance to write logs to the filesystem.
///
/// If set to `None`, `log_file_path` will default to [`DEFAULT_LOG_FILE_PATH`].
/// If set to `None`, `log_level` will default to [`DEFAULT_LOG_LEVEL`].

Comment threadsrc/builder.rs Outdated
self
}

/// Configures the [`Node`] instance to write logs to the provided custom log writer.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Let's link LogWriter here:

Suggested change
/// Configures the [`Node`] instance to write logs to the provided custom log writer.
/// Configures the [`Node`] instance to write logs to the provided custom [`LogWriter`].

Comment threadsrc/builder.rs Outdated
/// Sets the log file path if logs need to live separate from the storage directory path.
pub fn set_log_file_path(&self, log_file_path: String) {
self.inner.write().unwrap().set_log_file_path(log_file_path);
/// Configures the [`Node`] instance to write logs to the filesystem with an optional

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adjust as above.

Comment threadsrc/builder.rs Outdated
self.inner.write().unwrap().set_log_facade_logger(log_level);
}

/// Configures the [`Node`] instance to write logs to the provided custom log writer.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adjust as above.

Comment threadsrc/lib.rs Outdated

pub use io::utils::generate_entropy_mnemonic;

pub use config::FilesystemLoggerConfig;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As mentioned above, no need to export this anymore.

Comment threadsrc/logger.rs Outdated
use std::path::Path;
use std::sync::Arc;

/// A unit of logging output with Metadata to enable filtering module_path,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Lowercase metadata and tick all the variable names:

Suggested change
/// A unit of logging output with Metadata to enable filtering module_path,
/// A unit of logging output with metadata to enable filtering `module_path`,

Comment threadsrc/logger.rs
pub line: u32,
}

#[cfg(feature = "uniffi")]

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This needs docs (copied from above).

Comment threadsrc/logger.rs
level: Level,
/// Defines the behavior required for writing log records.
///
/// This version is used when the `uniffi` feature is enabled.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's still add the second paragraph from above here before going into the Uniffi-specific differences.

Comment threadtests/integration_tests_rust.rs Outdated
setup_builder!(builder, config);
builder.set_chain_source_esplora(esplora_url.clone(), Some(sync_config));

let log_file_path = format!("{}/{}", config.storage_dir_path, "ldk_node.log");

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's drop this? Or any reason why this test would need a special case? If we want to test the interface works, we could consider adding a test for that which then however should confirm that only the correct types of messages are logged. But let's maybe add that as a follow-up.

* Add flexible log writer interface for multiple destinations
* Implement filesystem writing capability via FilesystemLogger
* Prefix LDK-based objects with 'Ldk' for consistency
* Add configuration options for log file path and log level
@enigbe
enigbeforce-pushed the 2024-11-configurable-logging-system branch 2 times, most recently from 241bfcc to 96522d9CompareJanuary 29, 2025 19:56
@tnulltnull mentioned this pull request Jan 30, 2025
- modify tests to forward logs to mock in-memory
`log` logger
- correct "Forwards" spelling error
* Add support for user-provided custom logger to
write logs to, allowing users to provide any logger
that implements LogWriter
* Add test to cover this use case, implementing Log-
Writer for the mock, in-memory MockLogger.
* Fix setting log's global logger twice.
* Revert the renaming of LogLevel to LdkLevel.
This commit addresses a series of fixes, refactors,
and documentation changes meant to optimize the new
logging system to be better with:
- Improved memory usage by eliminating unnecessary data
allocations and improving log record handling.
- Enhanced logging system with support for foreign
implementations (as tested with Kotlin).
- Improved documentation across board.
- Cleaner refactors and simpler code that improve
clarity.
- Streamlined logging configuration.
- Pre-existing bug fixes.
@enigbe
enigbeforce-pushed the 2024-11-configurable-logging-system branch from 96522d9 to e509cf8CompareJanuary 30, 2025 12:24

@tnulltnull left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM 🎉

Thank you very much!

@tnull
tnull merged commit 83159d0 into lightningdevkit:mainJan 30, 2025
@tnulltnull mentioned this pull request Jan 30, 2025
@amackillop

Copy link
Copy Markdown

Sweet will try this out

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@enigbe@tnull@amackillop