diff --git a/sdk/metric/internal/gen.go b/sdk/metric/internal/gen.go new file mode 100644 index 00000000000..7dae2ebe7dd --- /dev/null +++ b/sdk/metric/internal/gen.go @@ -0,0 +1,8 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +// Package internal provides internal functionality for the sdk/metric package. +package internal // import "go.opentelemetry.io/otel/sdk/metric/internal" + +//go:generate gotmpl --body=../../../internal/shared/x/x.go.tmpl "--data={ \"pkg\": \"go.opentelemetry.io/otel/sdk/metric\" }" --out=x/x.go +//go:generate gotmpl --body=../../../internal/shared/x/x_test.go.tmpl "--data={}" --out=x/x_test.go diff --git a/sdk/metric/internal/x/enabled_instrument.go b/sdk/metric/internal/x/enabled_instrument.go new file mode 100644 index 00000000000..9558ae5e8f9 --- /dev/null +++ b/sdk/metric/internal/x/enabled_instrument.go @@ -0,0 +1,15 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package x // import "go.opentelemetry.io/otel/sdk/metric/internal/x" + +import "context" + +// EnabledInstrument interface is implemented by synchronous instruments. +type EnabledInstrument interface { + // Enabled reports whether the instrument will process measurements for the given context. + // + // This function can be used in places where measuring an instrument + // would result in computationally expensive operations. + Enabled(context.Context) bool +} diff --git a/sdk/metric/internal/x/enabled_instrument_test.go b/sdk/metric/internal/x/enabled_instrument_test.go new file mode 100644 index 00000000000..097d24e7fe3 --- /dev/null +++ b/sdk/metric/internal/x/enabled_instrument_test.go @@ -0,0 +1,26 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package x + +import ( + "context" + "testing" + + "github.com/stretchr/testify/require" +) + +type testInstrument struct{} + +func (*testInstrument) Enabled(_ context.Context) bool { + return true +} + +func TestEnabledInstrument(t *testing.T) { + var ei EnabledInstrument = &testInstrument{} + + ctx := t.Context() + enabled := ei.Enabled(ctx) + + require.True(t, enabled, "Enabled() should return true") +} diff --git a/sdk/metric/internal/x/x.go b/sdk/metric/internal/x/x.go index 294dcf8469e..87aea94a47d 100644 --- a/sdk/metric/internal/x/x.go +++ b/sdk/metric/internal/x/x.go @@ -1,36 +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 metric 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/metric]. package x // import "go.opentelemetry.io/otel/sdk/metric/internal/x" import ( - "context" "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 { - key string + keys []string parse func(v string) (T, bool) } -//nolint:unused -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 @@ -40,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. @@ -52,14 +56,3 @@ func (f Feature[T]) Enabled() bool { _, ok := f.Lookup() return ok } - -// EnabledInstrument informs whether the instrument is enabled. -// -// EnabledInstrument interface is implemented by synchronous instruments. -type EnabledInstrument interface { - // Enabled reports whether the instrument will process measurements for the given context. - // - // This function can be used in places where measuring an instrument - // would result in computationally expensive operations. - Enabled(context.Context) bool -} diff --git a/sdk/metric/internal/x/x_test.go b/sdk/metric/internal/x/x_test.go index d7d5e41ce4a..a715d7608a7 100644 --- a/sdk/metric/internal/x/x_test.go +++ b/sdk/metric/internal/x/x_test.go @@ -1,15 +1,42 @@ +// 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" ) -//nolint:unused +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() @@ -19,12 +46,10 @@ func run(steps ...func(*testing.T)) func(*testing.T) { } } -//nolint:unused -func setenv(k, v string) func(t *testing.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) } } -//nolint:unused func assertEnabled[T any](f Feature[T], want T) func(*testing.T) { return func(t *testing.T) { t.Helper() @@ -36,7 +61,6 @@ func assertEnabled[T any](f Feature[T], want T) func(*testing.T) { } } -//nolint:unused func assertDisabled[T any](f Feature[T]) func(*testing.T) { var zero T return func(t *testing.T) {