feat(gl-sdk): add LogListener for foreign-binding log capture#705
feat(gl-sdk): add LogListener for foreign-binding log capture#705
Conversation
cdecker
left a comment
There was a problem hiding this comment.
I'm not quite happy with encumbering what is supposed to be the simple interface (naked top-level functions) with things like logging and event subscriber handling. That's more advanced concerns and exactly a problem of naked functions: they have no context other than the global static context they could be configured, hence all possible variants must be expressible in the argument list, which adds complexity back in.
I'd say if you use the simple interface you do not get to play with these advanced features, if you want those you have to use the OOP interface, where we can use builders to incrementally expose details. That is also in tune with the semver logic: naked functions change their entire interface when you add optional arguments, whereas builders can add new setters without breaking existing code.
Can we split out the log handling, which I'd merge right away, and keep the event streaming discussion separate?
1ff607a to
ee2b047
Compare
44c479b to
9f015d6
Compare
Returns a pretty-printed JSON envelope { timestamp, node, sdk } where
the node section serializes getinfo/listpeerchannels/listfunds and the
sdk section carries version + node_state. Failed sub-calls are embedded
as { "error": "..." } instead of failing the dump. Adds serde derive on
the response types so each section is real nested JSON, queryable with
jq. Payment and invoice history are intentionally excluded to avoid
leaking preimages, payment hashes, bolt11 strings, and labels into
support dumps.
Apps install a process-wide LogListener and receive every log message
emitted by gl-sdk and the underlying gl-client library. The bridge
sits on top of the `log` crate facade — gl-sdk's own `tracing` calls
are routed through `tracing`'s `log` feature, and gl-client's direct
`log::*!` calls flow through the same channel.
Public surface (free functions, not encumbering the Node entry
points):
- `set_logger(level, listener)` — install once per process.
- `set_log_level(level)` — adjust the filter at runtime.
- `LogEntry { level, message, target, file, line }` — record
forwarded to the listener.
- `LogLevel { Error, Warn, Info, Debug, Trace }`.
- `LogListener.on_log(entry)` — UniFFI callback interface.
Tests
- Python test_logging.py verifies LogLevel variants, LogEntry shape,
LogListener callback type, and that set_log_level is callable.
- Kotlin LoggingTest.kt installs the listener in @BeforeClass
(handling the "already installed" error gracefully) and asserts
that register_or_recover actually drives log entries through the
listener with non-empty targets.
Event-streaming changes (NodeEventListener trait, set_event_listener
on Node, the encumbering `event_listener` argument on register /
recover / connect / register_or_recover) are intentionally NOT in
this commit. The simple top-level functions stay 3-arg. Event
streaming can be reintroduced via the OOP / builder interface in a
separate PR.
|
Rebased on top of |
|
Very cool to see the log-listener in action 👍 |
[Two callback-based listener APIs for mobile integrators, both
following the Breez SDK shape.
Logging
from both gl-sdk (via tracing's log bridge) and gl-client (111
log::*! callsites flow through automatically).
for easier triage of production issues.
matching the ChannelState/OutputStatus/PaymentStatus convention;
bindings still render as UPPER_SNAKE per uniffi convention.
installs; subsequent calls are silent no-ops. Returns an error only
when another crate has already installed a log logger, replacing the
previous .expect("logger already set") panic.
LogListener (napi4 feature enabled).
Node events
events (InvoicePaid today; room for more).
NodeEventStream is gone.
register / recover / connect / register_or_recover each gain an
event_listener: Option<Box> parameter. The
SDK installs it atomically during node bring-up, so events that fire
during the first round of RPCs are not missed.
Callers who need post-construction flexibility wire a mutable delegate
inside their listener implementation (Flow/LiveData/ObservableObject
pattern). Matches Breez's EventListener shape.
teardown is automatic.
Tests
Python test_logging.py asserts the logging types exist and set_logger
accepts a listener.
Kotlin LoggingTest.kt installs a listener and drives register_or_recover
as a smoke test for cross-language log capture.](feat(gl-sdk): add LogListener for foreign-binding log capture #705 (comment))
feat(gl-sdk): add generate_diagnostic_data for support dumps
Returns a pretty-printed JSON envelope { timestamp, node, sdk } where
the node section serializes getinfo/listpeerchannels/listfunds and the
sdk section carries version + node_state. Failed sub-calls are embedded
as { "error": "..." } instead of failing the dump. Adds serde derive on
the response types so each section is real nested JSON, queryable with
jq. Payment and invoice history are intentionally excluded to avoid
leaking preimages, payment hashes, bolt11 strings, and labels into
support dumps.