From a5c82bf26e2cd70201a360fb8557dff40567d5dc Mon Sep 17 00:00:00 2001 From: Aaron Clawson <3766680+MadVikingGod@users.noreply.github.com> Date: Thu, 20 Jul 2023 10:50:12 -0500 Subject: [PATCH] Add otlpmetric Temporality Preference env var (#4287) * Add otlp metrics env vars --------- Co-authored-by: Tyler Yahn --- CHANGELOG.md | 1 + .../otlpmetric/internal/oconf/envconfig.go | 43 ++++++++ .../internal/oconf/envconfig_test.go | 103 ++++++++++++++++++ 3 files changed, 147 insertions(+) create mode 100644 exporters/otlp/otlpmetric/internal/oconf/envconfig_test.go diff --git a/CHANGELOG.md b/CHANGELOG.md index 85f1ec63320..b0924627aff 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm - Add `PeriodicReader` struct in `go.opentelemetry.io/otel/sdk/metric`. (#4244) - Add `Exporter` struct in `go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc`. (#4272) - Add `Exporter` struct in `go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp`. (#4272) +- OTLP Metrics Exporter now supports the `OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE` environment variable. (#4287) - Add `WithoutCounterSuffixes` option in `go.opentelemetry.io/otel/exporters/prometheus` to disable addition of `_total` suffixes. (#4306) - Add info and debug logging to the metric SDK. (#4315) diff --git a/exporters/otlp/otlpmetric/internal/oconf/envconfig.go b/exporters/otlp/otlpmetric/internal/oconf/envconfig.go index 93b1209388d..a8cbdb8eb2a 100644 --- a/exporters/otlp/otlpmetric/internal/oconf/envconfig.go +++ b/exporters/otlp/otlpmetric/internal/oconf/envconfig.go @@ -24,6 +24,9 @@ import ( "time" "go.opentelemetry.io/otel/exporters/otlp/internal/envconfig" + "go.opentelemetry.io/otel/internal/global" + "go.opentelemetry.io/otel/sdk/metric" + "go.opentelemetry.io/otel/sdk/metric/metricdata" ) // DefaultEnvOptionsReader is the default environments reader. @@ -96,6 +99,7 @@ func getOptionsFromEnv() []GenericOption { WithEnvCompression("METRICS_COMPRESSION", func(c Compression) { opts = append(opts, WithCompression(c)) }), envconfig.WithDuration("TIMEOUT", func(d time.Duration) { opts = append(opts, WithTimeout(d)) }), envconfig.WithDuration("METRICS_TIMEOUT", func(d time.Duration) { opts = append(opts, WithTimeout(d)) }), + withEnvTemporalityPreference("METRICS_TEMPORALITY_PREFERENCE", func(t metric.TemporalitySelector) { opts = append(opts, WithTemporalitySelector(t)) }), ) return opts @@ -148,3 +152,42 @@ func withTLSConfig(c *tls.Config, fn func(*tls.Config)) func(e *envconfig.EnvOpt } } } + +func withEnvTemporalityPreference(n string, fn func(metric.TemporalitySelector)) func(e *envconfig.EnvOptionsReader) { + return func(e *envconfig.EnvOptionsReader) { + if s, ok := e.GetEnvValue(n); ok { + switch strings.ToLower(s) { + case "cumulative": + fn(cumulativeTemporality) + case "delta": + fn(deltaTemporality) + case "lowmemory": + fn(lowMemory) + default: + global.Warn("OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE is set to an invalid value, ignoring.", "value", s) + } + } + } +} + +func cumulativeTemporality(metric.InstrumentKind) metricdata.Temporality { + return metricdata.CumulativeTemporality +} + +func deltaTemporality(ik metric.InstrumentKind) metricdata.Temporality { + switch ik { + case metric.InstrumentKindCounter, metric.InstrumentKindHistogram, metric.InstrumentKindObservableCounter: + return metricdata.DeltaTemporality + default: + return metricdata.CumulativeTemporality + } +} + +func lowMemory(ik metric.InstrumentKind) metricdata.Temporality { + switch ik { + case metric.InstrumentKindCounter, metric.InstrumentKindHistogram: + return metricdata.DeltaTemporality + default: + return metricdata.CumulativeTemporality + } +} diff --git a/exporters/otlp/otlpmetric/internal/oconf/envconfig_test.go b/exporters/otlp/otlpmetric/internal/oconf/envconfig_test.go new file mode 100644 index 00000000000..0c54c78e29a --- /dev/null +++ b/exporters/otlp/otlpmetric/internal/oconf/envconfig_test.go @@ -0,0 +1,103 @@ +// 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 oconf + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "go.opentelemetry.io/otel/sdk/metric" + "go.opentelemetry.io/otel/sdk/metric/metricdata" +) + +func TestWithEnvTemporalityPreference(t *testing.T) { + origReader := DefaultEnvOptionsReader.GetEnv + tests := []struct { + name string + envValue string + want map[metric.InstrumentKind]metricdata.Temporality + }{ + { + name: "default do not set the selector", + envValue: "", + }, + { + name: "non-normative do not set the selector", + envValue: "non-normative", + }, + { + name: "cumulative", + envValue: "cumulative", + want: map[metric.InstrumentKind]metricdata.Temporality{ + metric.InstrumentKindCounter: metricdata.CumulativeTemporality, + metric.InstrumentKindHistogram: metricdata.CumulativeTemporality, + metric.InstrumentKindUpDownCounter: metricdata.CumulativeTemporality, + metric.InstrumentKindObservableCounter: metricdata.CumulativeTemporality, + metric.InstrumentKindObservableUpDownCounter: metricdata.CumulativeTemporality, + metric.InstrumentKindObservableGauge: metricdata.CumulativeTemporality, + }, + }, + { + name: "delta", + envValue: "delta", + want: map[metric.InstrumentKind]metricdata.Temporality{ + metric.InstrumentKindCounter: metricdata.DeltaTemporality, + metric.InstrumentKindHistogram: metricdata.DeltaTemporality, + metric.InstrumentKindUpDownCounter: metricdata.CumulativeTemporality, + metric.InstrumentKindObservableCounter: metricdata.DeltaTemporality, + metric.InstrumentKindObservableUpDownCounter: metricdata.CumulativeTemporality, + metric.InstrumentKindObservableGauge: metricdata.CumulativeTemporality, + }, + }, + { + name: "lowmemory", + envValue: "lowmemory", + want: map[metric.InstrumentKind]metricdata.Temporality{ + metric.InstrumentKindCounter: metricdata.DeltaTemporality, + metric.InstrumentKindHistogram: metricdata.DeltaTemporality, + metric.InstrumentKindUpDownCounter: metricdata.CumulativeTemporality, + metric.InstrumentKindObservableCounter: metricdata.CumulativeTemporality, + metric.InstrumentKindObservableUpDownCounter: metricdata.CumulativeTemporality, + metric.InstrumentKindObservableGauge: metricdata.CumulativeTemporality, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + DefaultEnvOptionsReader.GetEnv = func(key string) string { + if key == "OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE" { + return tt.envValue + } + return origReader(key) + } + cfg := Config{} + cfg = ApplyGRPCEnvConfigs(cfg) + + if tt.want == nil { + // There is no function set, the SDK's default is used. + assert.Nil(t, cfg.Metrics.TemporalitySelector) + return + } + + require.NotNil(t, cfg.Metrics.TemporalitySelector) + for ik, want := range tt.want { + assert.Equal(t, want, cfg.Metrics.TemporalitySelector(ik)) + } + }) + } + DefaultEnvOptionsReader.GetEnv = origReader +}