1
0
mirror of https://github.com/open-telemetry/opentelemetry-go.git synced 2025-10-31 00:07:40 +02:00

chore: sdk/internal/x - generate x package from shared template (#7495)

Closes #7384 

- Add sdk/internal/gen.go with gotmpl directives to generate x/x.go and
x/x_test.go
- Refactor sdk/internal/x/x.go to generic Feature[T] with multi-key
support and Keys()
- Move Resource feature into sdk/internal/x/features.go
(case-insensitive true enables)
- Add tests: generic feature tests in x_test.go and Resource tests in
features_test.go
- Aligns with sdk/log/internal/x pattern introduced in PR #7389

Internal note: replaces Key() with Keys() for internal Feature API.
Build and lints pass

---------

Co-authored-by: Damien Mathieu <42@dmathieu.com>
This commit is contained in:
Nikhil Mantri
2025-10-16 12:53:17 +05:30
committed by GitHub
parent b37822be24
commit f346decaf6
14 changed files with 115 additions and 262 deletions

8
sdk/internal/gen.go Normal file
View File

@@ -0,0 +1,8 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0
// Package internal provides internal functionality for the sdk package.
package internal // import "go.opentelemetry.io/otel/sdk/internal"
//go:generate gotmpl --body=../../internal/shared/x/x.go.tmpl "--data={ \"pkg\": \"go.opentelemetry.io/otel/sdk\" }" --out=x/x.go
//go:generate gotmpl --body=../../internal/shared/x/x_test.go.tmpl "--data={}" --out=x/x_test.go

View File

@@ -0,0 +1,39 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0
// Package x documents experimental features for [go.opentelemetry.io/otel/sdk].
package x // import "go.opentelemetry.io/otel/sdk/internal/x"
import "strings"
// Resource is an experimental feature flag that defines if resource detectors
// should be included experimental semantic conventions.
//
// To enable this feature set the OTEL_GO_X_RESOURCE environment variable
// to the case-insensitive string value of "true" (i.e. "True" and "TRUE"
// will also enable this).
var Resource = newFeature(
[]string{"RESOURCE"},
func(v string) (string, bool) {
if strings.EqualFold(v, "true") {
return v, true
}
return "", false
},
)
// Observability is an experimental feature flag that determines if SDK
// observability metrics are enabled.
//
// To enable this feature set the OTEL_GO_X_OBSERVABILITY environment variable
// to the case-insensitive string value of "true" (i.e. "True" and "TRUE"
// will also enable this).
var Observability = newFeature(
[]string{"OBSERVABILITY", "SELF_OBSERVABILITY"},
func(v string) (string, bool) {
if strings.EqualFold(v, "true") {
return v, true
}
return "", false
},
)

View File

@@ -0,0 +1,21 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0
package x
import (
"testing"
"github.com/stretchr/testify/require"
)
func TestResource(t *testing.T) {
const key = "OTEL_GO_X_RESOURCE"
require.Contains(t, Resource.Keys(), key)
t.Run("100", run(setenv(key, "100"), assertDisabled(Resource)))
t.Run("true", run(setenv(key, "true"), assertEnabled(Resource, "true")))
t.Run("True", run(setenv(key, "True"), assertEnabled(Resource, "True")))
t.Run("false", run(setenv(key, "false"), assertDisabled(Resource)))
t.Run("empty", run(assertDisabled(Resource)))
}

View File

@@ -1,48 +1,38 @@
// Code generated by gotmpl. DO NOT MODIFY.
// source: internal/shared/x/x.go.tmpl
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0
// Package x contains support for OTel SDK experimental features.
//
// This package should only be used for features defined in the specification.
// It should not be used for experiments or new project ideas.
// Package x documents experimental features for [go.opentelemetry.io/otel/sdk].
package x // import "go.opentelemetry.io/otel/sdk/internal/x"
import (
"os"
"strings"
)
// Resource is an experimental feature flag that defines if resource detectors
// should be included experimental semantic conventions.
//
// To enable this feature set the OTEL_GO_X_RESOURCE environment variable
// to the case-insensitive string value of "true" (i.e. "True" and "TRUE"
// will also enable this).
var Resource = newFeature("RESOURCE", func(v string) (string, bool) {
if strings.EqualFold(v, "true") {
return v, true
}
return "", false
})
// Feature is an experimental feature control flag. It provides a uniform way
// to interact with these feature flags and parse their values.
type Feature[T any] struct {
key string
keys []string
parse func(v string) (T, bool)
}
func newFeature[T any](suffix string, parse func(string) (T, bool)) Feature[T] {
func newFeature[T any](suffix []string, parse func(string) (T, bool)) Feature[T] {
const envKeyRoot = "OTEL_GO_X_"
keys := make([]string, 0, len(suffix))
for _, s := range suffix {
keys = append(keys, envKeyRoot+s)
}
return Feature[T]{
key: envKeyRoot + suffix,
keys: keys,
parse: parse,
}
}
// Key returns the environment variable key that needs to be set to enable the
// Keys returns the environment variable keys that can be set to enable the
// feature.
func (f Feature[T]) Key() string { return f.key }
func (f Feature[T]) Keys() []string { return f.keys }
// Lookup returns the user configured value for the feature and true if the
// user has enabled the feature. Otherwise, if the feature is not enabled, a
@@ -52,11 +42,13 @@ func (f Feature[T]) Lookup() (v T, ok bool) {
//
// > The SDK MUST interpret an empty value of an environment variable the
// > same way as when the variable is unset.
vRaw := os.Getenv(f.key)
if vRaw == "" {
return v, ok
for _, key := range f.keys {
vRaw := os.Getenv(key)
if vRaw != "" {
return f.parse(vRaw)
}
}
return f.parse(vRaw)
return v, ok
}
// Enabled reports whether the feature is enabled.

View File

@@ -1,25 +1,40 @@
// Code generated by gotmpl. DO NOT MODIFY.
// source: internal/shared/x/x_text.go.tmpl
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0
package x
import (
"strings"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestResource(t *testing.T) {
const key = "OTEL_GO_X_RESOURCE"
require.Equal(t, key, Resource.Key())
const (
mockKey = "OTEL_GO_X_MOCK_FEATURE"
mockKey2 = "OTEL_GO_X_MOCK_FEATURE2"
)
t.Run("true", run(setenv(key, "true"), assertEnabled(Resource, "true")))
t.Run("True", run(setenv(key, "True"), assertEnabled(Resource, "True")))
t.Run("TRUE", run(setenv(key, "TRUE"), assertEnabled(Resource, "TRUE")))
t.Run("false", run(setenv(key, "false"), assertDisabled(Resource)))
t.Run("1", run(setenv(key, "1"), assertDisabled(Resource)))
t.Run("empty", run(assertDisabled(Resource)))
var mockFeature = newFeature([]string{"MOCK_FEATURE", "MOCK_FEATURE2"}, func(v string) (string, bool) {
if strings.EqualFold(v, "true") {
return v, true
}
return "", false
})
func TestFeature(t *testing.T) {
require.Contains(t, mockFeature.Keys(), mockKey)
require.Contains(t, mockFeature.Keys(), mockKey2)
t.Run("100", run(setenv(mockKey, "100"), assertDisabled(mockFeature)))
t.Run("true", run(setenv(mockKey, "true"), assertEnabled(mockFeature, "true")))
t.Run("True", run(setenv(mockKey, "True"), assertEnabled(mockFeature, "True")))
t.Run("false", run(setenv(mockKey, "false"), assertDisabled(mockFeature)))
t.Run("empty", run(assertDisabled(mockFeature)))
}
func run(steps ...func(*testing.T)) func(*testing.T) {
@@ -31,7 +46,7 @@ func run(steps ...func(*testing.T)) func(*testing.T) {
}
}
func setenv(k, v string) func(t *testing.T) { //nolint:unparam
func setenv(k, v string) func(t *testing.T) { //nolint:unparam // This is a reusable test utility function.
return func(t *testing.T) { t.Setenv(k, v) }
}

View File

@@ -7,7 +7,7 @@ Package trace contains support for OpenTelemetry distributed tracing.
The following assumes a basic familiarity with OpenTelemetry concepts.
See https://opentelemetry.io.
See [go.opentelemetry.io/otel/sdk/trace/internal/x] for information about
See [go.opentelemetry.io/otel/sdk/internal/x] for information about
the experimental features.
*/
package trace // import "go.opentelemetry.io/otel/sdk/trace"

View File

@@ -1,8 +0,0 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0
// Package internal provides internal functionality for the sdk/trace package.
package internal // import "go.opentelemetry.io/otel/sdk/trace/internal"
//go:generate gotmpl --body=../../../internal/shared/x/x.go.tmpl "--data={ \"pkg\": \"go.opentelemetry.io/otel/sdk/trace\" }" --out=x/x.go
//go:generate gotmpl --body=../../../internal/shared/x/x_test.go.tmpl "--data={}" --out=x/x_test.go

View File

@@ -12,7 +12,7 @@ import (
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/metric"
"go.opentelemetry.io/otel/sdk"
"go.opentelemetry.io/otel/sdk/trace/internal/x"
"go.opentelemetry.io/otel/sdk/internal/x"
semconv "go.opentelemetry.io/otel/semconv/v1.37.0"
"go.opentelemetry.io/otel/semconv/v1.37.0/otelconv"
)

View File

@@ -12,7 +12,7 @@ import (
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/metric"
"go.opentelemetry.io/otel/sdk"
"go.opentelemetry.io/otel/sdk/trace/internal/x"
"go.opentelemetry.io/otel/sdk/internal/x"
"go.opentelemetry.io/otel/semconv/v1.37.0/otelconv"
"go.opentelemetry.io/otel/trace"
)

View File

@@ -1,35 +0,0 @@
# Experimental Features
The Trace SDK contains features that have not yet stabilized in the OpenTelemetry specification.
These features are added to the OpenTelemetry Go Trace SDK prior to stabilization in the specification so that users can start experimenting with them and provide feedback.
These features may change in backwards incompatible ways as feedback is applied.
See the [Compatibility and Stability](#compatibility-and-stability) section for more information.
## Features
- [Observability](#observability)
### Observability
The SDK can be configured to provide observability about itself using OpenTelemetry metrics.
To opt-in, set the environment variable `OTEL_GO_X_OBSERVABILITY` to `true`.
When enabled, the SDK will create the following metrics using the global `MeterProvider`:
- `otel.sdk.span.live`
- `otel.sdk.span.started`
Please see the [Semantic conventions for OpenTelemetry SDK metrics] documentation for more details on these metrics.
[Semantic conventions for OpenTelemetry SDK metrics]: https://github.com/open-telemetry/semantic-conventions/blob/v1.36.0/docs/otel/sdk-metrics.md
## Compatibility and Stability
Experimental features do not fall within the scope of the OpenTelemetry Go versioning and stability [policy](../../../../VERSIONING.md).
These features may be removed or modified in successive version releases, including patch versions.
When an experimental feature is promoted to a stable feature, a migration path will be included in the changelog entry of the release.
There is no guarantee that any environment variable feature flags that enabled the experimental feature will be supported by the stable version.
If they are supported, they may be accompanied with a deprecation notice stating a timeline for the removal of that support.

View File

@@ -1,22 +0,0 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0
package x // import "go.opentelemetry.io/otel/sdk/trace/internal/x"
import "strings"
// Observability is an experimental feature flag that determines if SDK
// observability metrics are enabled.
//
// To enable this feature set the OTEL_GO_X_OBSERVABILITY environment variable
// to the case-insensitive string value of "true" (i.e. "True" and "TRUE"
// will also enable this).
var Observability = newFeature(
[]string{"OBSERVABILITY", "SELF_OBSERVABILITY"},
func(v string) (string, bool) {
if strings.EqualFold(v, "true") {
return v, true
}
return "", false
},
)

View File

@@ -1,24 +0,0 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0
package x
import (
"testing"
"github.com/stretchr/testify/require"
)
func TestObservability(t *testing.T) {
const key = "OTEL_GO_X_OBSERVABILITY"
require.Contains(t, Observability.Keys(), key)
const altKey = "OTEL_GO_X_SELF_OBSERVABILITY"
require.Contains(t, Observability.Keys(), altKey)
t.Run("100", run(setenv(key, "100"), assertDisabled(Observability)))
t.Run("true", run(setenv(key, "true"), assertEnabled(Observability, "true")))
t.Run("True", run(setenv(key, "True"), assertEnabled(Observability, "True")))
t.Run("false", run(setenv(key, "false"), assertDisabled(Observability)))
t.Run("empty", run(assertDisabled(Observability)))
}

View File

@@ -1,58 +0,0 @@
// Code generated by gotmpl. DO NOT MODIFY.
// source: internal/shared/x/x.go.tmpl
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0
// Package x documents experimental features for [go.opentelemetry.io/otel/sdk/trace].
package x // import "go.opentelemetry.io/otel/sdk/trace/internal/x"
import (
"os"
)
// Feature is an experimental feature control flag. It provides a uniform way
// to interact with these feature flags and parse their values.
type Feature[T any] struct {
keys []string
parse func(v string) (T, bool)
}
func newFeature[T any](suffix []string, parse func(string) (T, bool)) Feature[T] {
const envKeyRoot = "OTEL_GO_X_"
keys := make([]string, 0, len(suffix))
for _, s := range suffix {
keys = append(keys, envKeyRoot+s)
}
return Feature[T]{
keys: keys,
parse: parse,
}
}
// Keys returns the environment variable keys that can be set to enable the
// feature.
func (f Feature[T]) Keys() []string { return f.keys }
// Lookup returns the user configured value for the feature and true if the
// user has enabled the feature. Otherwise, if the feature is not enabled, a
// zero-value and false are returned.
func (f Feature[T]) Lookup() (v T, ok bool) {
// https://github.com/open-telemetry/opentelemetry-specification/blob/62effed618589a0bec416a87e559c0a9d96289bb/specification/configuration/sdk-environment-variables.md#parsing-empty-value
//
// > The SDK MUST interpret an empty value of an environment variable the
// > same way as when the variable is unset.
for _, key := range f.keys {
vRaw := os.Getenv(key)
if vRaw != "" {
return f.parse(vRaw)
}
}
return v, ok
}
// Enabled reports whether the feature is enabled.
func (f Feature[T]) Enabled() bool {
_, ok := f.Lookup()
return ok
}

View File

@@ -1,75 +0,0 @@
// Code generated by gotmpl. DO NOT MODIFY.
// source: internal/shared/x/x_text.go.tmpl
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0
package x
import (
"strings"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
const (
mockKey = "OTEL_GO_X_MOCK_FEATURE"
mockKey2 = "OTEL_GO_X_MOCK_FEATURE2"
)
var mockFeature = newFeature([]string{"MOCK_FEATURE", "MOCK_FEATURE2"}, func(v string) (string, bool) {
if strings.EqualFold(v, "true") {
return v, true
}
return "", false
})
func TestFeature(t *testing.T) {
require.Contains(t, mockFeature.Keys(), mockKey)
require.Contains(t, mockFeature.Keys(), mockKey2)
t.Run("100", run(setenv(mockKey, "100"), assertDisabled(mockFeature)))
t.Run("true", run(setenv(mockKey, "true"), assertEnabled(mockFeature, "true")))
t.Run("True", run(setenv(mockKey, "True"), assertEnabled(mockFeature, "True")))
t.Run("false", run(setenv(mockKey, "false"), assertDisabled(mockFeature)))
t.Run("empty", run(assertDisabled(mockFeature)))
}
func run(steps ...func(*testing.T)) func(*testing.T) {
return func(t *testing.T) {
t.Helper()
for _, step := range steps {
step(t)
}
}
}
func setenv(k, v string) func(t *testing.T) { //nolint:unparam // This is a reusable test utility function.
return func(t *testing.T) { t.Setenv(k, v) }
}
func assertEnabled[T any](f Feature[T], want T) func(*testing.T) {
return func(t *testing.T) {
t.Helper()
assert.True(t, f.Enabled(), "not enabled")
v, ok := f.Lookup()
assert.True(t, ok, "Lookup state")
assert.Equal(t, want, v, "Lookup value")
}
}
func assertDisabled[T any](f Feature[T]) func(*testing.T) {
var zero T
return func(t *testing.T) {
t.Helper()
assert.False(t, f.Enabled(), "enabled")
v, ok := f.Lookup()
assert.False(t, ok, "Lookup state")
assert.Equal(t, zero, v, "Lookup value")
}
}