forked from open-telemetry/opentelemetry-go
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add the internal experimental metric feature package (open-telemetry#…
…4715) * Add the internal experimental metric feature pkg * Interpret empty envvar same as unset * Test Exemplars and CardinalityLimit * Fix empty test for CardinalityLimit * Abstract common testing patterns * Add test cases from review feedback * Rename assertions based on review feedback --------- Co-authored-by: Chester Cheung <[email protected]>
- Loading branch information
1 parent
9ccb0c6
commit d37d851
Showing
2 changed files
with
174 additions
and
0 deletions.
There are no files selected for viewing
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
// Copyright The OpenTelemetry Authors | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
|
||
// 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 // import "go.opentelemetry.io/otel/sdk/metric/internal/x" | ||
|
||
import ( | ||
"os" | ||
"strconv" | ||
"strings" | ||
) | ||
|
||
var ( | ||
// Exemplars is an experimental feature flag that defines if exemplars | ||
// should be recorded for metric data-points. | ||
// | ||
// To enable this feature set the OTEL_GO_X_EXEMPLAR environment variable | ||
// to the case-insensitive string value of "true" (i.e. "True" and "TRUE" | ||
// will also enable this). | ||
Exemplars = newFeature("EXEMPLAR", func(v string) (string, bool) { | ||
if strings.ToLower(v) == "true" { | ||
return v, true | ||
} | ||
return "", false | ||
}) | ||
|
||
// CardinalityLimit is an experimental feature flag that defines if | ||
// cardinality limits should be applied to the recorded metric data-points. | ||
// | ||
// To enable this feature set the OTEL_GO_X_CARDINALITY_LIMIT environment | ||
// variable to the integer limit value you want to use. | ||
CardinalityLimit = newFeature("CARDINALITY_LIMIT", func(v string) (int, bool) { | ||
n, err := strconv.Atoi(v) | ||
if err != nil { | ||
return 0, false | ||
} | ||
return n, true | ||
}) | ||
) | ||
|
||
// 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 | ||
parse func(v string) (T, bool) | ||
} | ||
|
||
func newFeature[T any](suffix string, parse func(string) (T, bool)) Feature[T] { | ||
const envKeyRoot = "OTEL_GO_X_" | ||
return Feature[T]{ | ||
key: envKeyRoot + suffix, | ||
parse: parse, | ||
} | ||
} | ||
|
||
// Key returns the environment variable key that needs to be set to enable the | ||
// feature. | ||
func (f Feature[T]) Key() string { return f.key } | ||
|
||
// 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. | ||
vRaw := os.Getenv(f.key) | ||
if vRaw == "" { | ||
return v, ok | ||
} | ||
return f.parse(vRaw) | ||
} | ||
|
||
// Enabled returns if the feature is enabled. | ||
func (f Feature[T]) Enabled() bool { | ||
_, ok := f.Lookup() | ||
return ok | ||
} |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
// Copyright The OpenTelemetry Authors | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
|
||
package x | ||
|
||
import ( | ||
"testing" | ||
|
||
"github.com/stretchr/testify/assert" | ||
"github.com/stretchr/testify/require" | ||
) | ||
|
||
func TestExemplars(t *testing.T) { | ||
const key = "OTEL_GO_X_EXEMPLAR" | ||
require.Equal(t, key, Exemplars.Key()) | ||
|
||
t.Run("true", run(setenv(key, "true"), assertEnabled(Exemplars, "true"))) | ||
t.Run("True", run(setenv(key, "True"), assertEnabled(Exemplars, "True"))) | ||
t.Run("TRUE", run(setenv(key, "TRUE"), assertEnabled(Exemplars, "TRUE"))) | ||
t.Run("false", run(setenv(key, "false"), assertDisabled(Exemplars))) | ||
t.Run("1", run(setenv(key, "1"), assertDisabled(Exemplars))) | ||
t.Run("empty", run(assertDisabled(Exemplars))) | ||
} | ||
|
||
func TestCardinalityLimit(t *testing.T) { | ||
const key = "OTEL_GO_X_CARDINALITY_LIMIT" | ||
require.Equal(t, key, CardinalityLimit.Key()) | ||
|
||
t.Run("100", run(setenv(key, "100"), assertEnabled(CardinalityLimit, 100))) | ||
t.Run("-1", run(setenv(key, "-1"), assertEnabled(CardinalityLimit, -1))) | ||
t.Run("false", run(setenv(key, "false"), assertDisabled(CardinalityLimit))) | ||
t.Run("empty", run(assertDisabled(CardinalityLimit))) | ||
} | ||
|
||
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) { | ||
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") | ||
} | ||
} |