-
Notifications
You must be signed in to change notification settings - Fork 1.4k
Add the internal experimental metric feature package #4715
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 8 commits
Commits
Show all changes
11 commits
Select commit
Hold shift + click to select a range
3e48ea5
Add the internal experimental metric feature pkg
MrAlias 758061d
Merge branch 'main' into metric-x
MrAlias 95998d0
Merge branch 'main' into metric-x
hanyuancheung 39a4033
Merge branch 'main' into metric-x
MrAlias 4e99d1b
Interpret empty envvar same as unset
MrAlias 9d6b55b
Test Exemplars and CardinalityLimit
MrAlias f1c9a92
Fix empty test for CardinalityLimit
MrAlias 69a2d40
Abstract common testing patterns
MrAlias f47cba3
Add test cases from review feedback
MrAlias 1a1b4d0
Merge branch 'main' into metric-x
MrAlias ec66f05
Rename assertions based on review feedback
MrAlias File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
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
| 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 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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,78 @@ | ||
| // 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"), evalEnabled(Exemplars, "true"))) | ||
| t.Run("false", run(setenv(key, "false"), evalDisabled(Exemplars))) | ||
|
MrAlias marked this conversation as resolved.
Outdated
|
||
| t.Run("empty", run(evalDisabled(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"), evalEnabled(CardinalityLimit, 100))) | ||
| t.Run("-1", run(setenv(key, "-1"), evalEnabled(CardinalityLimit, -1))) | ||
| t.Run("false", run(setenv(key, "false"), evalDisabled(CardinalityLimit))) | ||
| t.Run("empty", run(evalDisabled(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 evalEnabled[T any](f Feature[T], want T) func(*testing.T) { | ||
| return func(t *testing.T) { | ||
| t.Helper() | ||
|
MrAlias marked this conversation as resolved.
|
||
| 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 evalDisabled[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") | ||
| } | ||
| } | ||
|
MrAlias marked this conversation as resolved.
|
||
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.
Uh oh!
There was an error while loading. Please reload this page.