feat(core): add a log destination an FFI layer can install (design 050) - #237
Merged
Conversation
…050) The `log_*` facade had exactly one kind of destination: a process-global `tracing::Subscriber`. That left both FFI doors — `weather-station-py` and `weather-station-cpp` — installing the host application's global subscriber on its behalf, which is the trespass those doors otherwise refuse. It also produced a real defect: a `tracing::Layer` has nowhere to put the caller's `void *user_data`, so the C++ header kept a static `SinkHolder`, wrote it non-atomically while the runtime thread read it, and (being last-wins over a first-wins C layer) silently replaced the first caller's sink while returning `false` to say it had not. Add an optional, non-default `log` feature whose only effect is a second arm in each of the four macros, plus a `#[doc(hidden)] __private` re-export of the `log` crate so a facade user needs the feature but not the dependency. A `log::Log` impl is an ordinary value, so the context pointer travels with the callback, and `set_logger` decides first-wins once, in Rust, for every binding. No new public API, no new `unsafe`. With the feature off — the default, and every MCU build — the expansion is what it was. `tracing` stays the default and the recommendation for Rust consumers; both may be on at once. `aimdb-sync` mirrors the feature, which is required rather than cosmetic: the macros are `#[macro_export]`ed, so `#[cfg(feature = "log")]` inside them is resolved against the expanding crate, and `aimdb-core/log` alone would leave its ten call sites unrouted. `aimdb-sync/tests/log_facade.rs` guards that. Two things the design doc understated, now written down where they will be read (crate docs, §4/§5.1 of the design): - `tracing`'s dispatcher has a reentrancy guard and `log` has none, so a destination that logs back into aimdb recurses without bound rather than dropping an event. - "both features on" is not the same as "the process wanted both": with `tracing-subscriber`'s default `tracing-log` feature, a process that installed only a subscriber sees every event twice. Tests cover acceptance criteria 1-7. `log::set_logger` is once per process, so each criterion that installs a logger owns its own integration-test binary. Criterion 4 uses a hand-written counting `tracing::Subscriber` rather than `tracing-subscriber`, since not needing it is the point. Steps 2 and 3 of the sequencing are the FFI doors themselves, which live outside this repository. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QgaHbjCPgkNS6Y34jxqVYH
The crate docs had grown to 106 lines for one optional feature, and the tests were explaining themselves twice. Keep the parts a reader cannot reconstruct — the five destination guarantees, and the fact that the feature must be mirrored by every crate expanding the facade — and leave filtering, duplicate delivery and the direct-`tracing::` connectors to the design doc, which already covers them at length. No behaviour or test change: 118 comment lines out, none of the code touched. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QgaHbjCPgkNS6Y34jxqVYH
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The
log_*facade had exactly one kind of destination: a process-globaltracing::Subscriber. That left both FFI doors —weather-station-pyandweather-station-cpp— installing the host application's global subscriberon its behalf, which is the trespass those doors otherwise refuse. It also
produced a real defect: a
tracing::Layerhas nowhere to put the caller'svoid *user_data, so the C++ header kept a staticSinkHolder, wrote itnon-atomically while the runtime thread read it, and (being last-wins over a
first-wins C layer) silently replaced the first caller's sink while returning
falseto say it had not.Add an optional, non-default
logfeature whose only effect is a second armin each of the four macros, plus a
#[doc(hidden)] __privatere-export of thelogcrate so a facade user needs the feature but not the dependency. Alog::Logimpl is an ordinary value, so the context pointer travels with thecallback, and
set_loggerdecides first-wins once, in Rust, for every binding.No new public API, no new
unsafe. With the feature off — the default, andevery MCU build — the expansion is what it was.
tracingstays the default andthe recommendation for Rust consumers; both may be on at once.
aimdb-syncmirrors the feature, which is required rather than cosmetic: themacros are
#[macro_export]ed, so#[cfg(feature = "log")]inside them isresolved against the expanding crate, and
aimdb-core/logalone would leaveits ten call sites unrouted.
aimdb-sync/tests/log_facade.rsguards that.Two things the design doc understated, now written down where they will be
read (crate docs, §4/§5.1 of the design):
tracing's dispatcher has a reentrancy guard andloghas none, so adestination that logs back into aimdb recurses without bound rather than
dropping an event.
tracing-subscriber's defaulttracing-logfeature, a process thatinstalled only a subscriber sees every event twice.
Tests cover acceptance criteria 1-7.
log::set_loggeris once per process, soeach criterion that installs a logger owns its own integration-test binary.
Criterion 4 uses a hand-written counting
tracing::Subscriberrather thantracing-subscriber, since not needing it is the point.Steps 2 and 3 of the sequencing are the FFI doors themselves, which live
outside this repository.
Co-Authored-By: Claude Opus 5 noreply@anthropic.com
Claude-Session: https://claude.ai/code/session_01QgaHbjCPgkNS6Y34jxqVYH