Require WithContext log wrappers on OutboundPayments calls and pass payment hashes - #4342

Merged
valentinewallace merged 4 commits into
lightningdevkit:mainfrom
TheBlueMatt:2026-01-decode-log-payment-hash
Jan 29, 2026
Merged

Require WithContext log wrappers on OutboundPayments calls and pass payment hashes#4342
valentinewallace merged 4 commits into
lightningdevkit:mainfrom
TheBlueMatt:2026-01-decode-log-payment-hash

Conversation

@TheBlueMatt

Copy link
Copy Markdown
Collaborator
In much of LDK we pass around `Logger` objects both to avoid having
to `Clone` `Logger` `Deref`s (soon to only be `Logger`s) and to
allow us to set context with a wrapper such that any log calls on
that wrapper get additional useful metadata in them.
Sadly, when we added a `Logger` type to `OutboundPayments` we broke
the ability to do the second thing - payment information logged
directly or indirectly via logic in the `OutboundPayments` has no
context making log-searching rather challenging.
Here we move to instead using the automated `add_logging`
proc-macro to require that `OutboundPayment` functions receive a
`WithContext` logger, appropriately setting (especially) the
`payment_hash` as we do so.
Fixes #4307

I believe the first three commits can/should be backported to 0.2. Ideally we'd also backports a variant to 0.1 to fix#4307 there but I'm not really sure its quite worth it to write a whole new version just for 0.1.

@ldk-reviews-bot

ldk-reviews-bot commented Jan 25, 2026

Copy link
Copy Markdown

👋 Thanks for assigning @wpaulino as a reviewer!
I'll wait for their review and will help manage the review process.
Once they submit their review, I'll check if a second reviewer would be helpful.

@TheBlueMatt
TheBlueMattforce-pushed the 2026-01-decode-log-payment-hash branch from 3ca1fbd to ad93b8dCompareJanuary 25, 2026 16:51
@codecov

codecovBot commented Jan 25, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 88.41202% with 27 lines in your changes missing coverage. Please review.
✅ Project coverage is 86.10%. Comparing base (7fe3268) to head (878e632).
⚠️ Report is 8 commits behind head on main.

Files with missing linesPatch %Lines
lightning/src/ln/outbound_payment.rs85.40%12 Missing and 8 partials ⚠️
lightning/src/ln/channelmanager.rs86.53%7 Missing ⚠️
Additional details and impacted files
@@ Coverage Diff @@## main #4342 +/- ##
=========================================
+ Coverage 0 86.10% +86.10% 
=========================================
Files 0 156 +156 Lines 0 102526 +102526 Branches 0 102526 +102526 =========================================
+ Hits 0 88284 +88284 - Misses 0 11747 +11747 - Partials 0 2495 +2495 
FlagCoverage Δ
tests86.10% <88.41%> (?)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Planning to backport to 0.2 in #4344

wpaulino
wpaulino previously approved these changes Jan 26, 2026

@wpaulinowpaulino left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code LGTM though I'm not really a fan of the hidden logger argument.

@ldk-reviews-bot

Copy link
Copy Markdown

👋 The first review has been submitted!

Do you think this PR is ready for a second reviewer? If so, click here to assign a second reviewer.

@ldk-reviews-bot

Copy link
Copy Markdown

✅ Added second reviewer: @valentinewallace

@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

I'm not really a fan of the hidden logger argument.

I could go either way. It seems like an easy way to ensure we always have the logger which will make @joostjager happy, and in this case its fairly straightforward (because we're not trying to automate passing the logger from ChannelManager).

@valentinewallacevalentinewallace left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I'm not really into this juice vs squeeze ratio. I think @joostjager should take a look since it's supposed to make him happy :) If it really does, I guess it's fine.

Mentioned to Matt offline but Claude pointed out that the visit-mutsyn feature allows removing ~all the boilerplate.

@joostjagerjoostjager left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Appreciate the attempt, but I don't think this is the right direction. The macro adds a layer of complexity, and the hidden logger argument that gets silently injected into every self.*() call isn't very intuitive. I agree with @wpaulino and @valentinewallace's hesitation.

My preference would still be to keep exploring something similar to what the tracing crate does with the context. For no-std the developer experience might not be optimal, but I still think that is a reasonable trade-off.

If that is unacceptable, I think we're better off sticking with explicit logger parameters and types. At least the code does what it says.

@TheBlueMatt
TheBlueMattforce-pushed the 2026-01-decode-log-payment-hash branch from 5759a10 to f50c6a6CompareJanuary 27, 2026 20:08
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Alrighty, explicit loggers it is.

@TheBlueMatt
TheBlueMattforce-pushed the 2026-01-decode-log-payment-hash branch 3 times, most recently from 8508cf3 to 5e5c54aCompareJanuary 27, 2026 20:13
wpaulino
wpaulino previously approved these changes Jan 28, 2026
In `ChannelMonitor` logging, we often wrap a logger with
`WithChannelMonitor` to automatically include metadata in our
structured logging. That's great, except having too many logger
wrapping types flying around makes for less compatibility if we
have methods that want to require a wrapped-logger.
Here we change the `WithChannelMonitor` "constructors" to actually
return a `WithContext` instead, making things more consistent.
@TheBlueMatt
TheBlueMattforce-pushed the 2026-01-decode-log-payment-hash branch from 5e5c54a to 91e4354CompareJanuary 28, 2026 14:09
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Had to rebase for a trivial conflict.

@valentinewallacevalentinewallace left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM. I think CI is still sad

