fix: conform feature_flag span event to the OTEL spec - #54
Open
Vadman97 wants to merge 1 commit into
Open
Conversation
The OTEL spec (launchdarkly/sdk-specs specs/OTEL-openteletry-integration)
types `feature_flag.result.variationIndex` as an int (req 1.2.2.11) and
`feature_flag.result.reason.inExperiment` as a boolean (req 1.2.2.10).
This hook was emitting both as strings, so consumers matching on the typed
value did not match. The collector filter example in our own OTel docs
matches `attributes["feature_flag.result.reason.inExperiment"] == true`,
which never matched spans produced by this hook. The Go tracing hook
already emits the specified types.
Also adds the `environment_id` option (req 1.2.4) and the resulting
`feature_flag.set.id` attribute (req 1.2.2.9), neither of which was
implemented here. Invalid values are ignored and logged (req 1.2.4.1,
1.2.4.2).
Req 1.2.2.9.2 -- sourcing the environment ID from EvaluationSeriesContext
when it is not configured -- remains unimplemented, because
launchdarkly-server-sdk does not expose an environment ID on either
EvaluationSeriesContext or plugin EnvironmentMetadata.
Note for LaunchDarkly Observability: stored values are unchanged. The
ingest path stringifies span event attributes with fmt.Sprintf("%v"),
so `True` -> "true" and `0` -> "0", byte-identical to the previous output.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Vadman97
marked this pull request as ready for review
August 11, 2026 21:49
kinyoklion
requested changes
Aug 11, 2026
kinyoklion
left a comment
Member
There was a problem hiding this comment.
This needs the update for reading the environment ID from the stream headers. The ability to configure it is a fallback.
The hook should include the environment ID. If the go SDK is currently only doing this, then it needs updated as well and I can check that.
Example of setting it from the .Net SDK: https://github.com/launchdarkly/dotnet-core/blob/98cf36ab260906284bc5276b8c656c399925d14e/pkgs/telemetry/src/TracingHook.cs#L235
Python has the eventsource embedded, so it should be a very small change really.
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.
Summary
Brings the
feature_flagspan event into conformance with the OTEL spec (OTELv1.0.0, ACCEPTED).Three requirements were unmet:
feature_flag.result.variationIndexis an intstr(...)feature_flag.result.reason.inExperimentis a boolean'true'feature_flag.set.idand a configurableenvironmentIdThe Go tracing hook (
go-server-sdk/ldotel) already emits the specified types and supportsset.id, so this also removes a cross-SDK inconsistency.Why the types matter
Consumers matching on the typed value silently never matched. Our own OTel documentation gives this collector filter:
- 'not ((name == "feature_flag" and attributes["feature_flag.result.reason.inExperiment"] == true) or name == "exception")'== truedoes not match the string"true", so that documented example did not work against spans produced by this hook.Changes
variationIndex→ int,inExperiment→TrueHookOptions.environment_id; when set, emitsfeature_flag.set.id(req 1.2.2.9.1.1)environment_id(non-string or empty) is ignored and logged to theldclient.otellogger, equivalent to unset (reqs 1.2.4.1, 1.2.4.2)attributesannotatedDict[str, AttributeValue]so mypy accepts the mixed value typesNot implemented: req 1.2.2.9.2
Sourcing the environment ID from
EvaluationSeriesContextwhen it is not configured is not implementable today.launchdarkly-server-sdk9.14.1 exposes no environment ID on eitherEvaluationSeriesContext(key/context/default_value/methodonly) or pluginEnvironmentMetadata(sdk/sdk_key/application). Go hasseriesContext.EnvironmentID(); Python has no equivalent. Only the config path (1.2.2.9.1) is covered here.Compatibility
This changes the wire type of two attributes. Anyone filtering on the string forms (
"true","0") in a downstream OTel backend will need to match the typed values instead.No impact on LaunchDarkly Observability data. Ingest stringifies span event attributes via
fmt.Sprintf("%v", v)(backend/clickhouse/trace_row.goattributesToMap) intoStringcolumns, soTrue→"true"and0→"0"— byte-identical to the previous output.Testing
make test— 19 passed (12 pre-existing, 7 new)make lint—mypy,isort,pycodestyleall cleanNote for reviewers
Spec req 1.2.3.3 looks internally inconsistent and I did not touch it: it says the variation span must carry
feature_flag.context.key, but its own prose cross-references 1.2.2.5, which definesfeature_flag.context.id. Both this hook and the Go hook setcontext.id. Separately, the public observability docs state that a flag span event is identified by carryingfeature_flag.context.key. Worth reconciling, but that's a spec decision rather than a code fix.🤖 Generated with Claude Code
Note
Overview
Brings
feature_flagspan events into OTEL spec conformance by fixing attribute types and adding optional environment ID support.variationIndexis now emitted as an int (was a string), andinExperimentas a boolean (was'true'). Downstream filters matching on typed values will now work; filters that matched the old string forms need updating.Adds optional
HookOptions.environment_id, which emitsfeature_flag.set.idwhen set to a non-empty string. Invalid values are ignored with a warning on theldclient.otellogger.Reviewed by Cursor Bugbot for commit 4060136. Bugbot is set up for automated code reviews on this repo. Configure here.