feat(otel): bootstrap OTel SDK with autoexport/autoprop#33
Conversation
Add pkg/utils/otel.go with SetupOTelSDKWithConfig using the autoexport/autoprop packages to eliminate exporter boilerplate. All exporter, endpoint, and propagator config is delegated to standard OTEL_* env vars. Call SetupOTelSDKWithConfig from main with graceful shutdown so spans are exported on exit. Enable OTEL_SERVICE_NAME in Helm chart. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Issue: LFXV2-1735 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Signed-off-by: Trevor Bramwell <tbramwell@linuxfoundation.org>
There was a problem hiding this comment.
This license expression (BSD-3-Clause AND LicenseRef-scancode-google-patent-license-golang) is the standard Go patent grant that accompanies all Google-contributed Go dependencies. It is not a code issue — it requires the license policy allowlist to be updated to permit the LicenseRef-scancode-google-patent-license-golang identifier. Leaving this thread open for the policy team to action.
There was a problem hiding this comment.
Pull request overview
This PR adds first-class OpenTelemetry SDK bootstrapping to the voting service using autoexport and autoprop, so existing HTTP/log instrumentation can produce/export telemetry by registering SDK providers at startup.
Changes:
- Add
pkg/utils/otel.goto initialize trace/metric/log providers and propagators via standardOTEL_*environment variables. - Add
pkg/utils/otel_test.goto validate env-driven config, sampler behavior, and SDK init/shutdown with exporters disabled. - Initialize the OTel SDK from
cmd/voting-api/main.goand set a defaultOTEL_SERVICE_NAMEin the Helm chart values.
Reviewed changes
Copilot reviewed 5 out of 6 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| pkg/utils/otel.go | New OTel SDK bootstrap utility (resource, sampler, providers, shutdown). |
| pkg/utils/otel_test.go | New tests covering env config, sampler parsing, and init/shutdown behavior. |
| cmd/voting-api/main.go | Wires OTel SDK initialization and graceful shutdown into service startup. |
| charts/lfx-v2-voting-service/values.yaml | Sets default OTEL_SERVICE_NAME and documents that other OTel env is overridden in ArgoCD. |
| go.mod | Adds required OpenTelemetry dependencies (autoexport/autoprop + SDK/log/metric). |
| go.sum | Adds dependency checksums for the newly introduced modules. |
Comments suppressed due to low confidence (3)
pkg/utils/otel_test.go:36
- This test uses os.Setenv/os.Unsetenv instead of t.Setenv, which means environment restoration depends on the defer and can still leak when the test fails early or subtests are added. Prefer t.Setenv (and t.Cleanup for any additional restoration) for safer, race-free env handling.
_ = os.Setenv("OTEL_SERVICE_NAME", "test-service")
_ = os.Setenv("OTEL_SERVICE_VERSION", "1.2.3")
defer func() {
_ = os.Unsetenv("OTEL_SERVICE_NAME")
_ = os.Unsetenv("OTEL_SERVICE_VERSION")
}()
pkg/utils/otel_test.go:180
- The subtest setup unsets env vars with os.Unsetenv (no automatic restoration), which can leak state across other tests in this package when the runner has OTEL_* variables set. Prefer setting explicit empty values via t.Setenv (or saving/restoring with t.Cleanup) instead of globally unsetting.
t.Run(tt.sampler+"_"+tt.arg, func(t *testing.T) {
_ = os.Unsetenv("OTEL_TRACES_SAMPLER")
_ = os.Unsetenv("OTEL_TRACES_SAMPLER_ARG")
if tt.sampler != "" {
t.Setenv("OTEL_TRACES_SAMPLER", tt.sampler)
}
if tt.arg != "" {
t.Setenv("OTEL_TRACES_SAMPLER_ARG", tt.arg)
}
pkg/utils/otel_test.go:216
- This test unsets OTEL_SERVICE_* via os.Unsetenv without restoring prior values, which can leak into subsequent tests in this package. Prefer t.Setenv with empty values (or save/restore with t.Cleanup) to keep test environment isolation.
t.Setenv("OTEL_TRACES_EXPORTER", "none")
t.Setenv("OTEL_METRICS_EXPORTER", "none")
t.Setenv("OTEL_LOGS_EXPORTER", "none")
_ = os.Unsetenv("OTEL_SERVICE_NAME")
_ = os.Unsetenv("OTEL_SERVICE_VERSION")
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- Conditionally add service.version attribute only when non-empty - Remove hard-coded 1s batch timeout; use SDK default (5s) - Replace os.Unsetenv/os.Setenv in tests with t.Setenv - Use strings.Contains for sampler Description() assertion 🤖 Generated with [Claude Code](https://claude.com/claude-code) Issue: LFXV2-1735 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Signed-off-by: Trevor Bramwell <tbramwell@linuxfoundation.org>
Review feedback addressed (commit
|
| Thread | Change |
|---|---|
Empty service.version in newResource |
service.version attribute is now only added when cfg.ServiceVersion != "" |
| Hard-coded 1s batch timeout | Removed trace.WithBatchTimeout(time.Second); SDK default (5s) used instead. "time" import removed. |
os.Unsetenv without restore in tests |
All os.Unsetenv/os.Setenv+defer patterns replaced with t.Setenv throughout otel_test.go. "os" import removed. |
Brittle sampler Description() assertion |
Replaced exact string match with strings.Contains(s.Description(), "TraceIDRatioBased") |
The license compliance thread (thread 1) has been responded to but left open — it requires a policy allowlist update for LicenseRef-scancode-google-patent-license-golang, which is the standard Go patent grant and not a code issue.
Review feedback addressedAll 4 copilot review threads have been resolved in commit
The license compliance thread ( |
Address review comments from copilot[bot]:
- pkg/utils/otel.go: guard service.name attribute on non-empty value
- pkg/utils/otel.go: fix ServiceVersion doc comment re: default
- pkg/utils/otel.go: loosen OTEL_*_EXPORTER doc to reference autoexport
- cmd/voting-api/main.go: fix comment ("Command-line" → "Environment variable")
Resolves 4 review threads.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Issue: LFXV2-1735
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: Trevor Bramwell <tbramwell@linuxfoundation.org>
Second-round review follow-upAddressed all 4 actionable copilot[bot] threads from the second review pass (commit
The license-compliance thread ( |
Address review comments from copilot-pull-request-reviewer: - pkg/utils/otel.go: default sampler to parentbased_always_on per the OpenTelemetry spec; handle always_on explicitly in switch - go.mod: upgrade otelhttp v0.65.0 → v0.68.0 to align all contrib/* deps to the same release line Resolves 2 review threads. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Issue: LFXV2-1735 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Signed-off-by: Trevor Bramwell <tbramwell@linuxfoundation.org>
Third-round review follow-upAddressed 2 new copilot[bot] threads (commit
The license-compliance thread ( |
Summary
pkg/utils/otel.go— bootstraps OpenTelemetry SDK usingautoexport/autoproppackages, eliminating manual exporter boilerplatepkg/utils/otel_test.go— tests for config, sampler, and SDK init (usesOTEL_*_EXPORTER=noneso no external deps required)SetupOTelSDKWithConfigfrommain.goafterInitStructureLogConfig(), with graceful 25s shutdown timeoutOTEL_SERVICE_NAMEincharts/lfx-v2-voting-service/values.yaml; all other exporter/endpoint/sampler config delegated to ArgoCD overrides via standardOTEL_*env varsThe service already had
otelhttp.NewHandlerandslogotel.OtelHandlerwired, but no SDK initialization — so no TracerProvider was registered and no spans were exported. This fixes that.Design decisions
OTEL_*env vars control everythingOTEL_SERVICE_NAMEin chart defaults — endpoint, protocol, sampler set in ArgoCD production overrides, keeping chart safe for local dev (no exporter configured = no-op)Test plan
make build— passesmake test(pkg/utils) — all OTel tests pass;OTEL_*_EXPORTER=noneused so no external deps neededTestEventProcessor_Start— confirmed present onmainbefore this change, unrelatedCloses LFXV2-1735
🤖 Generated with Claude Code