Comment threadlightning/src/ln/channelmanager.rs Outdated
Comment threadlightning/src/ln/channelmanager.rs Outdated
// being fully configured. See the docs for `ChannelManagerReadArgs` for more.
match source {
HTLCSource::OutboundRoute { ref path, ref session_priv, ref payment_id, .. } => {
let logger = WithContext::from(&self.logger, None, None, Some(*payment_hash));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

It might make sense to use the first-hop channel_id/peer here, similarly for claims below

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

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

Oh it is done but in the last commit.

Comment threadlightning/src/ln/channelmanager.rs
Comment threadlightning/src/util/logger.rs Outdated
Comment on lines +326 to +328
if self.payment_id.is_some() {
record.payment_id = self.payment_id;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Somewhat pre-existing, but could it be worth checking that we're not overriding an already set field with a different field here?

In much of LDK we pass around `Logger` objects both to avoid having
to `Clone` `Logger` `Deref`s (soon to only be `Logger`s) and to
allow us to set context with a wrapper such that any log calls on
that wrapper get additional useful metadata in them.
Sadly, when we added a `Logger` type to `OutboundPayments` we broke
the ability to do the second thing - payment information logged
directly or indirectly via logic in the `OutboundPayments` has no
context making log-searching rather challenging.
Here we fix this by retunring to passing loggers explicitly to
`OutboundPayments` methods that need them, specifically requiring
`WithContext` wrappers to ensure the callsite sets appropriate
context on the logger.
Fixeslightningdevkit#4307
While `PaymentHash`es are great for searching logs, in the case of
BOLT 12 the hash isn't selected until well into the payment
process. Thus, its important that we allow for filtering by
`PaymentId` as well to ensure payment-related logs are always
reliably searchable.
If a logger is wrapped with `WithContext` which is then wrapped
with another `WithContext` with different values, we want to use
the context information set closest to the code, ie that which
will be set first.
Thus, here, we avoid overriding context that has already been set.
@TheBlueMatt
TheBlueMattforce-pushed the 2026-01-decode-log-payment-hash branch from 91e4354 to 878e632CompareJanuary 28, 2026 22:26
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

The CI failure was a flake, but I added an avoid-overriding check in the context:

$ git diff-tree -U2 91e4354e36 878e632cdb
diff --git a/lightning/src/ln/channelmanager.rs b/lightning/src/ln/channelmanager.rs
index 35e4b9fcc0..1ad1f22f15 100644
--- a/lightning/src/ln/channelmanager.rs+++ b/lightning/src/ln/channelmanager.rs@@ -5803,5 +5803,4 @@ where
fn abandon_payment_with_reason(&self, payment_id: PaymentId, reason: PaymentFailureReason) {
let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(self);
-
self.pending_outbound_payments.abandon_payment(payment_id, reason, &self.pending_events);
}
diff --git a/lightning/src/util/logger.rs b/lightning/src/util/logger.rs
index 2921688f93..0d2eb47fa6 100644
--- a/lightning/src/util/logger.rs+++ b/lightning/src/util/logger.rs@@ -315,14 +315,14 @@ where
{
fn log(&self, mut record: Record) {
- if self.peer_id.is_some() {+ if self.peer_id.is_some() && record.peer_id.is_none() {
record.peer_id = self.peer_id
};
- if self.channel_id.is_some() {+ if self.channel_id.is_some() && record.channel_id.is_none() {
record.channel_id = self.channel_id;
}
- if self.payment_hash.is_some() {+ if self.payment_hash.is_some() && record.payment_hash.is_none() {
record.payment_hash = self.payment_hash;
}
- if self.payment_id.is_some() {+ if self.payment_id.is_some() && record.payment_id.is_none() {
record.payment_id = self.payment_id;
}

@valentinewallace

Copy link
Copy Markdown
Contributor

I added an avoid-overriding check in the context:

$ git diff-tree -U2 91e4354e36 878e632cdb
diff --git a/lightning/src/ln/channelmanager.rs b/lightning/src/ln/channelmanager.rs
index 35e4b9fcc0..1ad1f22f15 100644
--- a/lightning/src/ln/channelmanager.rs+++ b/lightning/src/ln/channelmanager.rs@@ -5803,5 +5803,4 @@ where
fn abandon_payment_with_reason(&self, payment_id: PaymentId, reason: PaymentFailureReason) {
let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(self);
-
self.pending_outbound_payments.abandon_payment(payment_id, reason, &self.pending_events);
}
diff --git a/lightning/src/util/logger.rs b/lightning/src/util/logger.rs
index 2921688f93..0d2eb47fa6 100644
--- a/lightning/src/util/logger.rs+++ b/lightning/src/util/logger.rs@@ -315,14 +315,14 @@ where
{
fn log(&self, mut record: Record) {
- if self.peer_id.is_some() {+ if self.peer_id.is_some() && record.peer_id.is_none() {
record.peer_id = self.peer_id
};
- if self.channel_id.is_some() {+ if self.channel_id.is_some() && record.channel_id.is_none() {
record.channel_id = self.channel_id;
}
- if self.payment_hash.is_some() {+ if self.payment_hash.is_some() && record.payment_hash.is_none() {
record.payment_hash = self.payment_hash;
}
- if self.payment_id.is_some() {+ if self.payment_id.is_some() && record.payment_id.is_none() {
record.payment_id = self.payment_id;
}

Is it a bug if this happens? I was thinking more like adding debug_asserts

@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

I tried that but we do already hit it in a few places. I doesn't really seem like a bug per se - we might be forwarding from a channel, wrap the logger, then go to wrap it again when for the to channel.

@valentinewallacevalentinewallace left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Left a comment regarding missing debug_asserts, but I assume there's a good reason. Would like to land this soon to unblock #4311

@valentinewallace

Copy link
Copy Markdown
Contributor

Pretty trivial now since the proc-macros left, so landing with 1 review

@valentinewallace
valentinewallace merged commit 9df0280 into lightningdevkit:mainJan 29, 2026
20 of 21 checks passed
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Backported in #4344.

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.

decode_onion_failure loggers dont have a payment hash

5 participants

@TheBlueMatt@ldk-reviews-bot@valentinewallace@joostjager@wpaulino
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Require WithContext log wrappers on OutboundPayments calls and pass payment hashes - #4342

Merged
valentinewallace merged 4 commits into
lightningdevkit:mainfrom
TheBlueMatt:2026-01-decode-log-payment-hash
Jan 29, 2026
Merged

Require WithContext log wrappers on OutboundPayments calls and pass payment hashes#4342
valentinewallace merged 4 commits into
lightningdevkit:mainfrom
TheBlueMatt:2026-01-decode-log-payment-hash

Conversation

@TheBlueMatt

Copy link
Copy Markdown
Collaborator
In much of LDK we pass around `Logger` objects both to avoid having
to `Clone` `Logger` `Deref`s (soon to only be `Logger`s) and to
allow us to set context with a wrapper such that any log calls on
that wrapper get additional useful metadata in them.
Sadly, when we added a `Logger` type to `OutboundPayments` we broke
the ability to do the second thing - payment information logged
directly or indirectly via logic in the `OutboundPayments` has no
context making log-searching rather challenging.
Here we move to instead using the automated `add_logging`
proc-macro to require that `OutboundPayment` functions receive a
`WithContext` logger, appropriately setting (especially) the
`payment_hash` as we do so.
Fixes #4307

I believe the first three commits can/should be backported to 0.2. Ideally we'd also backports a variant to 0.1 to fix#4307 there but I'm not really sure its quite worth it to write a whole new version just for 0.1.

@ldk-reviews-bot

ldk-reviews-bot commented Jan 25, 2026

Copy link
Copy Markdown

👋 Thanks for assigning @wpaulino as a reviewer!
I'll wait for their review and will help manage the review process.
Once they submit their review, I'll check if a second reviewer would be helpful.

@TheBlueMatt
TheBlueMattforce-pushed the 2026-01-decode-log-payment-hash branch from 3ca1fbd to ad93b8dCompareJanuary 25, 2026 16:51
@codecov

codecovBot commented Jan 25, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 88.41202% with 27 lines in your changes missing coverage. Please review.
✅ Project coverage is 86.10%. Comparing base (7fe3268) to head (878e632).
⚠️ Report is 8 commits behind head on main.

Files with missing linesPatch %Lines
lightning/src/ln/outbound_payment.rs85.40%12 Missing and 8 partials ⚠️
lightning/src/ln/channelmanager.rs86.53%7 Missing ⚠️
Additional details and impacted files
@@ Coverage Diff @@## main #4342 +/- ##
=========================================
+ Coverage 0 86.10% +86.10% 
=========================================
Files 0 156 +156 Lines 0 102526 +102526 Branches 0 102526 +102526 =========================================
+ Hits 0 88284 +88284 - Misses 0 11747 +11747 - Partials 0 2495 +2495 
FlagCoverage Δ
tests86.10% <88.41%> (?)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Planning to backport to 0.2 in #4344

wpaulino
wpaulino previously approved these changes Jan 26, 2026

@wpaulinowpaulino left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code LGTM though I'm not really a fan of the hidden logger argument.

@ldk-reviews-bot

Copy link
Copy Markdown

👋 The first review has been submitted!

Do you think this PR is ready for a second reviewer? If so, click here to assign a second reviewer.

@ldk-reviews-bot

Copy link
Copy Markdown

✅ Added second reviewer: @valentinewallace

@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

I'm not really a fan of the hidden logger argument.

I could go either way. It seems like an easy way to ensure we always have the logger which will make @joostjager happy, and in this case its fairly straightforward (because we're not trying to automate passing the logger from ChannelManager).

@valentinewallacevalentinewallace left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I'm not really into this juice vs squeeze ratio. I think @joostjager should take a look since it's supposed to make him happy :) If it really does, I guess it's fine.

Mentioned to Matt offline but Claude pointed out that the visit-mutsyn feature allows removing ~all the boilerplate.

@joostjagerjoostjager left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Appreciate the attempt, but I don't think this is the right direction. The macro adds a layer of complexity, and the hidden logger argument that gets silently injected into every self.*() call isn't very intuitive. I agree with @wpaulino and @valentinewallace's hesitation.

My preference would still be to keep exploring something similar to what the tracing crate does with the context. For no-std the developer experience might not be optimal, but I still think that is a reasonable trade-off.

If that is unacceptable, I think we're better off sticking with explicit logger parameters and types. At least the code does what it says.

@TheBlueMatt
TheBlueMattforce-pushed the 2026-01-decode-log-payment-hash branch from 5759a10 to f50c6a6CompareJanuary 27, 2026 20:08
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Alrighty, explicit loggers it is.

@TheBlueMatt
TheBlueMattforce-pushed the 2026-01-decode-log-payment-hash branch 3 times, most recently from 8508cf3 to 5e5c54aCompareJanuary 27, 2026 20:13
wpaulino
wpaulino previously approved these changes Jan 28, 2026
In `ChannelMonitor` logging, we often wrap a logger with
`WithChannelMonitor` to automatically include metadata in our
structured logging. That's great, except having too many logger
wrapping types flying around makes for less compatibility if we
have methods that want to require a wrapped-logger.
Here we change the `WithChannelMonitor` "constructors" to actually
return a `WithContext` instead, making things more consistent.
@TheBlueMatt
TheBlueMattforce-pushed the 2026-01-decode-log-payment-hash branch from 5e5c54a to 91e4354CompareJanuary 28, 2026 14:09
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Had to rebase for a trivial conflict.

@valentinewallacevalentinewallace left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM. I think CI is still sad

Comment threadlightning/src/ln/channelmanager.rs Outdated
Comment threadlightning/src/ln/channelmanager.rs Outdated
// being fully configured. See the docs for `ChannelManagerReadArgs` for more.
match source {
HTLCSource::OutboundRoute { ref path, ref session_priv, ref payment_id, .. } => {
let logger = WithContext::from(&self.logger, None, None, Some(*payment_hash));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

It might make sense to use the first-hop channel_id/peer here, similarly for claims below

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

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

Oh it is done but in the last commit.

Comment threadlightning/src/ln/channelmanager.rs
Comment threadlightning/src/util/logger.rs Outdated
Comment on lines +326 to +328
if self.payment_id.is_some() {
record.payment_id = self.payment_id;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Somewhat pre-existing, but could it be worth checking that we're not overriding an already set field with a different field here?

In much of LDK we pass around `Logger` objects both to avoid having
to `Clone` `Logger` `Deref`s (soon to only be `Logger`s) and to
allow us to set context with a wrapper such that any log calls on
that wrapper get additional useful metadata in them.
Sadly, when we added a `Logger` type to `OutboundPayments` we broke
the ability to do the second thing - payment information logged
directly or indirectly via logic in the `OutboundPayments` has no
context making log-searching rather challenging.
Here we fix this by retunring to passing loggers explicitly to
`OutboundPayments` methods that need them, specifically requiring
`WithContext` wrappers to ensure the callsite sets appropriate
context on the logger.
Fixeslightningdevkit#4307
While `PaymentHash`es are great for searching logs, in the case of
BOLT 12 the hash isn't selected until well into the payment
process. Thus, its important that we allow for filtering by
`PaymentId` as well to ensure payment-related logs are always
reliably searchable.
If a logger is wrapped with `WithContext` which is then wrapped
with another `WithContext` with different values, we want to use
the context information set closest to the code, ie that which
will be set first.
Thus, here, we avoid overriding context that has already been set.
@TheBlueMatt
TheBlueMattforce-pushed the 2026-01-decode-log-payment-hash branch from 91e4354 to 878e632CompareJanuary 28, 2026 22:26
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

The CI failure was a flake, but I added an avoid-overriding check in the context:

$ git diff-tree -U2 91e4354e36 878e632cdb
diff --git a/lightning/src/ln/channelmanager.rs b/lightning/src/ln/channelmanager.rs
index 35e4b9fcc0..1ad1f22f15 100644
--- a/lightning/src/ln/channelmanager.rs+++ b/lightning/src/ln/channelmanager.rs@@ -5803,5 +5803,4 @@ where
fn abandon_payment_with_reason(&self, payment_id: PaymentId, reason: PaymentFailureReason) {
let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(self);
-
self.pending_outbound_payments.abandon_payment(payment_id, reason, &self.pending_events);
}
diff --git a/lightning/src/util/logger.rs b/lightning/src/util/logger.rs
index 2921688f93..0d2eb47fa6 100644
--- a/lightning/src/util/logger.rs+++ b/lightning/src/util/logger.rs@@ -315,14 +315,14 @@ where
{
fn log(&self, mut record: Record) {
- if self.peer_id.is_some() {+ if self.peer_id.is_some() && record.peer_id.is_none() {
record.peer_id = self.peer_id
};
- if self.channel_id.is_some() {+ if self.channel_id.is_some() && record.channel_id.is_none() {
record.channel_id = self.channel_id;
}
- if self.payment_hash.is_some() {+ if self.payment_hash.is_some() && record.payment_hash.is_none() {
record.payment_hash = self.payment_hash;
}
- if self.payment_id.is_some() {+ if self.payment_id.is_some() && record.payment_id.is_none() {
record.payment_id = self.payment_id;
}

@valentinewallace

Copy link
Copy Markdown
Contributor

I added an avoid-overriding check in the context:

$ git diff-tree -U2 91e4354e36 878e632cdb
diff --git a/lightning/src/ln/channelmanager.rs b/lightning/src/ln/channelmanager.rs
index 35e4b9fcc0..1ad1f22f15 100644
--- a/lightning/src/ln/channelmanager.rs+++ b/lightning/src/ln/channelmanager.rs@@ -5803,5 +5803,4 @@ where
fn abandon_payment_with_reason(&self, payment_id: PaymentId, reason: PaymentFailureReason) {
let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(self);
-
self.pending_outbound_payments.abandon_payment(payment_id, reason, &self.pending_events);
}
diff --git a/lightning/src/util/logger.rs b/lightning/src/util/logger.rs
index 2921688f93..0d2eb47fa6 100644
--- a/lightning/src/util/logger.rs+++ b/lightning/src/util/logger.rs@@ -315,14 +315,14 @@ where
{
fn log(&self, mut record: Record) {
- if self.peer_id.is_some() {+ if self.peer_id.is_some() && record.peer_id.is_none() {
record.peer_id = self.peer_id
};
- if self.channel_id.is_some() {+ if self.channel_id.is_some() && record.channel_id.is_none() {
record.channel_id = self.channel_id;
}
- if self.payment_hash.is_some() {+ if self.payment_hash.is_some() && record.payment_hash.is_none() {
record.payment_hash = self.payment_hash;
}
- if self.payment_id.is_some() {+ if self.payment_id.is_some() && record.payment_id.is_none() {
record.payment_id = self.payment_id;
}

Is it a bug if this happens? I was thinking more like adding debug_asserts

@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

I tried that but we do already hit it in a few places. I doesn't really seem like a bug per se - we might be forwarding from a channel, wrap the logger, then go to wrap it again when for the to channel.

@valentinewallacevalentinewallace left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Left a comment regarding missing debug_asserts, but I assume there's a good reason. Would like to land this soon to unblock #4311

@valentinewallace

Copy link
Copy Markdown
Contributor

Pretty trivial now since the proc-macros left, so landing with 1 review

@valentinewallace
valentinewallace merged commit 9df0280 into lightningdevkit:mainJan 29, 2026
20 of 21 checks passed
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Backported in #4344.

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.

decode_onion_failure loggers dont have a payment hash

5 participants

@TheBlueMatt@ldk-reviews-bot@valentinewallace@joostjager@wpaulino
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Require WithContext log wrappers on OutboundPayments calls and pass payment hashes - #4342

Merged
valentinewallace merged 4 commits into
lightningdevkit:mainfrom
TheBlueMatt:2026-01-decode-log-payment-hash
Jan 29, 2026
Merged

Require WithContext log wrappers on OutboundPayments calls and pass payment hashes#4342
valentinewallace merged 4 commits into
lightningdevkit:mainfrom
TheBlueMatt:2026-01-decode-log-payment-hash

Conversation

@TheBlueMatt

Copy link
Copy Markdown
Collaborator
In much of LDK we pass around `Logger` objects both to avoid having
to `Clone` `Logger` `Deref`s (soon to only be `Logger`s) and to
allow us to set context with a wrapper such that any log calls on
that wrapper get additional useful metadata in them.
Sadly, when we added a `Logger` type to `OutboundPayments` we broke
the ability to do the second thing - payment information logged
directly or indirectly via logic in the `OutboundPayments` has no
context making log-searching rather challenging.
Here we move to instead using the automated `add_logging`
proc-macro to require that `OutboundPayment` functions receive a
`WithContext` logger, appropriately setting (especially) the
`payment_hash` as we do so.
Fixes #4307

I believe the first three commits can/should be backported to 0.2. Ideally we'd also backports a variant to 0.1 to fix#4307 there but I'm not really sure its quite worth it to write a whole new version just for 0.1.

@ldk-reviews-bot

ldk-reviews-bot commented Jan 25, 2026

Copy link
Copy Markdown

👋 Thanks for assigning @wpaulino as a reviewer!
I'll wait for their review and will help manage the review process.
Once they submit their review, I'll check if a second reviewer would be helpful.

@TheBlueMatt
TheBlueMattforce-pushed the 2026-01-decode-log-payment-hash branch from 3ca1fbd to ad93b8dCompareJanuary 25, 2026 16:51
@codecov

codecovBot commented Jan 25, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 88.41202% with 27 lines in your changes missing coverage. Please review.
✅ Project coverage is 86.10%. Comparing base (7fe3268) to head (878e632).
⚠️ Report is 8 commits behind head on main.

Files with missing linesPatch %Lines
lightning/src/ln/outbound_payment.rs85.40%12 Missing and 8 partials ⚠️
lightning/src/ln/channelmanager.rs86.53%7 Missing ⚠️
Additional details and impacted files
@@ Coverage Diff @@## main #4342 +/- ##
=========================================
+ Coverage 0 86.10% +86.10% 
=========================================
Files 0 156 +156 Lines 0 102526 +102526 Branches 0 102526 +102526 =========================================
+ Hits 0 88284 +88284 - Misses 0 11747 +11747 - Partials 0 2495 +2495 
FlagCoverage Δ
tests86.10% <88.41%> (?)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Planning to backport to 0.2 in #4344

wpaulino
wpaulino previously approved these changes Jan 26, 2026

@wpaulinowpaulino left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code LGTM though I'm not really a fan of the hidden logger argument.

@ldk-reviews-bot

Copy link
Copy Markdown

👋 The first review has been submitted!

Do you think this PR is ready for a second reviewer? If so, click here to assign a second reviewer.

@ldk-reviews-bot

Copy link
Copy Markdown

✅ Added second reviewer: @valentinewallace

@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

I'm not really a fan of the hidden logger argument.

I could go either way. It seems like an easy way to ensure we always have the logger which will make @joostjager happy, and in this case its fairly straightforward (because we're not trying to automate passing the logger from ChannelManager).

@valentinewallacevalentinewallace left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I'm not really into this juice vs squeeze ratio. I think @joostjager should take a look since it's supposed to make him happy :) If it really does, I guess it's fine.

Mentioned to Matt offline but Claude pointed out that the visit-mutsyn feature allows removing ~all the boilerplate.

@joostjagerjoostjager left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Appreciate the attempt, but I don't think this is the right direction. The macro adds a layer of complexity, and the hidden logger argument that gets silently injected into every self.*() call isn't very intuitive. I agree with @wpaulino and @valentinewallace's hesitation.

My preference would still be to keep exploring something similar to what the tracing crate does with the context. For no-std the developer experience might not be optimal, but I still think that is a reasonable trade-off.

If that is unacceptable, I think we're better off sticking with explicit logger parameters and types. At least the code does what it says.

@TheBlueMatt
TheBlueMattforce-pushed the 2026-01-decode-log-payment-hash branch from 5759a10 to f50c6a6CompareJanuary 27, 2026 20:08
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Alrighty, explicit loggers it is.

@TheBlueMatt
TheBlueMattforce-pushed the 2026-01-decode-log-payment-hash branch 3 times, most recently from 8508cf3 to 5e5c54aCompareJanuary 27, 2026 20:13
wpaulino
wpaulino previously approved these changes Jan 28, 2026
In `ChannelMonitor` logging, we often wrap a logger with
`WithChannelMonitor` to automatically include metadata in our
structured logging. That's great, except having too many logger
wrapping types flying around makes for less compatibility if we
have methods that want to require a wrapped-logger.
Here we change the `WithChannelMonitor` "constructors" to actually
return a `WithContext` instead, making things more consistent.
@TheBlueMatt
TheBlueMattforce-pushed the 2026-01-decode-log-payment-hash branch from 5e5c54a to 91e4354CompareJanuary 28, 2026 14:09
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Had to rebase for a trivial conflict.

@valentinewallacevalentinewallace left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM. I think CI is still sad

Comment threadlightning/src/ln/channelmanager.rs Outdated
Comment threadlightning/src/ln/channelmanager.rs Outdated
// being fully configured. See the docs for `ChannelManagerReadArgs` for more.
match source {
HTLCSource::OutboundRoute { ref path, ref session_priv, ref payment_id, .. } => {
let logger = WithContext::from(&self.logger, None, None, Some(*payment_hash));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

It might make sense to use the first-hop channel_id/peer here, similarly for claims below

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

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

Oh it is done but in the last commit.

Comment threadlightning/src/ln/channelmanager.rs
Comment threadlightning/src/util/logger.rs Outdated
Comment on lines +326 to +328
if self.payment_id.is_some() {
record.payment_id = self.payment_id;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Somewhat pre-existing, but could it be worth checking that we're not overriding an already set field with a different field here?

In much of LDK we pass around `Logger` objects both to avoid having
to `Clone` `Logger` `Deref`s (soon to only be `Logger`s) and to
allow us to set context with a wrapper such that any log calls on
that wrapper get additional useful metadata in them.
Sadly, when we added a `Logger` type to `OutboundPayments` we broke
the ability to do the second thing - payment information logged
directly or indirectly via logic in the `OutboundPayments` has no
context making log-searching rather challenging.
Here we fix this by retunring to passing loggers explicitly to
`OutboundPayments` methods that need them, specifically requiring
`WithContext` wrappers to ensure the callsite sets appropriate
context on the logger.
Fixeslightningdevkit#4307
While `PaymentHash`es are great for searching logs, in the case of
BOLT 12 the hash isn't selected until well into the payment
process. Thus, its important that we allow for filtering by
`PaymentId` as well to ensure payment-related logs are always
reliably searchable.
If a logger is wrapped with `WithContext` which is then wrapped
with another `WithContext` with different values, we want to use
the context information set closest to the code, ie that which
will be set first.
Thus, here, we avoid overriding context that has already been set.
@TheBlueMatt
TheBlueMattforce-pushed the 2026-01-decode-log-payment-hash branch from 91e4354 to 878e632CompareJanuary 28, 2026 22:26
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

The CI failure was a flake, but I added an avoid-overriding check in the context:

$ git diff-tree -U2 91e4354e36 878e632cdb
diff --git a/lightning/src/ln/channelmanager.rs b/lightning/src/ln/channelmanager.rs
index 35e4b9fcc0..1ad1f22f15 100644
--- a/lightning/src/ln/channelmanager.rs+++ b/lightning/src/ln/channelmanager.rs@@ -5803,5 +5803,4 @@ where
fn abandon_payment_with_reason(&self, payment_id: PaymentId, reason: PaymentFailureReason) {
let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(self);
-
self.pending_outbound_payments.abandon_payment(payment_id, reason, &self.pending_events);
}
diff --git a/lightning/src/util/logger.rs b/lightning/src/util/logger.rs
index 2921688f93..0d2eb47fa6 100644
--- a/lightning/src/util/logger.rs+++ b/lightning/src/util/logger.rs@@ -315,14 +315,14 @@ where
{
fn log(&self, mut record: Record) {
- if self.peer_id.is_some() {+ if self.peer_id.is_some() && record.peer_id.is_none() {
record.peer_id = self.peer_id
};
- if self.channel_id.is_some() {+ if self.channel_id.is_some() && record.channel_id.is_none() {
record.channel_id = self.channel_id;
}
- if self.payment_hash.is_some() {+ if self.payment_hash.is_some() && record.payment_hash.is_none() {
record.payment_hash = self.payment_hash;
}
- if self.payment_id.is_some() {+ if self.payment_id.is_some() && record.payment_id.is_none() {
record.payment_id = self.payment_id;
}

@valentinewallace

Copy link
Copy Markdown
Contributor

I added an avoid-overriding check in the context:

$ git diff-tree -U2 91e4354e36 878e632cdb
diff --git a/lightning/src/ln/channelmanager.rs b/lightning/src/ln/channelmanager.rs
index 35e4b9fcc0..1ad1f22f15 100644
--- a/lightning/src/ln/channelmanager.rs+++ b/lightning/src/ln/channelmanager.rs@@ -5803,5 +5803,4 @@ where
fn abandon_payment_with_reason(&self, payment_id: PaymentId, reason: PaymentFailureReason) {
let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(self);
-
self.pending_outbound_payments.abandon_payment(payment_id, reason, &self.pending_events);
}
diff --git a/lightning/src/util/logger.rs b/lightning/src/util/logger.rs
index 2921688f93..0d2eb47fa6 100644
--- a/lightning/src/util/logger.rs+++ b/lightning/src/util/logger.rs@@ -315,14 +315,14 @@ where
{
fn log(&self, mut record: Record) {
- if self.peer_id.is_some() {+ if self.peer_id.is_some() && record.peer_id.is_none() {
record.peer_id = self.peer_id
};
- if self.channel_id.is_some() {+ if self.channel_id.is_some() && record.channel_id.is_none() {
record.channel_id = self.channel_id;
}
- if self.payment_hash.is_some() {+ if self.payment_hash.is_some() && record.payment_hash.is_none() {
record.payment_hash = self.payment_hash;
}
- if self.payment_id.is_some() {+ if self.payment_id.is_some() && record.payment_id.is_none() {
record.payment_id = self.payment_id;
}

Is it a bug if this happens? I was thinking more like adding debug_asserts

@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

I tried that but we do already hit it in a few places. I doesn't really seem like a bug per se - we might be forwarding from a channel, wrap the logger, then go to wrap it again when for the to channel.

@valentinewallacevalentinewallace left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Left a comment regarding missing debug_asserts, but I assume there's a good reason. Would like to land this soon to unblock #4311

@valentinewallace

Copy link
Copy Markdown
Contributor

Pretty trivial now since the proc-macros left, so landing with 1 review

@valentinewallace
valentinewallace merged commit 9df0280 into lightningdevkit:mainJan 29, 2026
20 of 21 checks passed
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Backported in #4344.

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.

decode_onion_failure loggers dont have a payment hash

5 participants

@TheBlueMatt@ldk-reviews-bot@valentinewallace@joostjager@wpaulino
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Require WithContext log wrappers on OutboundPayments calls and pass payment hashes - #4342

Merged
valentinewallace merged 4 commits into
lightningdevkit:mainfrom
TheBlueMatt:2026-01-decode-log-payment-hash
Jan 29, 2026
Merged

Require WithContext log wrappers on OutboundPayments calls and pass payment hashes#4342
valentinewallace merged 4 commits into
lightningdevkit:mainfrom
TheBlueMatt:2026-01-decode-log-payment-hash

Conversation

@TheBlueMatt

Copy link
Copy Markdown
Collaborator
In much of LDK we pass around `Logger` objects both to avoid having
to `Clone` `Logger` `Deref`s (soon to only be `Logger`s) and to
allow us to set context with a wrapper such that any log calls on
that wrapper get additional useful metadata in them.
Sadly, when we added a `Logger` type to `OutboundPayments` we broke
the ability to do the second thing - payment information logged
directly or indirectly via logic in the `OutboundPayments` has no
context making log-searching rather challenging.
Here we move to instead using the automated `add_logging`
proc-macro to require that `OutboundPayment` functions receive a
`WithContext` logger, appropriately setting (especially) the
`payment_hash` as we do so.
Fixes #4307

I believe the first three commits can/should be backported to 0.2. Ideally we'd also backports a variant to 0.1 to fix#4307 there but I'm not really sure its quite worth it to write a whole new version just for 0.1.

@ldk-reviews-bot

ldk-reviews-bot commented Jan 25, 2026

Copy link
Copy Markdown

👋 Thanks for assigning @wpaulino as a reviewer!
I'll wait for their review and will help manage the review process.
Once they submit their review, I'll check if a second reviewer would be helpful.

@TheBlueMatt
TheBlueMattforce-pushed the 2026-01-decode-log-payment-hash branch from 3ca1fbd to ad93b8dCompareJanuary 25, 2026 16:51
@codecov

codecovBot commented Jan 25, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 88.41202% with 27 lines in your changes missing coverage. Please review.
✅ Project coverage is 86.10%. Comparing base (7fe3268) to head (878e632).
⚠️ Report is 8 commits behind head on main.

Files with missing linesPatch %Lines
lightning/src/ln/outbound_payment.rs85.40%12 Missing and 8 partials ⚠️
lightning/src/ln/channelmanager.rs86.53%7 Missing ⚠️
Additional details and impacted files
@@ Coverage Diff @@## main #4342 +/- ##
=========================================
+ Coverage 0 86.10% +86.10% 
=========================================
Files 0 156 +156 Lines 0 102526 +102526 Branches 0 102526 +102526 =========================================
+ Hits 0 88284 +88284 - Misses 0 11747 +11747 - Partials 0 2495 +2495 
FlagCoverage Δ
tests86.10% <88.41%> (?)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Planning to backport to 0.2 in #4344

wpaulino
wpaulino previously approved these changes Jan 26, 2026

@wpaulinowpaulino left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code LGTM though I'm not really a fan of the hidden logger argument.

@ldk-reviews-bot

Copy link
Copy Markdown

👋 The first review has been submitted!

Do you think this PR is ready for a second reviewer? If so, click here to assign a second reviewer.

@ldk-reviews-bot

Copy link
Copy Markdown

✅ Added second reviewer: @valentinewallace

@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

I'm not really a fan of the hidden logger argument.

I could go either way. It seems like an easy way to ensure we always have the logger which will make @joostjager happy, and in this case its fairly straightforward (because we're not trying to automate passing the logger from ChannelManager).

@valentinewallacevalentinewallace left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I'm not really into this juice vs squeeze ratio. I think @joostjager should take a look since it's supposed to make him happy :) If it really does, I guess it's fine.

Mentioned to Matt offline but Claude pointed out that the visit-mutsyn feature allows removing ~all the boilerplate.

@joostjagerjoostjager left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Appreciate the attempt, but I don't think this is the right direction. The macro adds a layer of complexity, and the hidden logger argument that gets silently injected into every self.*() call isn't very intuitive. I agree with @wpaulino and @valentinewallace's hesitation.

My preference would still be to keep exploring something similar to what the tracing crate does with the context. For no-std the developer experience might not be optimal, but I still think that is a reasonable trade-off.

If that is unacceptable, I think we're better off sticking with explicit logger parameters and types. At least the code does what it says.

@TheBlueMatt
TheBlueMattforce-pushed the 2026-01-decode-log-payment-hash branch from 5759a10 to f50c6a6CompareJanuary 27, 2026 20:08
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Alrighty, explicit loggers it is.

@TheBlueMatt
TheBlueMattforce-pushed the 2026-01-decode-log-payment-hash branch 3 times, most recently from 8508cf3 to 5e5c54aCompareJanuary 27, 2026 20:13
wpaulino
wpaulino previously approved these changes Jan 28, 2026
In `ChannelMonitor` logging, we often wrap a logger with
`WithChannelMonitor` to automatically include metadata in our
structured logging. That's great, except having too many logger
wrapping types flying around makes for less compatibility if we
have methods that want to require a wrapped-logger.
Here we change the `WithChannelMonitor` "constructors" to actually
return a `WithContext` instead, making things more consistent.
@TheBlueMatt
TheBlueMattforce-pushed the 2026-01-decode-log-payment-hash branch from 5e5c54a to 91e4354CompareJanuary 28, 2026 14:09
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Had to rebase for a trivial conflict.

@valentinewallacevalentinewallace left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM. I think CI is still sad

Comment threadlightning/src/ln/channelmanager.rs Outdated
Comment threadlightning/src/ln/channelmanager.rs Outdated
// being fully configured. See the docs for `ChannelManagerReadArgs` for more.
match source {
HTLCSource::OutboundRoute { ref path, ref session_priv, ref payment_id, .. } => {
let logger = WithContext::from(&self.logger, None, None, Some(*payment_hash));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

It might make sense to use the first-hop channel_id/peer here, similarly for claims below

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

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

Oh it is done but in the last commit.

Comment threadlightning/src/ln/channelmanager.rs
Comment threadlightning/src/util/logger.rs Outdated
Comment on lines +326 to +328
if self.payment_id.is_some() {
record.payment_id = self.payment_id;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Somewhat pre-existing, but could it be worth checking that we're not overriding an already set field with a different field here?

In much of LDK we pass around `Logger` objects both to avoid having
to `Clone` `Logger` `Deref`s (soon to only be `Logger`s) and to
allow us to set context with a wrapper such that any log calls on
that wrapper get additional useful metadata in them.
Sadly, when we added a `Logger` type to `OutboundPayments` we broke
the ability to do the second thing - payment information logged
directly or indirectly via logic in the `OutboundPayments` has no
context making log-searching rather challenging.
Here we fix this by retunring to passing loggers explicitly to
`OutboundPayments` methods that need them, specifically requiring
`WithContext` wrappers to ensure the callsite sets appropriate
context on the logger.
Fixeslightningdevkit#4307
While `PaymentHash`es are great for searching logs, in the case of
BOLT 12 the hash isn't selected until well into the payment
process. Thus, its important that we allow for filtering by
`PaymentId` as well to ensure payment-related logs are always
reliably searchable.
If a logger is wrapped with `WithContext` which is then wrapped
with another `WithContext` with different values, we want to use
the context information set closest to the code, ie that which
will be set first.
Thus, here, we avoid overriding context that has already been set.
@TheBlueMatt
TheBlueMattforce-pushed the 2026-01-decode-log-payment-hash branch from 91e4354 to 878e632CompareJanuary 28, 2026 22:26
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

The CI failure was a flake, but I added an avoid-overriding check in the context:

$ git diff-tree -U2 91e4354e36 878e632cdb
diff --git a/lightning/src/ln/channelmanager.rs b/lightning/src/ln/channelmanager.rs
index 35e4b9fcc0..1ad1f22f15 100644
--- a/lightning/src/ln/channelmanager.rs+++ b/lightning/src/ln/channelmanager.rs@@ -5803,5 +5803,4 @@ where
fn abandon_payment_with_reason(&self, payment_id: PaymentId, reason: PaymentFailureReason) {
let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(self);
-
self.pending_outbound_payments.abandon_payment(payment_id, reason, &self.pending_events);
}
diff --git a/lightning/src/util/logger.rs b/lightning/src/util/logger.rs
index 2921688f93..0d2eb47fa6 100644
--- a/lightning/src/util/logger.rs+++ b/lightning/src/util/logger.rs@@ -315,14 +315,14 @@ where
{
fn log(&self, mut record: Record) {
- if self.peer_id.is_some() {+ if self.peer_id.is_some() && record.peer_id.is_none() {
record.peer_id = self.peer_id
};
- if self.channel_id.is_some() {+ if self.channel_id.is_some() && record.channel_id.is_none() {
record.channel_id = self.channel_id;
}
- if self.payment_hash.is_some() {+ if self.payment_hash.is_some() && record.payment_hash.is_none() {
record.payment_hash = self.payment_hash;
}
- if self.payment_id.is_some() {+ if self.payment_id.is_some() && record.payment_id.is_none() {
record.payment_id = self.payment_id;
}

@valentinewallace

Copy link
Copy Markdown
Contributor

I added an avoid-overriding check in the context:

$ git diff-tree -U2 91e4354e36 878e632cdb
diff --git a/lightning/src/ln/channelmanager.rs b/lightning/src/ln/channelmanager.rs
index 35e4b9fcc0..1ad1f22f15 100644
--- a/lightning/src/ln/channelmanager.rs+++ b/lightning/src/ln/channelmanager.rs@@ -5803,5 +5803,4 @@ where
fn abandon_payment_with_reason(&self, payment_id: PaymentId, reason: PaymentFailureReason) {
let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(self);
-
self.pending_outbound_payments.abandon_payment(payment_id, reason, &self.pending_events);
}
diff --git a/lightning/src/util/logger.rs b/lightning/src/util/logger.rs
index 2921688f93..0d2eb47fa6 100644
--- a/lightning/src/util/logger.rs+++ b/lightning/src/util/logger.rs@@ -315,14 +315,14 @@ where
{
fn log(&self, mut record: Record) {
- if self.peer_id.is_some() {+ if self.peer_id.is_some() && record.peer_id.is_none() {
record.peer_id = self.peer_id
};
- if self.channel_id.is_some() {+ if self.channel_id.is_some() && record.channel_id.is_none() {
record.channel_id = self.channel_id;
}
- if self.payment_hash.is_some() {+ if self.payment_hash.is_some() && record.payment_hash.is_none() {
record.payment_hash = self.payment_hash;
}
- if self.payment_id.is_some() {+ if self.payment_id.is_some() && record.payment_id.is_none() {
record.payment_id = self.payment_id;
}

Is it a bug if this happens? I was thinking more like adding debug_asserts

@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

I tried that but we do already hit it in a few places. I doesn't really seem like a bug per se - we might be forwarding from a channel, wrap the logger, then go to wrap it again when for the to channel.

@valentinewallacevalentinewallace left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Left a comment regarding missing debug_asserts, but I assume there's a good reason. Would like to land this soon to unblock #4311

@valentinewallace

Copy link
Copy Markdown
Contributor

Pretty trivial now since the proc-macros left, so landing with 1 review

@valentinewallace
valentinewallace merged commit 9df0280 into lightningdevkit:mainJan 29, 2026
20 of 21 checks passed
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Backported in #4344.

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.

decode_onion_failure loggers dont have a payment hash

5 participants

@TheBlueMatt@ldk-reviews-bot@valentinewallace@joostjager@wpaulino
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Require WithContext log wrappers on OutboundPayments calls and pass payment hashes - #4342

Merged
valentinewallace merged 4 commits into
lightningdevkit:mainfrom
TheBlueMatt:2026-01-decode-log-payment-hash
Jan 29, 2026
Merged

Require WithContext log wrappers on OutboundPayments calls and pass payment hashes#4342
valentinewallace merged 4 commits into
lightningdevkit:mainfrom
TheBlueMatt:2026-01-decode-log-payment-hash

Conversation

@TheBlueMatt

Copy link
Copy Markdown
Collaborator
In much of LDK we pass around `Logger` objects both to avoid having
to `Clone` `Logger` `Deref`s (soon to only be `Logger`s) and to
allow us to set context with a wrapper such that any log calls on
that wrapper get additional useful metadata in them.
Sadly, when we added a `Logger` type to `OutboundPayments` we broke
the ability to do the second thing - payment information logged
directly or indirectly via logic in the `OutboundPayments` has no
context making log-searching rather challenging.
Here we move to instead using the automated `add_logging`
proc-macro to require that `OutboundPayment` functions receive a
`WithContext` logger, appropriately setting (especially) the
`payment_hash` as we do so.
Fixes #4307

I believe the first three commits can/should be backported to 0.2. Ideally we'd also backports a variant to 0.1 to fix#4307 there but I'm not really sure its quite worth it to write a whole new version just for 0.1.

@ldk-reviews-bot

ldk-reviews-bot commented Jan 25, 2026

Copy link
Copy Markdown

👋 Thanks for assigning @wpaulino as a reviewer!
I'll wait for their review and will help manage the review process.
Once they submit their review, I'll check if a second reviewer would be helpful.

@TheBlueMatt
TheBlueMattforce-pushed the 2026-01-decode-log-payment-hash branch from 3ca1fbd to ad93b8dCompareJanuary 25, 2026 16:51
@codecov

codecovBot commented Jan 25, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 88.41202% with 27 lines in your changes missing coverage. Please review.
✅ Project coverage is 86.10%. Comparing base (7fe3268) to head (878e632).
⚠️ Report is 8 commits behind head on main.

Files with missing linesPatch %Lines
lightning/src/ln/outbound_payment.rs85.40%12 Missing and 8 partials ⚠️
lightning/src/ln/channelmanager.rs86.53%7 Missing ⚠️
Additional details and impacted files
@@ Coverage Diff @@## main #4342 +/- ##
=========================================
+ Coverage 0 86.10% +86.10% 
=========================================
Files 0 156 +156 Lines 0 102526 +102526 Branches 0 102526 +102526 =========================================
+ Hits 0 88284 +88284 - Misses 0 11747 +11747 - Partials 0 2495 +2495 
FlagCoverage Δ
tests86.10% <88.41%> (?)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Planning to backport to 0.2 in #4344

wpaulino
wpaulino previously approved these changes Jan 26, 2026

@wpaulinowpaulino left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code LGTM though I'm not really a fan of the hidden logger argument.

@ldk-reviews-bot

Copy link
Copy Markdown

👋 The first review has been submitted!

Do you think this PR is ready for a second reviewer? If so, click here to assign a second reviewer.

@ldk-reviews-bot

Copy link
Copy Markdown

✅ Added second reviewer: @valentinewallace

@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

I'm not really a fan of the hidden logger argument.

I could go either way. It seems like an easy way to ensure we always have the logger which will make @joostjager happy, and in this case its fairly straightforward (because we're not trying to automate passing the logger from ChannelManager).

@valentinewallacevalentinewallace left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I'm not really into this juice vs squeeze ratio. I think @joostjager should take a look since it's supposed to make him happy :) If it really does, I guess it's fine.

Mentioned to Matt offline but Claude pointed out that the visit-mutsyn feature allows removing ~all the boilerplate.

@joostjagerjoostjager left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Appreciate the attempt, but I don't think this is the right direction. The macro adds a layer of complexity, and the hidden logger argument that gets silently injected into every self.*() call isn't very intuitive. I agree with @wpaulino and @valentinewallace's hesitation.

My preference would still be to keep exploring something similar to what the tracing crate does with the context. For no-std the developer experience might not be optimal, but I still think that is a reasonable trade-off.

If that is unacceptable, I think we're better off sticking with explicit logger parameters and types. At least the code does what it says.

@TheBlueMatt
TheBlueMattforce-pushed the 2026-01-decode-log-payment-hash branch from 5759a10 to f50c6a6CompareJanuary 27, 2026 20:08
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Alrighty, explicit loggers it is.

@TheBlueMatt
TheBlueMattforce-pushed the 2026-01-decode-log-payment-hash branch 3 times, most recently from 8508cf3 to 5e5c54aCompareJanuary 27, 2026 20:13
wpaulino
wpaulino previously approved these changes Jan 28, 2026
In `ChannelMonitor` logging, we often wrap a logger with
`WithChannelMonitor` to automatically include metadata in our
structured logging. That's great, except having too many logger
wrapping types flying around makes for less compatibility if we
have methods that want to require a wrapped-logger.
Here we change the `WithChannelMonitor` "constructors" to actually
return a `WithContext` instead, making things more consistent.
@TheBlueMatt
TheBlueMattforce-pushed the 2026-01-decode-log-payment-hash branch from 5e5c54a to 91e4354CompareJanuary 28, 2026 14:09
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Had to rebase for a trivial conflict.

@valentinewallacevalentinewallace left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM. I think CI is still sad

Comment threadlightning/src/ln/channelmanager.rs Outdated
Comment threadlightning/src/ln/channelmanager.rs Outdated
// being fully configured. See the docs for `ChannelManagerReadArgs` for more.
match source {
HTLCSource::OutboundRoute { ref path, ref session_priv, ref payment_id, .. } => {
let logger = WithContext::from(&self.logger, None, None, Some(*payment_hash));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

It might make sense to use the first-hop channel_id/peer here, similarly for claims below

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

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

Oh it is done but in the last commit.

Comment threadlightning/src/ln/channelmanager.rs
Comment threadlightning/src/util/logger.rs Outdated
Comment on lines +326 to +328
if self.payment_id.is_some() {
record.payment_id = self.payment_id;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Somewhat pre-existing, but could it be worth checking that we're not overriding an already set field with a different field here?

In much of LDK we pass around `Logger` objects both to avoid having
to `Clone` `Logger` `Deref`s (soon to only be `Logger`s) and to
allow us to set context with a wrapper such that any log calls on
that wrapper get additional useful metadata in them.
Sadly, when we added a `Logger` type to `OutboundPayments` we broke
the ability to do the second thing - payment information logged
directly or indirectly via logic in the `OutboundPayments` has no
context making log-searching rather challenging.
Here we fix this by retunring to passing loggers explicitly to
`OutboundPayments` methods that need them, specifically requiring
`WithContext` wrappers to ensure the callsite sets appropriate
context on the logger.
Fixeslightningdevkit#4307
While `PaymentHash`es are great for searching logs, in the case of
BOLT 12 the hash isn't selected until well into the payment
process. Thus, its important that we allow for filtering by
`PaymentId` as well to ensure payment-related logs are always
reliably searchable.
If a logger is wrapped with `WithContext` which is then wrapped
with another `WithContext` with different values, we want to use
the context information set closest to the code, ie that which
will be set first.
Thus, here, we avoid overriding context that has already been set.
@TheBlueMatt
TheBlueMattforce-pushed the 2026-01-decode-log-payment-hash branch from 91e4354 to 878e632CompareJanuary 28, 2026 22:26
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

The CI failure was a flake, but I added an avoid-overriding check in the context:

$ git diff-tree -U2 91e4354e36 878e632cdb
diff --git a/lightning/src/ln/channelmanager.rs b/lightning/src/ln/channelmanager.rs
index 35e4b9fcc0..1ad1f22f15 100644
--- a/lightning/src/ln/channelmanager.rs+++ b/lightning/src/ln/channelmanager.rs@@ -5803,5 +5803,4 @@ where
fn abandon_payment_with_reason(&self, payment_id: PaymentId, reason: PaymentFailureReason) {
let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(self);
-
self.pending_outbound_payments.abandon_payment(payment_id, reason, &self.pending_events);
}
diff --git a/lightning/src/util/logger.rs b/lightning/src/util/logger.rs
index 2921688f93..0d2eb47fa6 100644
--- a/lightning/src/util/logger.rs+++ b/lightning/src/util/logger.rs@@ -315,14 +315,14 @@ where
{
fn log(&self, mut record: Record) {
- if self.peer_id.is_some() {+ if self.peer_id.is_some() && record.peer_id.is_none() {
record.peer_id = self.peer_id
};
- if self.channel_id.is_some() {+ if self.channel_id.is_some() && record.channel_id.is_none() {
record.channel_id = self.channel_id;
}
- if self.payment_hash.is_some() {+ if self.payment_hash.is_some() && record.payment_hash.is_none() {
record.payment_hash = self.payment_hash;
}
- if self.payment_id.is_some() {+ if self.payment_id.is_some() && record.payment_id.is_none() {
record.payment_id = self.payment_id;
}

@valentinewallace

Copy link
Copy Markdown
Contributor

I added an avoid-overriding check in the context:

$ git diff-tree -U2 91e4354e36 878e632cdb
diff --git a/lightning/src/ln/channelmanager.rs b/lightning/src/ln/channelmanager.rs
index 35e4b9fcc0..1ad1f22f15 100644
--- a/lightning/src/ln/channelmanager.rs+++ b/lightning/src/ln/channelmanager.rs@@ -5803,5 +5803,4 @@ where
fn abandon_payment_with_reason(&self, payment_id: PaymentId, reason: PaymentFailureReason) {
let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(self);
-
self.pending_outbound_payments.abandon_payment(payment_id, reason, &self.pending_events);
}
diff --git a/lightning/src/util/logger.rs b/lightning/src/util/logger.rs
index 2921688f93..0d2eb47fa6 100644
--- a/lightning/src/util/logger.rs+++ b/lightning/src/util/logger.rs@@ -315,14 +315,14 @@ where
{
fn log(&self, mut record: Record) {
- if self.peer_id.is_some() {+ if self.peer_id.is_some() && record.peer_id.is_none() {
record.peer_id = self.peer_id
};
- if self.channel_id.is_some() {+ if self.channel_id.is_some() && record.channel_id.is_none() {
record.channel_id = self.channel_id;
}
- if self.payment_hash.is_some() {+ if self.payment_hash.is_some() && record.payment_hash.is_none() {
record.payment_hash = self.payment_hash;
}
- if self.payment_id.is_some() {+ if self.payment_id.is_some() && record.payment_id.is_none() {
record.payment_id = self.payment_id;
}

Is it a bug if this happens? I was thinking more like adding debug_asserts

@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

I tried that but we do already hit it in a few places. I doesn't really seem like a bug per se - we might be forwarding from a channel, wrap the logger, then go to wrap it again when for the to channel.

@valentinewallacevalentinewallace left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Left a comment regarding missing debug_asserts, but I assume there's a good reason. Would like to land this soon to unblock #4311

@valentinewallace

Copy link
Copy Markdown
Contributor

Pretty trivial now since the proc-macros left, so landing with 1 review

@valentinewallace
valentinewallace merged commit 9df0280 into lightningdevkit:mainJan 29, 2026
20 of 21 checks passed
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Backported in #4344.

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.

decode_onion_failure loggers dont have a payment hash

5 participants

@TheBlueMatt@ldk-reviews-bot@valentinewallace@joostjager@wpaulino
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Require WithContext log wrappers on OutboundPayments calls and pass payment hashes - #4342

Merged
valentinewallace merged 4 commits into
lightningdevkit:mainfrom
TheBlueMatt:2026-01-decode-log-payment-hash
Jan 29, 2026
Merged

Require WithContext log wrappers on OutboundPayments calls and pass payment hashes#4342
valentinewallace merged 4 commits into
lightningdevkit:mainfrom
TheBlueMatt:2026-01-decode-log-payment-hash

Conversation

@TheBlueMatt

Copy link
Copy Markdown
Collaborator
In much of LDK we pass around `Logger` objects both to avoid having
to `Clone` `Logger` `Deref`s (soon to only be `Logger`s) and to
allow us to set context with a wrapper such that any log calls on
that wrapper get additional useful metadata in them.
Sadly, when we added a `Logger` type to `OutboundPayments` we broke
the ability to do the second thing - payment information logged
directly or indirectly via logic in the `OutboundPayments` has no
context making log-searching rather challenging.
Here we move to instead using the automated `add_logging`
proc-macro to require that `OutboundPayment` functions receive a
`WithContext` logger, appropriately setting (especially) the
`payment_hash` as we do so.
Fixes #4307

I believe the first three commits can/should be backported to 0.2. Ideally we'd also backports a variant to 0.1 to fix#4307 there but I'm not really sure its quite worth it to write a whole new version just for 0.1.

@ldk-reviews-bot

ldk-reviews-bot commented Jan 25, 2026

Copy link
Copy Markdown

👋 Thanks for assigning @wpaulino as a reviewer!
I'll wait for their review and will help manage the review process.
Once they submit their review, I'll check if a second reviewer would be helpful.

@TheBlueMatt
TheBlueMattforce-pushed the 2026-01-decode-log-payment-hash branch from 3ca1fbd to ad93b8dCompareJanuary 25, 2026 16:51
@codecov

codecovBot commented Jan 25, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 88.41202% with 27 lines in your changes missing coverage. Please review.
✅ Project coverage is 86.10%. Comparing base (7fe3268) to head (878e632).
⚠️ Report is 8 commits behind head on main.

Files with missing linesPatch %Lines
lightning/src/ln/outbound_payment.rs85.40%12 Missing and 8 partials ⚠️
lightning/src/ln/channelmanager.rs86.53%7 Missing ⚠️
Additional details and impacted files
@@ Coverage Diff @@## main #4342 +/- ##
=========================================
+ Coverage 0 86.10% +86.10% 
=========================================
Files 0 156 +156 Lines 0 102526 +102526 Branches 0 102526 +102526 =========================================
+ Hits 0 88284 +88284 - Misses 0 11747 +11747 - Partials 0 2495 +2495 
FlagCoverage Δ
tests86.10% <88.41%> (?)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Planning to backport to 0.2 in #4344

wpaulino
wpaulino previously approved these changes Jan 26, 2026

@wpaulinowpaulino left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code LGTM though I'm not really a fan of the hidden logger argument.

@ldk-reviews-bot

Copy link
Copy Markdown

👋 The first review has been submitted!

Do you think this PR is ready for a second reviewer? If so, click here to assign a second reviewer.

@ldk-reviews-bot

Copy link
Copy Markdown

✅ Added second reviewer: @valentinewallace

@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

I'm not really a fan of the hidden logger argument.

I could go either way. It seems like an easy way to ensure we always have the logger which will make @joostjager happy, and in this case its fairly straightforward (because we're not trying to automate passing the logger from ChannelManager).

@valentinewallacevalentinewallace left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I'm not really into this juice vs squeeze ratio. I think @joostjager should take a look since it's supposed to make him happy :) If it really does, I guess it's fine.

Mentioned to Matt offline but Claude pointed out that the visit-mutsyn feature allows removing ~all the boilerplate.

@joostjagerjoostjager left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Appreciate the attempt, but I don't think this is the right direction. The macro adds a layer of complexity, and the hidden logger argument that gets silently injected into every self.*() call isn't very intuitive. I agree with @wpaulino and @valentinewallace's hesitation.

My preference would still be to keep exploring something similar to what the tracing crate does with the context. For no-std the developer experience might not be optimal, but I still think that is a reasonable trade-off.

If that is unacceptable, I think we're better off sticking with explicit logger parameters and types. At least the code does what it says.

@TheBlueMatt
TheBlueMattforce-pushed the 2026-01-decode-log-payment-hash branch from 5759a10 to f50c6a6CompareJanuary 27, 2026 20:08
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Alrighty, explicit loggers it is.

@TheBlueMatt
TheBlueMattforce-pushed the 2026-01-decode-log-payment-hash branch 3 times, most recently from 8508cf3 to 5e5c54aCompareJanuary 27, 2026 20:13
wpaulino
wpaulino previously approved these changes Jan 28, 2026
In `ChannelMonitor` logging, we often wrap a logger with
`WithChannelMonitor` to automatically include metadata in our
structured logging. That's great, except having too many logger
wrapping types flying around makes for less compatibility if we
have methods that want to require a wrapped-logger.
Here we change the `WithChannelMonitor` "constructors" to actually
return a `WithContext` instead, making things more consistent.
@TheBlueMatt
TheBlueMattforce-pushed the 2026-01-decode-log-payment-hash branch from 5e5c54a to 91e4354CompareJanuary 28, 2026 14:09
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Had to rebase for a trivial conflict.

@valentinewallacevalentinewallace left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM. I think CI is still sad

Comment threadlightning/src/ln/channelmanager.rs Outdated
Comment threadlightning/src/ln/channelmanager.rs Outdated
// being fully configured. See the docs for `ChannelManagerReadArgs` for more.
match source {
HTLCSource::OutboundRoute { ref path, ref session_priv, ref payment_id, .. } => {
let logger = WithContext::from(&self.logger, None, None, Some(*payment_hash));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

It might make sense to use the first-hop channel_id/peer here, similarly for claims below

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

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

Oh it is done but in the last commit.

Comment threadlightning/src/ln/channelmanager.rs
Comment threadlightning/src/util/logger.rs Outdated
Comment on lines +326 to +328
if self.payment_id.is_some() {
record.payment_id = self.payment_id;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Somewhat pre-existing, but could it be worth checking that we're not overriding an already set field with a different field here?

In much of LDK we pass around `Logger` objects both to avoid having
to `Clone` `Logger` `Deref`s (soon to only be `Logger`s) and to
allow us to set context with a wrapper such that any log calls on
that wrapper get additional useful metadata in them.
Sadly, when we added a `Logger` type to `OutboundPayments` we broke
the ability to do the second thing - payment information logged
directly or indirectly via logic in the `OutboundPayments` has no
context making log-searching rather challenging.
Here we fix this by retunring to passing loggers explicitly to
`OutboundPayments` methods that need them, specifically requiring
`WithContext` wrappers to ensure the callsite sets appropriate
context on the logger.
Fixeslightningdevkit#4307
While `PaymentHash`es are great for searching logs, in the case of
BOLT 12 the hash isn't selected until well into the payment
process. Thus, its important that we allow for filtering by
`PaymentId` as well to ensure payment-related logs are always
reliably searchable.
If a logger is wrapped with `WithContext` which is then wrapped
with another `WithContext` with different values, we want to use
the context information set closest to the code, ie that which
will be set first.
Thus, here, we avoid overriding context that has already been set.
@TheBlueMatt
TheBlueMattforce-pushed the 2026-01-decode-log-payment-hash branch from 91e4354 to 878e632CompareJanuary 28, 2026 22:26
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

The CI failure was a flake, but I added an avoid-overriding check in the context:

$ git diff-tree -U2 91e4354e36 878e632cdb
diff --git a/lightning/src/ln/channelmanager.rs b/lightning/src/ln/channelmanager.rs
index 35e4b9fcc0..1ad1f22f15 100644
--- a/lightning/src/ln/channelmanager.rs+++ b/lightning/src/ln/channelmanager.rs@@ -5803,5 +5803,4 @@ where
fn abandon_payment_with_reason(&self, payment_id: PaymentId, reason: PaymentFailureReason) {
let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(self);
-
self.pending_outbound_payments.abandon_payment(payment_id, reason, &self.pending_events);
}
diff --git a/lightning/src/util/logger.rs b/lightning/src/util/logger.rs
index 2921688f93..0d2eb47fa6 100644
--- a/lightning/src/util/logger.rs+++ b/lightning/src/util/logger.rs@@ -315,14 +315,14 @@ where
{
fn log(&self, mut record: Record) {
- if self.peer_id.is_some() {+ if self.peer_id.is_some() && record.peer_id.is_none() {
record.peer_id = self.peer_id
};
- if self.channel_id.is_some() {+ if self.channel_id.is_some() && record.channel_id.is_none() {
record.channel_id = self.channel_id;
}
- if self.payment_hash.is_some() {+ if self.payment_hash.is_some() && record.payment_hash.is_none() {
record.payment_hash = self.payment_hash;
}
- if self.payment_id.is_some() {+ if self.payment_id.is_some() && record.payment_id.is_none() {
record.payment_id = self.payment_id;
}

@valentinewallace

Copy link
Copy Markdown
Contributor

I added an avoid-overriding check in the context:

$ git diff-tree -U2 91e4354e36 878e632cdb
diff --git a/lightning/src/ln/channelmanager.rs b/lightning/src/ln/channelmanager.rs
index 35e4b9fcc0..1ad1f22f15 100644
--- a/lightning/src/ln/channelmanager.rs+++ b/lightning/src/ln/channelmanager.rs@@ -5803,5 +5803,4 @@ where
fn abandon_payment_with_reason(&self, payment_id: PaymentId, reason: PaymentFailureReason) {
let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(self);
-
self.pending_outbound_payments.abandon_payment(payment_id, reason, &self.pending_events);
}
diff --git a/lightning/src/util/logger.rs b/lightning/src/util/logger.rs
index 2921688f93..0d2eb47fa6 100644
--- a/lightning/src/util/logger.rs+++ b/lightning/src/util/logger.rs@@ -315,14 +315,14 @@ where
{
fn log(&self, mut record: Record) {
- if self.peer_id.is_some() {+ if self.peer_id.is_some() && record.peer_id.is_none() {
record.peer_id = self.peer_id
};
- if self.channel_id.is_some() {+ if self.channel_id.is_some() && record.channel_id.is_none() {
record.channel_id = self.channel_id;
}
- if self.payment_hash.is_some() {+ if self.payment_hash.is_some() && record.payment_hash.is_none() {
record.payment_hash = self.payment_hash;
}
- if self.payment_id.is_some() {+ if self.payment_id.is_some() && record.payment_id.is_none() {
record.payment_id = self.payment_id;
}

Is it a bug if this happens? I was thinking more like adding debug_asserts

@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

I tried that but we do already hit it in a few places. I doesn't really seem like a bug per se - we might be forwarding from a channel, wrap the logger, then go to wrap it again when for the to channel.

@valentinewallacevalentinewallace left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Left a comment regarding missing debug_asserts, but I assume there's a good reason. Would like to land this soon to unblock #4311

@valentinewallace

Copy link
Copy Markdown
Contributor

Pretty trivial now since the proc-macros left, so landing with 1 review

@valentinewallace
valentinewallace merged commit 9df0280 into lightningdevkit:mainJan 29, 2026
20 of 21 checks passed
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Backported in #4344.

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.

decode_onion_failure loggers dont have a payment hash

5 participants

@TheBlueMatt@ldk-reviews-bot@valentinewallace@joostjager@wpaulino
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Require WithContext log wrappers on OutboundPayments calls and pass payment hashes - #4342

Merged
valentinewallace merged 4 commits into
lightningdevkit:mainfrom
TheBlueMatt:2026-01-decode-log-payment-hash
Jan 29, 2026
Merged

Require WithContext log wrappers on OutboundPayments calls and pass payment hashes#4342
valentinewallace merged 4 commits into
lightningdevkit:mainfrom
TheBlueMatt:2026-01-decode-log-payment-hash

Conversation

@TheBlueMatt

Copy link
Copy Markdown
Collaborator
In much of LDK we pass around `Logger` objects both to avoid having
to `Clone` `Logger` `Deref`s (soon to only be `Logger`s) and to
allow us to set context with a wrapper such that any log calls on
that wrapper get additional useful metadata in them.
Sadly, when we added a `Logger` type to `OutboundPayments` we broke
the ability to do the second thing - payment information logged
directly or indirectly via logic in the `OutboundPayments` has no
context making log-searching rather challenging.
Here we move to instead using the automated `add_logging`
proc-macro to require that `OutboundPayment` functions receive a
`WithContext` logger, appropriately setting (especially) the
`payment_hash` as we do so.
Fixes #4307

I believe the first three commits can/should be backported to 0.2. Ideally we'd also backports a variant to 0.1 to fix#4307 there but I'm not really sure its quite worth it to write a whole new version just for 0.1.

@ldk-reviews-bot

ldk-reviews-bot commented Jan 25, 2026

Copy link
Copy Markdown

👋 Thanks for assigning @wpaulino as a reviewer!
I'll wait for their review and will help manage the review process.
Once they submit their review, I'll check if a second reviewer would be helpful.

@TheBlueMatt
TheBlueMattforce-pushed the 2026-01-decode-log-payment-hash branch from 3ca1fbd to ad93b8dCompareJanuary 25, 2026 16:51
@codecov

codecovBot commented Jan 25, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 88.41202% with 27 lines in your changes missing coverage. Please review.
✅ Project coverage is 86.10%. Comparing base (7fe3268) to head (878e632).
⚠️ Report is 8 commits behind head on main.

Files with missing linesPatch %Lines
lightning/src/ln/outbound_payment.rs85.40%12 Missing and 8 partials ⚠️
lightning/src/ln/channelmanager.rs86.53%7 Missing ⚠️
Additional details and impacted files
@@ Coverage Diff @@## main #4342 +/- ##
=========================================
+ Coverage 0 86.10% +86.10% 
=========================================
Files 0 156 +156 Lines 0 102526 +102526 Branches 0 102526 +102526 =========================================
+ Hits 0 88284 +88284 - Misses 0 11747 +11747 - Partials 0 2495 +2495 
FlagCoverage Δ
tests86.10% <88.41%> (?)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Planning to backport to 0.2 in #4344

wpaulino
wpaulino previously approved these changes Jan 26, 2026

@wpaulinowpaulino left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code LGTM though I'm not really a fan of the hidden logger argument.

@ldk-reviews-bot

Copy link
Copy Markdown

👋 The first review has been submitted!

Do you think this PR is ready for a second reviewer? If so, click here to assign a second reviewer.

@ldk-reviews-bot

Copy link
Copy Markdown

✅ Added second reviewer: @valentinewallace

@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

I'm not really a fan of the hidden logger argument.

I could go either way. It seems like an easy way to ensure we always have the logger which will make @joostjager happy, and in this case its fairly straightforward (because we're not trying to automate passing the logger from ChannelManager).

@valentinewallacevalentinewallace left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I'm not really into this juice vs squeeze ratio. I think @joostjager should take a look since it's supposed to make him happy :) If it really does, I guess it's fine.

Mentioned to Matt offline but Claude pointed out that the visit-mutsyn feature allows removing ~all the boilerplate.

@joostjagerjoostjager left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Appreciate the attempt, but I don't think this is the right direction. The macro adds a layer of complexity, and the hidden logger argument that gets silently injected into every self.*() call isn't very intuitive. I agree with @wpaulino and @valentinewallace's hesitation.

My preference would still be to keep exploring something similar to what the tracing crate does with the context. For no-std the developer experience might not be optimal, but I still think that is a reasonable trade-off.

If that is unacceptable, I think we're better off sticking with explicit logger parameters and types. At least the code does what it says.

@TheBlueMatt
TheBlueMattforce-pushed the 2026-01-decode-log-payment-hash branch from 5759a10 to f50c6a6CompareJanuary 27, 2026 20:08
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Alrighty, explicit loggers it is.

@TheBlueMatt
TheBlueMattforce-pushed the 2026-01-decode-log-payment-hash branch 3 times, most recently from 8508cf3 to 5e5c54aCompareJanuary 27, 2026 20:13
wpaulino
wpaulino previously approved these changes Jan 28, 2026
In `ChannelMonitor` logging, we often wrap a logger with
`WithChannelMonitor` to automatically include metadata in our
structured logging. That's great, except having too many logger
wrapping types flying around makes for less compatibility if we
have methods that want to require a wrapped-logger.
Here we change the `WithChannelMonitor` "constructors" to actually
return a `WithContext` instead, making things more consistent.
@TheBlueMatt
TheBlueMattforce-pushed the 2026-01-decode-log-payment-hash branch from 5e5c54a to 91e4354CompareJanuary 28, 2026 14:09
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Had to rebase for a trivial conflict.

@valentinewallacevalentinewallace left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM. I think CI is still sad

Comment threadlightning/src/ln/channelmanager.rs Outdated
Comment threadlightning/src/ln/channelmanager.rs Outdated
// being fully configured. See the docs for `ChannelManagerReadArgs` for more.
match source {
HTLCSource::OutboundRoute { ref path, ref session_priv, ref payment_id, .. } => {
let logger = WithContext::from(&self.logger, None, None, Some(*payment_hash));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

It might make sense to use the first-hop channel_id/peer here, similarly for claims below

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

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

Oh it is done but in the last commit.

Comment threadlightning/src/ln/channelmanager.rs
Comment threadlightning/src/util/logger.rs Outdated
Comment on lines +326 to +328
if self.payment_id.is_some() {
record.payment_id = self.payment_id;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Somewhat pre-existing, but could it be worth checking that we're not overriding an already set field with a different field here?

In much of LDK we pass around `Logger` objects both to avoid having
to `Clone` `Logger` `Deref`s (soon to only be `Logger`s) and to
allow us to set context with a wrapper such that any log calls on
that wrapper get additional useful metadata in them.
Sadly, when we added a `Logger` type to `OutboundPayments` we broke
the ability to do the second thing - payment information logged
directly or indirectly via logic in the `OutboundPayments` has no
context making log-searching rather challenging.
Here we fix this by retunring to passing loggers explicitly to
`OutboundPayments` methods that need them, specifically requiring
`WithContext` wrappers to ensure the callsite sets appropriate
context on the logger.
Fixeslightningdevkit#4307
While `PaymentHash`es are great for searching logs, in the case of
BOLT 12 the hash isn't selected until well into the payment
process. Thus, its important that we allow for filtering by
`PaymentId` as well to ensure payment-related logs are always
reliably searchable.
If a logger is wrapped with `WithContext` which is then wrapped
with another `WithContext` with different values, we want to use
the context information set closest to the code, ie that which
will be set first.
Thus, here, we avoid overriding context that has already been set.
@TheBlueMatt
TheBlueMattforce-pushed the 2026-01-decode-log-payment-hash branch from 91e4354 to 878e632CompareJanuary 28, 2026 22:26
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

The CI failure was a flake, but I added an avoid-overriding check in the context:

$ git diff-tree -U2 91e4354e36 878e632cdb
diff --git a/lightning/src/ln/channelmanager.rs b/lightning/src/ln/channelmanager.rs
index 35e4b9fcc0..1ad1f22f15 100644
--- a/lightning/src/ln/channelmanager.rs+++ b/lightning/src/ln/channelmanager.rs@@ -5803,5 +5803,4 @@ where
fn abandon_payment_with_reason(&self, payment_id: PaymentId, reason: PaymentFailureReason) {
let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(self);
-
self.pending_outbound_payments.abandon_payment(payment_id, reason, &self.pending_events);
}
diff --git a/lightning/src/util/logger.rs b/lightning/src/util/logger.rs
index 2921688f93..0d2eb47fa6 100644
--- a/lightning/src/util/logger.rs+++ b/lightning/src/util/logger.rs@@ -315,14 +315,14 @@ where
{
fn log(&self, mut record: Record) {
- if self.peer_id.is_some() {+ if self.peer_id.is_some() && record.peer_id.is_none() {
record.peer_id = self.peer_id
};
- if self.channel_id.is_some() {+ if self.channel_id.is_some() && record.channel_id.is_none() {
record.channel_id = self.channel_id;
}
- if self.payment_hash.is_some() {+ if self.payment_hash.is_some() && record.payment_hash.is_none() {
record.payment_hash = self.payment_hash;
}
- if self.payment_id.is_some() {+ if self.payment_id.is_some() && record.payment_id.is_none() {
record.payment_id = self.payment_id;
}

@valentinewallace

Copy link
Copy Markdown
Contributor

I added an avoid-overriding check in the context:

$ git diff-tree -U2 91e4354e36 878e632cdb
diff --git a/lightning/src/ln/channelmanager.rs b/lightning/src/ln/channelmanager.rs
index 35e4b9fcc0..1ad1f22f15 100644
--- a/lightning/src/ln/channelmanager.rs+++ b/lightning/src/ln/channelmanager.rs@@ -5803,5 +5803,4 @@ where
fn abandon_payment_with_reason(&self, payment_id: PaymentId, reason: PaymentFailureReason) {
let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(self);
-
self.pending_outbound_payments.abandon_payment(payment_id, reason, &self.pending_events);
}
diff --git a/lightning/src/util/logger.rs b/lightning/src/util/logger.rs
index 2921688f93..0d2eb47fa6 100644
--- a/lightning/src/util/logger.rs+++ b/lightning/src/util/logger.rs@@ -315,14 +315,14 @@ where
{
fn log(&self, mut record: Record) {
- if self.peer_id.is_some() {+ if self.peer_id.is_some() && record.peer_id.is_none() {
record.peer_id = self.peer_id
};
- if self.channel_id.is_some() {+ if self.channel_id.is_some() && record.channel_id.is_none() {
record.channel_id = self.channel_id;
}
- if self.payment_hash.is_some() {+ if self.payment_hash.is_some() && record.payment_hash.is_none() {
record.payment_hash = self.payment_hash;
}
- if self.payment_id.is_some() {+ if self.payment_id.is_some() && record.payment_id.is_none() {
record.payment_id = self.payment_id;
}

Is it a bug if this happens? I was thinking more like adding debug_asserts

@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

I tried that but we do already hit it in a few places. I doesn't really seem like a bug per se - we might be forwarding from a channel, wrap the logger, then go to wrap it again when for the to channel.

@valentinewallacevalentinewallace left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Left a comment regarding missing debug_asserts, but I assume there's a good reason. Would like to land this soon to unblock #4311

@valentinewallace

Copy link
Copy Markdown
Contributor

Pretty trivial now since the proc-macros left, so landing with 1 review

@valentinewallace
valentinewallace merged commit 9df0280 into lightningdevkit:mainJan 29, 2026
20 of 21 checks passed
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Backported in #4344.

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.

decode_onion_failure loggers dont have a payment hash

5 participants

@TheBlueMatt@ldk-reviews-bot@valentinewallace@joostjager@wpaulino
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Require WithContext log wrappers on OutboundPayments calls and pass payment hashes - #4342

Merged
valentinewallace merged 4 commits into
lightningdevkit:mainfrom
TheBlueMatt:2026-01-decode-log-payment-hash
Jan 29, 2026
Merged

Require WithContext log wrappers on OutboundPayments calls and pass payment hashes#4342
valentinewallace merged 4 commits into
lightningdevkit:mainfrom
TheBlueMatt:2026-01-decode-log-payment-hash

Conversation

@TheBlueMatt

Copy link
Copy Markdown
Collaborator
In much of LDK we pass around `Logger` objects both to avoid having
to `Clone` `Logger` `Deref`s (soon to only be `Logger`s) and to
allow us to set context with a wrapper such that any log calls on
that wrapper get additional useful metadata in them.
Sadly, when we added a `Logger` type to `OutboundPayments` we broke
the ability to do the second thing - payment information logged
directly or indirectly via logic in the `OutboundPayments` has no
context making log-searching rather challenging.
Here we move to instead using the automated `add_logging`
proc-macro to require that `OutboundPayment` functions receive a
`WithContext` logger, appropriately setting (especially) the
`payment_hash` as we do so.
Fixes #4307

I believe the first three commits can/should be backported to 0.2. Ideally we'd also backports a variant to 0.1 to fix#4307 there but I'm not really sure its quite worth it to write a whole new version just for 0.1.

@ldk-reviews-bot

ldk-reviews-bot commented Jan 25, 2026

Copy link
Copy Markdown

👋 Thanks for assigning @wpaulino as a reviewer!
I'll wait for their review and will help manage the review process.
Once they submit their review, I'll check if a second reviewer would be helpful.

@TheBlueMatt
TheBlueMattforce-pushed the 2026-01-decode-log-payment-hash branch from 3ca1fbd to ad93b8dCompareJanuary 25, 2026 16:51
@codecov

codecovBot commented Jan 25, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 88.41202% with 27 lines in your changes missing coverage. Please review.
✅ Project coverage is 86.10%. Comparing base (7fe3268) to head (878e632).
⚠️ Report is 8 commits behind head on main.

Files with missing linesPatch %Lines
lightning/src/ln/outbound_payment.rs85.40%12 Missing and 8 partials ⚠️
lightning/src/ln/channelmanager.rs86.53%7 Missing ⚠️
Additional details and impacted files
@@ Coverage Diff @@## main #4342 +/- ##
=========================================
+ Coverage 0 86.10% +86.10% 
=========================================
Files 0 156 +156 Lines 0 102526 +102526 Branches 0 102526 +102526 =========================================
+ Hits 0 88284 +88284 - Misses 0 11747 +11747 - Partials 0 2495 +2495 
FlagCoverage Δ
tests86.10% <88.41%> (?)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Planning to backport to 0.2 in #4344

wpaulino
wpaulino previously approved these changes Jan 26, 2026

@wpaulinowpaulino left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code LGTM though I'm not really a fan of the hidden logger argument.

@ldk-reviews-bot

Copy link
Copy Markdown

👋 The first review has been submitted!

Do you think this PR is ready for a second reviewer? If so, click here to assign a second reviewer.

@ldk-reviews-bot

Copy link
Copy Markdown

✅ Added second reviewer: @valentinewallace

@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

I'm not really a fan of the hidden logger argument.

I could go either way. It seems like an easy way to ensure we always have the logger which will make @joostjager happy, and in this case its fairly straightforward (because we're not trying to automate passing the logger from ChannelManager).

@valentinewallacevalentinewallace left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I'm not really into this juice vs squeeze ratio. I think @joostjager should take a look since it's supposed to make him happy :) If it really does, I guess it's fine.

Mentioned to Matt offline but Claude pointed out that the visit-mutsyn feature allows removing ~all the boilerplate.

@joostjagerjoostjager left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Appreciate the attempt, but I don't think this is the right direction. The macro adds a layer of complexity, and the hidden logger argument that gets silently injected into every self.*() call isn't very intuitive. I agree with @wpaulino and @valentinewallace's hesitation.

My preference would still be to keep exploring something similar to what the tracing crate does with the context. For no-std the developer experience might not be optimal, but I still think that is a reasonable trade-off.

If that is unacceptable, I think we're better off sticking with explicit logger parameters and types. At least the code does what it says.

@TheBlueMatt
TheBlueMattforce-pushed the 2026-01-decode-log-payment-hash branch from 5759a10 to f50c6a6CompareJanuary 27, 2026 20:08
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Alrighty, explicit loggers it is.

@TheBlueMatt
TheBlueMattforce-pushed the 2026-01-decode-log-payment-hash branch 3 times, most recently from 8508cf3 to 5e5c54aCompareJanuary 27, 2026 20:13
wpaulino
wpaulino previously approved these changes Jan 28, 2026
In `ChannelMonitor` logging, we often wrap a logger with
`WithChannelMonitor` to automatically include metadata in our
structured logging. That's great, except having too many logger
wrapping types flying around makes for less compatibility if we
have methods that want to require a wrapped-logger.
Here we change the `WithChannelMonitor` "constructors" to actually
return a `WithContext` instead, making things more consistent.
@TheBlueMatt
TheBlueMattforce-pushed the 2026-01-decode-log-payment-hash branch from 5e5c54a to 91e4354CompareJanuary 28, 2026 14:09
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Had to rebase for a trivial conflict.

@valentinewallacevalentinewallace left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM. I think CI is still sad

Comment threadlightning/src/ln/channelmanager.rs Outdated
Comment threadlightning/src/ln/channelmanager.rs Outdated
// being fully configured. See the docs for `ChannelManagerReadArgs` for more.
match source {
HTLCSource::OutboundRoute { ref path, ref session_priv, ref payment_id, .. } => {
let logger = WithContext::from(&self.logger, None, None, Some(*payment_hash));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

It might make sense to use the first-hop channel_id/peer here, similarly for claims below

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

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

Oh it is done but in the last commit.

Comment threadlightning/src/ln/channelmanager.rs
Comment threadlightning/src/util/logger.rs Outdated
Comment on lines +326 to +328
if self.payment_id.is_some() {
record.payment_id = self.payment_id;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Somewhat pre-existing, but could it be worth checking that we're not overriding an already set field with a different field here?

In much of LDK we pass around `Logger` objects both to avoid having
to `Clone` `Logger` `Deref`s (soon to only be `Logger`s) and to
allow us to set context with a wrapper such that any log calls on
that wrapper get additional useful metadata in them.
Sadly, when we added a `Logger` type to `OutboundPayments` we broke
the ability to do the second thing - payment information logged
directly or indirectly via logic in the `OutboundPayments` has no
context making log-searching rather challenging.
Here we fix this by retunring to passing loggers explicitly to
`OutboundPayments` methods that need them, specifically requiring
`WithContext` wrappers to ensure the callsite sets appropriate
context on the logger.
Fixeslightningdevkit#4307
While `PaymentHash`es are great for searching logs, in the case of
BOLT 12 the hash isn't selected until well into the payment
process. Thus, its important that we allow for filtering by
`PaymentId` as well to ensure payment-related logs are always
reliably searchable.
If a logger is wrapped with `WithContext` which is then wrapped
with another `WithContext` with different values, we want to use
the context information set closest to the code, ie that which
will be set first.
Thus, here, we avoid overriding context that has already been set.
@TheBlueMatt
TheBlueMattforce-pushed the 2026-01-decode-log-payment-hash branch from 91e4354 to 878e632CompareJanuary 28, 2026 22:26
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

The CI failure was a flake, but I added an avoid-overriding check in the context:

$ git diff-tree -U2 91e4354e36 878e632cdb
diff --git a/lightning/src/ln/channelmanager.rs b/lightning/src/ln/channelmanager.rs
index 35e4b9fcc0..1ad1f22f15 100644
--- a/lightning/src/ln/channelmanager.rs+++ b/lightning/src/ln/channelmanager.rs@@ -5803,5 +5803,4 @@ where
fn abandon_payment_with_reason(&self, payment_id: PaymentId, reason: PaymentFailureReason) {
let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(self);
-
self.pending_outbound_payments.abandon_payment(payment_id, reason, &self.pending_events);
}
diff --git a/lightning/src/util/logger.rs b/lightning/src/util/logger.rs
index 2921688f93..0d2eb47fa6 100644
--- a/lightning/src/util/logger.rs+++ b/lightning/src/util/logger.rs@@ -315,14 +315,14 @@ where
{
fn log(&self, mut record: Record) {
- if self.peer_id.is_some() {+ if self.peer_id.is_some() && record.peer_id.is_none() {
record.peer_id = self.peer_id
};
- if self.channel_id.is_some() {+ if self.channel_id.is_some() && record.channel_id.is_none() {
record.channel_id = self.channel_id;
}
- if self.payment_hash.is_some() {+ if self.payment_hash.is_some() && record.payment_hash.is_none() {
record.payment_hash = self.payment_hash;
}
- if self.payment_id.is_some() {+ if self.payment_id.is_some() && record.payment_id.is_none() {
record.payment_id = self.payment_id;
}

@valentinewallace

Copy link
Copy Markdown
Contributor

I added an avoid-overriding check in the context:

$ git diff-tree -U2 91e4354e36 878e632cdb
diff --git a/lightning/src/ln/channelmanager.rs b/lightning/src/ln/channelmanager.rs
index 35e4b9fcc0..1ad1f22f15 100644
--- a/lightning/src/ln/channelmanager.rs+++ b/lightning/src/ln/channelmanager.rs@@ -5803,5 +5803,4 @@ where
fn abandon_payment_with_reason(&self, payment_id: PaymentId, reason: PaymentFailureReason) {
let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(self);
-
self.pending_outbound_payments.abandon_payment(payment_id, reason, &self.pending_events);
}
diff --git a/lightning/src/util/logger.rs b/lightning/src/util/logger.rs
index 2921688f93..0d2eb47fa6 100644
--- a/lightning/src/util/logger.rs+++ b/lightning/src/util/logger.rs@@ -315,14 +315,14 @@ where
{
fn log(&self, mut record: Record) {
- if self.peer_id.is_some() {+ if self.peer_id.is_some() && record.peer_id.is_none() {
record.peer_id = self.peer_id
};
- if self.channel_id.is_some() {+ if self.channel_id.is_some() && record.channel_id.is_none() {
record.channel_id = self.channel_id;
}
- if self.payment_hash.is_some() {+ if self.payment_hash.is_some() && record.payment_hash.is_none() {
record.payment_hash = self.payment_hash;
}
- if self.payment_id.is_some() {+ if self.payment_id.is_some() && record.payment_id.is_none() {
record.payment_id = self.payment_id;
}

Is it a bug if this happens? I was thinking more like adding debug_asserts

@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

I tried that but we do already hit it in a few places. I doesn't really seem like a bug per se - we might be forwarding from a channel, wrap the logger, then go to wrap it again when for the to channel.

@valentinewallacevalentinewallace left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Left a comment regarding missing debug_asserts, but I assume there's a good reason. Would like to land this soon to unblock #4311

@valentinewallace

Copy link
Copy Markdown
Contributor

Pretty trivial now since the proc-macros left, so landing with 1 review

@valentinewallace
valentinewallace merged commit 9df0280 into lightningdevkit:mainJan 29, 2026
20 of 21 checks passed
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Backported in #4344.

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.

decode_onion_failure loggers dont have a payment hash

5 participants

@TheBlueMatt@ldk-reviews-bot@valentinewallace@joostjager@wpaulino