fix(otel): keep gatekeeper's own OTel diagnostics out of the log export bridge#51
Conversation
…rt bridge configureLogging fanned every slog record out to otelslog.NewHandler unconditionally, because the bridge's Enabled() defers to the OTel logger rather than the configured console level. That meant logOTelError's DEBUG record on a failed export was itself re-enqueued into the same OTel log-export pipeline that had just failed: failed export -> DEBUG diagnostic -> fanned out to the bridge -> re-enqueued -> next export fails carrying it -> another diagnostic, indefinitely while the collector is unreachable. Bounded (one record per export attempt, queue drops on overflow) but a genuine feedback loop. gatekeeper.OTelDiagnosticKey is a new marker attribute; logOTelError tags its record with it, and configureLogging now wraps the otelslog bridge handler in a filter that drops any marked record before forwarding. The console/file handler is unaffected and still sees every record. Fixes #48
📦 Previous Review (superseded by newer review)Claude finished @andybons's task in 1m 56s —— View job Claude's Review
Note: I was unable to run SummaryThis is a small, well-targeted fix. I traced the actual mechanics end-to-end and didn't find bugs:
Minor observations (non-blocking)
No security, race, or correctness issues found. The scope is appropriately narrow for the bug it fixes. |
The bridge filter matched any record carrying OTelDiagnosticKey regardless of value, so a future record setting the key to false would still be kept out of the export bridge. Key off the boolean value so only genuine diagnostics (true) are filtered.
|
Addressed the value-vs-presence observation in |
|
Claude finished @andybons's task in 2m 0s —— View job Reviewing PR #51 (updated)
Claude's ReviewRe-reviewed after commit Note: As with the prior review, I was unable to run Verified: the value-vs-presence fix is correct
record.Attrs(func(a slog.Attr) bool {
if a.Key == OTelDiagnosticKey && a.Value.Kind() == slog.KindBool && a.Value.Bool() {
marked = true
return false
}
return true
})This correctly filters only when the key is present and its value is
Test coverage matches the new behavior
I also confirmed by reading the surrounding code that:
No new issues found. This is a clean, well-targeted refinement of an already-correct fix — no inline comments needed. |
Fixes #48.
Problem
configureLoggingfans every slog record tootelslog.NewHandler("gatekeeper")unconditionally (the bridge'sEnabled()defers to the OTel logger, not the console level). ThelogOTelErrorhandler added in #47 emits a DEBUG diagnostic on every failed OTel export — which is then enqueued into the same failing log-export pipeline: failed export → DEBUG record → re-enqueued → next export fails carrying it → another record, indefinitely while the collector is unreachable. Bounded (one record per attempt) but a real feedback loop.Fix
An
otelDiagnosticFilterwraps only the otelslog bridge handler and drops any record carrying a new exported marker,gatekeeper.OTelDiagnosticKey("otel_diagnostic");logOTelErrortags its Debug call with that key. The console/file handler still receives every record, so diagnostics remain visible locally at DEBUG — they just never re-enter the export pipeline.WithAttrs/WithGroupare overridden so the filter survives slog's handler chaining.Chose the filter-wrapper over a dedicated non-bridged logger because
logOTelErroris registered globally viaotel.SetErrorHandlerand callsslog.Default(); the wrapper localizes bridge-specific behavior where the bridge is built without fighting slog's single-default-logger convention.Tests
TestOTelDiagnosticFilter_KeepsBridgeOutOfLoop(marked record reaches console, not bridge; unmarked reaches both) andTestLogOTelError_MarksAsOTelDiagnostic, both mutation-checked (neutering each flips its test red).go build ./... && go vet ./... && gofmt -l . && go test -race ./...all clean.CHANGELOG v0.17.2 Fixed entry.