Adopt proposal 0121: stable event names on emitted diagnostics - #295
Open
chris-colinsky wants to merge 3 commits into
Open
Adopt proposal 0121: stable event names on emitted diagnostics#295chris-colinsky wants to merge 3 commits into
chris-colinsky wants to merge 3 commits into
Conversation
Proposal 0121. A log record signalling a condition the spec defines carries a stable name in the openarmature namespace, on the OTel LogRecord's event_name field. Three names are emitted: the two section 6 isolation decisions and a section 5.5.15 token-budget breach. The fourth 0121 defines is a MAY for a caller-supplied client, which mode (a) leaves the caller's responsibility rather than inspecting, so nothing emits it. Neither OTel logging handler populates the field. Both map a stdlib record's attributes into the LogRecord's attributes and leave event_name unset, so a name passed through extra= arrives as an attribute and the field stays empty. install_log_bridge now lifts it, reaching past a private surface deliberately and degrading to the attributes-only shape rather than breaking if that surface moves. This is what the names are for: a log_records fixture entry declaring only a severity is satisfied by any record at that level, so silencing a mandated emitter left a fixture green when an unrelated warning fired on the same logger. Our harness had narrowed that by matching the emitting call site, which no other implementation can do. It now matches the name where a fixture declares one. The harness also reads the otel_observer directive carrying the OTel observer's construction knobs, preferring it over the bare case-level key so either spelling resolves the same way across the pin bump.
There was a problem hiding this comment.
🟡 Changes recommended
install_log_bridge’s new _EventNameHandler override can crash rather than degrade if OTel removes/renames the private _translate method, so the fallback logic needs to be made real.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Pull request overview
Implements spec proposal 0121 by introducing stable diagnostic event names for key warnings, ensuring those names propagate to the OTel LogRecord.event_name field, and updating the conformance harness to support the new otel_observer: fixture directive plus event_name-based log matching.
Changes:
- Add a central
observability.diagnosticsmodule that defines stable event-name constants plus helpers for tagging (extra=) and reading names fromlogging.LogRecord. - Tag the relevant diagnostics (Langfuse isolation warnings, token budget exceedance) and update the OTel logs bridge to lift the stdlib
event_nameattribute onto the OTelLogRecord.event_namefield. - Update conformance and unit tests to assert event-name behavior and to parse/precedence-resolve the new
otel_observer:directive (while remaining compatible with the pre-0121 bare keys).
File summaries
| File | Description |
|---|---|
src/openarmature/observability/diagnostics.py |
Introduces stable diagnostic event-name constants and helper functions (diagnostic, event_name_of). |
src/openarmature/observability/otel/logs.py |
Wraps the installed OTel logging handler to lift event_name onto the OTel LogRecord field. |
src/openarmature/observability/otel/observer.py |
Tags the token-budget warning with the stable TOKEN_BUDGET_EXCEEDED event name. |
src/openarmature/observability/langfuse/observer.py |
Tags the two Langfuse isolation-decision warnings with stable event names. |
tests/unit/test_observability_otel.py |
Adds unit coverage for the OTel log bridge “field lift” and the token-budget diagnostic name. |
tests/unit/test_observability_langfuse.py |
Adds unit coverage that the mandated isolation diagnostics carry the correct event names. |
tests/conformance/test_observability.py |
Adds _observer_kwargs_for_case to support the new otel_observer: directive with correct precedence over bare keys. |
tests/conformance/test_observability_langfuse.py |
Updates log_records matching to prefer event_name discrimination when declared, with call-site fallback. |
tests/conformance/test_fixture_parsing.py |
Adds focused tests for directive vs bare-key equivalence/precedence and for event_name discrimination behavior. |
CHANGELOG.md |
Documents proposal 0121 adoption, including the event names and the OTel field-lift rationale. |
Review details
- Files reviewed: 10/10 changed files
- Comments generated: 1
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
The harness branch that reads a fixture's event_name is unreachable at the current pin: no fixture at v0.112.0 declares one, and the six that do arrive at v0.118.0. Mutation confirmed it, forcing the branch never to fire left the whole suite green. Extracted as match_expected_log_record and driven directly, so a regression to level-only matching, which is the looseness 0121 closes, fails now rather than at the pin bump.
The comment claimed the bridge degrades rather than breaks if OTel's private _translate moves. It did not: super()._translate(...) inside an override raises AttributeError once the method is gone, which breaks logging instead of falling back. The subclass is now built only while the method exists, and the upstream handler is used unchanged otherwise, which is the attributes-only shape that predates the lift. A test drives the missing-method path, so the degradation is pinned rather than asserted in prose.
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.
Implements proposal 0121, accepted at spec v0.115.0. Two halves: stable event names on the diagnostics openarmature emits, and the
otel_observerfixture directive.This one closes a gap we reported ourselves. Review item #11 in the v0.17.0 batched review was that
expected.log_recordscould not pin the WARNING it existed to pin: entries declare only a level, so an adapter satisfies them by emitting any warning. We found it by mutation, downgrading the mandated emitter and watching fixture 158 stay green because an unrelated cached-client notice fired on the same logger.The event names
A log record signalling a condition the spec defines now carries a stable name in the
openarmature.namespace. Three are emitted:openarmature.langfuse.payload_suppressedopenarmature.langfuse.shared_provider_acceptedopenarmature.token_budget.exceededThe fourth name 0121 defines,
openarmature.langfuse.supplied_client_shared_provider, has no emitter here. It is a MAY, and §6 mode (a) leaves a caller-supplied client's provider the caller's responsibility rather than something we inspect. The obligation is on the record: where one is emitted it carries its name, and a diagnostic we choose not to emit needs none.Names are stable identifiers and will not be reworded once shipped. A record's human-readable message is free to change independently.
Getting the name onto the OTel field takes work
§7 puts the name on the OTel
LogRecord'sevent_namefield. Neither OTel logging handler populates it. Both map a stdlib record's attributes into the LogRecord'sattributesand leave the field unset, so a name passed throughextra=arrives as an attribute and the field stays empty.install_log_bridgenow installs a handler that lifts it. That reaches past_translate, the handlers' own private surface, deliberately: it is the only seam where a stdlib record becomes an OTel one. It degrades rather than breaks if that surface moves, falling back to the pre-0121 attributes-only shape, and a test pins the field so an OTel upgrade that breaks the lift fails loudly instead of silently reverting us.Worth stating plainly because the distinction is easy to lose: an attributes-only implementation satisfies a fixture reading the stdlib record while leaving an OTel consumer unable to filter on the name at all.
The
otel_observerdirective0121 moves the OTel observer's construction knobs under an
otel_observer:block; 12 fixtures carry the new spelling at v0.118.0 while the pinned copy still uses the bare case-level key. The harness reads both, with the directive winning where both appear, so a half-migrated fixture cannot resolve to the stale value. Without that, a fixture that moved to the directive would silently fall back to the observer default (payloads off) and a case asserting a payload is absent would keep passing for the wrong reason.Retiring our own workaround
Our harness had narrowed the level-only match by discriminating on the emitting call site (
_apply_isolation_policy). That worked but is not portable to another implementation, which is exactly what we told spec. It now matches on the event name where a fixture declares one, and keeps the call-site match only as a fallback for a fixture that does not.Testing
7 mutants, all killed: each of the three event names dropped individually, the bridge's lift removed, the harness ignoring the directive, and the directive losing to the bare key.
One is worth calling out. My first token-budget test logged the record itself rather than driving the emitter, so it passed with the name dropped entirely. The mutant caught it; the test now drives a real over-budget event through the observer.
Ahead of the pin
Spec v0.115.0 is beyond the current v0.112.0 pin, so this ships unit-tested. The
conformance.tomlentry, fixture 158'sevent_nameassertions, and the 12 fixtures carrying the directive all ride the pin bump.