adopt the tracing log facade, engine crates only (review 004 item 7's family-level decision, ADR-025): the family precedent checked and unanimous (alkcall/alktunnels/alksocks/alktty all on tracing = "0.1" facade-only, tracing-subscriber env-filter a dev-dep where present, silent by default); user sign-off taken in-session (2026-10-11, adopt-tracing option chosen) and recorded in the ADR's status; core stays dep- and emit-free (the explicit core-dep answer — the payload serde deps remain the sole core deps, the ADR states the ADR-020-§4 reading explicitly); all 19 library-path eprintln! sites migrated to tracing::warn!/error! with message text + the alkstore: prefix + the secret-free discipline unchanged (17 at review time, +2 from the review's own inline tick/lease restructures — re-grepped, sqlite 10 / pg 9, zero remainder, tests' harness-skip prints untouched); levels: warn = retried/degraded ops, error = unrecoverable loss (watcher death, discarded pooled clients, panic-path rollback failures); the four substrate-lineage files ride PROVENANCE delta D-41 (message-identical macro swap); deployment.md gains the diagnostics-posture section (operator recipe RUST_LOG=warn/error at the consuming binary; listener application_name diagnosability joins it), ADR index + design-decisions table carry the 025 row, CHANGELOG's 0.1.0 body states the facade; pg-fix-forwarder-diagnosability's output form now pinned (warn on failed reconnect connects with attempt/backoff context, error on the loop-exit poll error); gates green: build, workspace tests server-less, clippy -D warnings, fmt, doc gate zero warnings, fuzz corpus replay 6/6; task -> completed
This commit is contained in:
1 parent
ce5da852ca
commit
a9b6cf0c86
19 files changed
+332
-31
No files matched your search
@@ -0,0 +1,183 @@
|
||||
# ADR-025: The log facade — adopt `tracing`, engine crates only
|
||||
|
||||
## Status
|
||||
|
||||
Accepted (2026-10-11, Phase 1 — the family-level posture decision
|
||||
that `docs/reviews/004-pre-release-general-review.md` §"Findings
|
||||
recorded, not fixed" item 7 explicitly recorded rather than made: the
|
||||
review's item 7 named the posture gap (no facade, 17 library-path
|
||||
`eprintln!` sites carrying the diagnostics) and left the
|
||||
`log`/`tracing`/`eprintln!` call as a family-level decision. The
|
||||
class-1 amend-in-place window ([ADR-017](017-contract-versioning.md)
|
||||
§2) is still open — no versioned artifact exists — so the adoption
|
||||
lands pre-release, before the consumer task (the pg forwarder
|
||||
diagnosability fix) needs the output form. **User sign-off recorded
|
||||
2026-10-11: adopting `tracing`, this option chosen by the user in
|
||||
session** — the sign-off is part of this record per the deciding
|
||||
task's acceptance criterion.)
|
||||
|
||||
## Context
|
||||
|
||||
The workspace ships four published crates with diagnostics but no log
|
||||
facade: the engine crates emit operator-visible failures through
|
||||
`eprintln!` — unconditional, un-filterable, un-routable stderr writes
|
||||
whose verbosity the consumer cannot turn down and whose presence the
|
||||
consumer didn't necessarily ask for. The review verified all sites
|
||||
secret-free (no DSN/password material ever printed) and recorded the
|
||||
posture question rather than deciding it.
|
||||
|
||||
Three forces frame the decision:
|
||||
|
||||
1. **The family precedent is unanimous.** All four family repos were
|
||||
checked this session (2026-10-11): alkcall, alktunnels, alksocks,
|
||||
and alktty each depend on `tracing = "0.1"` in their library
|
||||
crates, emit through `tracing::{error,warn,info,debug}` macros,
|
||||
and carry no subscriber in `[dependencies]` — where
|
||||
`tracing-subscriber` (env-filter) appears at all (alktunnels,
|
||||
alksocks) it is a **`[dev-dependencies]`** entry, so the
|
||||
integration tests can mount a sink and assert on emissions; the
|
||||
subscriber wiring in production is the consuming binary's/app's
|
||||
business. Nobody in the family uses the `log` facade.
|
||||
2. **Core's dependency posture constrains the shape.** Core
|
||||
([ADR-001](001-crate-split.md) split;
|
||||
[ADR-020](020-enqueue-opt-semantics-and-bridges.md) §4's
|
||||
dependency posture, serde/serde_json being the sole core deps)
|
||||
must not grow a facade dependency — a dep on `alkstore` is a
|
||||
contract-posture change and buys nothing: core has **zero**
|
||||
`eprintln!` sites (re-grepped) — core emits nothing and needs to
|
||||
emit nothing; the diagnostics live in the engines. The adoption
|
||||
is therefore *engines-only*: lightest shape, contract untouched.
|
||||
*(Amendment note: ADR-020 §4's text is about payload encoding and
|
||||
says nothing about logging; the constraint applied here is
|
||||
ADR-020 §4's dependency line read with
|
||||
[core-contract.md](../core-contract.md) — the payload serde deps
|
||||
are the sole core deps. This ADR states the reading explicitly so
|
||||
the log decision doesn't silently stretch an encoding ADR beyond
|
||||
its text.)*
|
||||
3. **Published-crate consumer expectations.** A facade that emits
|
||||
nothing until the consumer installs a subscriber is the
|
||||
consumer-respectful posture — silent by default, opt-in filtering
|
||||
(`RUST_LOG`-style env-filter at the wiring site), structured
|
||||
fields, zero output cost when compiled without a subscriber. The
|
||||
`eprintln!` posture forces stderr noise on every consumer.
|
||||
`tracing`'s no-subscriber overhead is a cheap disabled-event call
|
||||
— invisible in the seam-latency budgets
|
||||
([ADR-007](007-transactional-seam.md)'s measured postures).
|
||||
|
||||
The deciding task also named the disposition question: what happens
|
||||
to the existing `eprintln!` sites under whichever option lands.
|
||||
|
||||
## Decision
|
||||
|
||||
**Adopt `tracing` as the log facade, in the engine crates only.**
|
||||
Core keeps its dependency posture untouched — no facade dep on
|
||||
`alkstore`, ever (the engines-only shape is the contract-safe one;
|
||||
moving diagnostics *into* core would be a separate decision with its
|
||||
own record anyway).
|
||||
|
||||
- **Dependency shape (mirrors the family, verified above):** each
|
||||
engine crate that emits (`alkstore-sqlite`,
|
||||
`alkstore-postgres`) gains `tracing = "0.1"` in
|
||||
`[dependencies]`; `tracing-subscriber` (env-filter) appears only
|
||||
under `[dev-dependencies]`, where a test needs to assert on
|
||||
emitted events. No subscriber is wired inside any library; the
|
||||
consuming binary/app owns that wiring (the family's uniform
|
||||
posture).
|
||||
- **Core: silent.** Zero `tracing` (or `log`) dependency; zero
|
||||
emission sites; the contract's failure reporting stays the typed
|
||||
error taxonomy ([core-contract.md](../core-contract.md)). This is
|
||||
the explicit answer to the core-dep question the deciding task
|
||||
required.
|
||||
- **Semantics of an event without a subscriber:** dropped — no
|
||||
output, no noise. *Silent by default, loud on opt-in* is the
|
||||
facade's standing guarantee and the operator-facing property this
|
||||
ADR pins.
|
||||
- **The secret-free rule is the posture of record:** no DSN,
|
||||
password, token, connection-credential material, or
|
||||
consumer-table content in any engine event — the review's verified
|
||||
property promoted to standing rule any new emission site inherits.
|
||||
Diagnostics name mechanisms, error classes, backoff/attempt state,
|
||||
and engine-owned identifiers (channel names, queue names, listener
|
||||
`application_name`-class labels), never credentials.
|
||||
- **Site disposition: migrate all.** The review counted 17
|
||||
library-path `eprintln!` sites (verified secret-free); the re-grep
|
||||
at decision time finds **19** — sqlite 10, pg 9 — the delta
|
||||
arriving from the review's own inline fixes re-structuring the
|
||||
tick/lease failure arms. All 19 migrate from `eprintln!` to the
|
||||
`tracing` facade in this adoption (levels: `warn` for
|
||||
retried/degraded operations and failed diagnostics paths, `error`
|
||||
for unrecoverable-loss conditions such as watcher death or the
|
||||
discarded poll error); message text and secret-free discipline
|
||||
carry over verbatim-class. No site is kept in `eprintln!` and none
|
||||
is deleted: every emission the review verified carries operator
|
||||
value the facade preserves. The pg forwarder's swallowed
|
||||
connect/poll errors (the review's item 7 arms,
|
||||
[the deciding task's consumer](../../tasks/pg-fix-forwarder-diagnosability.md))
|
||||
surface through the same facade — `warn` on the failed reconnect
|
||||
connects with attempt/backoff context, `error` on the loop-exit
|
||||
poll error.
|
||||
- **Levels and fields are engine-local detail**, not contract: the
|
||||
contract's only log-adjacent pin is the error taxonomy's
|
||||
`Display` shape. Event field names may change without an ADR;
|
||||
removing *an engine's diagnostics entirely* would be a posture
|
||||
change (record one).
|
||||
|
||||
## Consequences
|
||||
|
||||
**Positive**
|
||||
|
||||
- The family posture is uniform: one facade across alkcall,
|
||||
alktunnels, alksocks, alktty, and alkstore's engines; operators
|
||||
learn one wiring recipe (env-filter subscriber) per family binary.
|
||||
- Consumers of the published crates get silence by default and
|
||||
opt-in structured diagnostics — the published-crate expectation
|
||||
the deciding task named is answered in the consumer-respectful
|
||||
direction.
|
||||
- The diagnosability consumer task has its output form pinned
|
||||
before it runs; the pg forwarder fixes land as `tracing` events.
|
||||
- The secret-free discipline is now stated law, not inherited
|
||||
hygiene re-verified per review.
|
||||
|
||||
**Negative**
|
||||
|
||||
- Two engine crates gain a dependency (the facade only — no
|
||||
subscriber dep in `[dependencies]`); the dep-graph delta is one
|
||||
crate, family-adopted four times over.
|
||||
- Events are invisible until the consumer wires a subscriber —
|
||||
an operator running a bare integration sees no diagnostics
|
||||
where the old `eprintln!` posture printed unconditionally. The
|
||||
deployment matrix states this honestly (operators wire
|
||||
`RUST_LOG` at the consuming binary; the engine crates' docs say
|
||||
so).
|
||||
- No-op-subscriber sites still cost a disabled-event check per
|
||||
emission — unmeasured here, but the family carries it and the
|
||||
POC-measured budgets are `Event`-free paths.
|
||||
|
||||
## References
|
||||
|
||||
- `docs/reviews/004-pre-release-general-review.md` §"Findings
|
||||
recorded, not fixed" item 7 — the finding (the no-facade posture,
|
||||
the secret-free verification of the `eprintln!` set, the
|
||||
family-level framing); the §"What was checked and clean" security
|
||||
sweep is the secret-free verification of record.
|
||||
- The family precedent (verified in-session, 2026-10-11):
|
||||
`alkcall/Cargo.toml:31`, `alktunnels/Cargo.toml:32`,
|
||||
`alksocks/Cargo.toml:40`, `alktty/Cargo.toml:44` — `tracing`
|
||||
facade-only; `alktunnels`/`alksocks`' `tracing-subscriber`(env-filter)
|
||||
entries are `[dev-dependencies]`.
|
||||
- [ADR-001](001-crate-split.md) (crate split),
|
||||
[ADR-020](020-enqueue-opt-semantics-and-bridges.md) §4 + this ADR
|
||||
§Context point 2's explicit reading — core stays
|
||||
payload-deps-only.
|
||||
- [ADR-017](017-contract-versioning.md) §2 — the class-1 window this
|
||||
adoption rides; no contract text moves (no trait, type, or error
|
||||
variant changes; zero consumer-inventory rows required — nothing
|
||||
here assumes a consumer need, the diagnostics belong to engines
|
||||
and operators).
|
||||
- `docs/architecture/deployment.md` — the operator-facing posture
|
||||
section this ADR's landing adds.
|
||||
- [ADR-004](004-postgres-driver.md) — the pg forwarder's
|
||||
`application_name` diagnosability note, the first
|
||||
operator-diagnosability posture in the family.
|
||||
- The deciding task: `tasks/decide-log-facade-adoption.md` (sign-off
|
||||
recorded in §Status).
|
||||
Reference in new issue
Block a user