diff --git a/stats/internal/otel/otel.go b/stats/internal/otel/otel.go index 314c8beb..c679011c 100644 --- a/stats/internal/otel/otel.go +++ b/stats/internal/otel/otel.go @@ -208,11 +208,10 @@ func (m *Manager) Shutdown(ctx context.Context) error { // NewResource allows the creation of an OpenTelemetry resource // https://opentelemetry.io/docs/concepts/glossary/#resource -func NewResource(svcName, instanceID, svcVersion string, attrs ...attribute.KeyValue) (*resource.Resource, error) { +func NewResource(svcName, svcVersion string, attrs ...attribute.KeyValue) (*resource.Resource, error) { defaultAttrs := []attribute.KeyValue{ semconv.ServiceNameKey.String(svcName), semconv.ServiceVersionKey.String(svcVersion), - semconv.ServiceInstanceIDKey.String(instanceID), } return resource.Merge( resource.Default(), diff --git a/stats/internal/otel/otel_test.go b/stats/internal/otel/otel_test.go index 2b7b7bc4..073d7c8f 100644 --- a/stats/internal/otel/otel_test.go +++ b/stats/internal/otel/otel_test.go @@ -33,22 +33,31 @@ const ( metricsPort = "8889" ) +var ( + globalDefaultAttrs = []*promClient.LabelPair{ + {Name: ptr("service_version"), Value: ptr("v1.2.3")}, + {Name: ptr("telemetry_sdk_language"), Value: ptr("go")}, + {Name: ptr("telemetry_sdk_name"), Value: ptr("opentelemetry")}, + {Name: ptr("telemetry_sdk_version"), Value: ptr("1.14.0")}, + {Name: ptr("instanceName"), Value: ptr("my-instance-id")}, + } + globalGRPCDefaultAttrs = append(globalDefaultAttrs, + // the label1=value1 is coming from the otel-collector-config.yaml (see const_labels) + &promClient.LabelPair{Name: ptr("label1"), Value: ptr("value1")}, + ) +) + // see https://opentelemetry.io/docs/collector/getting-started/ func TestMetrics(t *testing.T) { var ( - ctx = context.Background() - meterName = "some-meter-name" - svcName = "TestMetrics" - svcInstanceName = "my-instance-id" - svcVersion = "v0.10.0" + ctx = context.Background() + meterName = "some-meter-name" + svcName = "TestMetrics" ) scenarios := []testCase{ { - name: "grpc", - additionalLabels: []*promClient.LabelPair{ - // the label1=value1 is coming from the otel-collector-config.yaml (see const_labels) - {Name: ptr("label1"), Value: ptr("value1")}, - }, + name: "grpc", + additionalLabels: globalGRPCDefaultAttrs, setupMeterProvider: func(t testing.TB, _ ...MeterProviderOption) (*sdkmetric.MeterProvider, string) { cwd, err := os.Getwd() require.NoError(t, err) @@ -56,7 +65,9 @@ func TestMetrics(t *testing.T) { filepath.Join(cwd, "testdata", "otel-collector-config.yaml"), ) - res, err := NewResource(svcName, svcInstanceName, svcVersion) + res, err := NewResource(svcName, "v1.2.3", + attribute.String("instanceName", "my-instance-id"), + ) require.NoError(t, err) var om Manager tp, mp, err := om.Setup(ctx, res, @@ -79,11 +90,14 @@ func TestMetrics(t *testing.T) { }, }, { - name: "prometheus", + name: "prometheus", + additionalLabels: globalDefaultAttrs, setupMeterProvider: func(t testing.TB, _ ...MeterProviderOption) (*sdkmetric.MeterProvider, string) { registry := prometheus.NewRegistry() - res, err := NewResource(svcName, svcInstanceName, svcVersion) + res, err := NewResource(svcName, "v1.2.3", + attribute.String("instanceName", "my-instance-id"), + ) require.NoError(t, err) var om Manager tp, mp, err := om.Setup(ctx, res, @@ -138,20 +152,22 @@ func TestMetrics(t *testing.T) { require.EqualValues(t, ptr(promClient.MetricType_COUNTER), metrics["foo"].Type) require.Len(t, metrics["foo"].Metric, 1) require.EqualValues(t, &promClient.Counter{Value: ptr(1.0)}, metrics["foo"].Metric[0].Counter) - require.ElementsMatch(t, append([]*promClient.LabelPair{ - {Name: ptr("hello"), Value: ptr("world")}, - {Name: ptr("job"), Value: &svcName}, - {Name: ptr("instance"), Value: &svcInstanceName}, - }, scenario.additionalLabels...), metrics["foo"].Metric[0].Label) + require.ElementsMatch(t, append( + scenario.additionalLabels, + &promClient.LabelPair{Name: ptr("hello"), Value: ptr("world")}, + &promClient.LabelPair{Name: ptr("job"), Value: &svcName}, + &promClient.LabelPair{Name: ptr("service_name"), Value: &svcName}, + ), metrics["foo"].Metric[0].Label) require.EqualValues(t, ptr("bar"), metrics["bar"].Name) require.EqualValues(t, ptr(promClient.MetricType_COUNTER), metrics["bar"].Type) require.Len(t, metrics["bar"].Metric, 1) require.EqualValues(t, &promClient.Counter{Value: ptr(5.0)}, metrics["bar"].Metric[0].Counter) - require.ElementsMatch(t, append([]*promClient.LabelPair{ - {Name: ptr("job"), Value: &svcName}, - {Name: ptr("instance"), Value: &svcInstanceName}, - }, scenario.additionalLabels...), metrics["bar"].Metric[0].Label) + require.ElementsMatch(t, append( + scenario.additionalLabels, + &promClient.LabelPair{Name: ptr("job"), Value: &svcName}, + &promClient.LabelPair{Name: ptr("service_name"), Value: &svcName}, + ), metrics["bar"].Metric[0].Label) requireHistogramEqual(t, metrics["baz"], histogram{ name: "baz", count: 1, sum: 20, @@ -161,11 +177,12 @@ func TestMetrics(t *testing.T) { {CumulativeCount: ptr(uint64(1)), UpperBound: ptr(30.0)}, {CumulativeCount: ptr(uint64(1)), UpperBound: ptr(math.Inf(1))}, }, - labels: append([]*promClient.LabelPair{ - {Name: ptr("a"), Value: ptr("b")}, - {Name: ptr("job"), Value: &svcName}, - {Name: ptr("instance"), Value: &svcInstanceName}, - }, scenario.additionalLabels...), + labels: append( + scenario.additionalLabels, + &promClient.LabelPair{Name: ptr("a"), Value: ptr("b")}, + &promClient.LabelPair{Name: ptr("job"), Value: &svcName}, + &promClient.LabelPair{Name: ptr("service_name"), Value: &svcName}, + ), }) requireHistogramEqual(t, metrics["qux"], histogram{ @@ -176,11 +193,12 @@ func TestMetrics(t *testing.T) { {CumulativeCount: ptr(uint64(1)), UpperBound: ptr(3.0)}, {CumulativeCount: ptr(uint64(1)), UpperBound: ptr(math.Inf(1))}, }, - labels: append([]*promClient.LabelPair{ - {Name: ptr("c"), Value: ptr("d")}, - {Name: ptr("job"), Value: &svcName}, - {Name: ptr("instance"), Value: &svcInstanceName}, - }, scenario.additionalLabels...), + labels: append( + scenario.additionalLabels, + &promClient.LabelPair{Name: ptr("c"), Value: ptr("d")}, + &promClient.LabelPair{Name: ptr("job"), Value: &svcName}, + &promClient.LabelPair{Name: ptr("service_name"), Value: &svcName}, + ), }) }) } @@ -188,18 +206,13 @@ func TestMetrics(t *testing.T) { func TestHistogramBuckets(t *testing.T) { var ( - ctx = context.Background() - svcName = "TestHistogramBuckets" - svcInstanceName = "my-instance-id" - svcVersion = "v0.10.0" + ctx = context.Background() + svcName = "TestHistogramBuckets" ) scenarios := []testCase{ { - name: "grpc", - additionalLabels: []*promClient.LabelPair{ - // the label1=value1 is coming from the otel-collector-config.yaml (see const_labels) - {Name: ptr("label1"), Value: ptr("value1")}, - }, + name: "grpc", + additionalLabels: globalGRPCDefaultAttrs, setupMeterProvider: func(t testing.TB, opts ...MeterProviderOption) (*sdkmetric.MeterProvider, string) { cwd, err := os.Getwd() require.NoError(t, err) @@ -207,7 +220,7 @@ func TestHistogramBuckets(t *testing.T) { filepath.Join(cwd, "testdata", "otel-collector-config.yaml"), ) - res, err := NewResource(svcName, svcInstanceName, svcVersion) + res, err := NewResource(svcName, "v1.2.3", attribute.String("instanceName", "my-instance-id")) require.NoError(t, err) var om Manager _, mp, err := om.Setup(ctx, res, @@ -226,11 +239,12 @@ func TestHistogramBuckets(t *testing.T) { }, }, { - name: "prometheus", + name: "prometheus", + additionalLabels: globalDefaultAttrs, setupMeterProvider: func(t testing.TB, opts ...MeterProviderOption) (*sdkmetric.MeterProvider, string) { registry := prometheus.NewRegistry() - res, err := NewResource(svcName, svcInstanceName, svcVersion) + res, err := NewResource(svcName, "v1.2.3", attribute.String("instanceName", "my-instance-id")) require.NoError(t, err) var om Manager tp, mp, err := om.Setup(ctx, res, @@ -282,11 +296,12 @@ func TestHistogramBuckets(t *testing.T) { {CumulativeCount: ptr(uint64(1)), UpperBound: ptr(30.0)}, {CumulativeCount: ptr(uint64(1)), UpperBound: ptr(math.Inf(1))}, }, - labels: append([]*promClient.LabelPair{ - {Name: ptr("a"), Value: ptr("b")}, - {Name: ptr("job"), Value: &svcName}, - {Name: ptr("instance"), Value: &svcInstanceName}, - }, scenario.additionalLabels...), + labels: append( + scenario.additionalLabels, + &promClient.LabelPair{Name: ptr("a"), Value: ptr("b")}, + &promClient.LabelPair{Name: ptr("job"), Value: &svcName}, + &promClient.LabelPair{Name: ptr("service_name"), Value: &svcName}, + ), }) requireHistogramEqual(t, metrics["bar"], histogram{ @@ -297,11 +312,12 @@ func TestHistogramBuckets(t *testing.T) { {CumulativeCount: ptr(uint64(1)), UpperBound: ptr(30.0)}, {CumulativeCount: ptr(uint64(1)), UpperBound: ptr(math.Inf(1))}, }, - labels: append([]*promClient.LabelPair{ - {Name: ptr("c"), Value: ptr("d")}, - {Name: ptr("job"), Value: &svcName}, - {Name: ptr("instance"), Value: &svcInstanceName}, - }, scenario.additionalLabels...), + labels: append( + scenario.additionalLabels, + &promClient.LabelPair{Name: ptr("c"), Value: ptr("d")}, + &promClient.LabelPair{Name: ptr("job"), Value: &svcName}, + &promClient.LabelPair{Name: ptr("service_name"), Value: &svcName}, + ), }) }) @@ -337,11 +353,12 @@ func TestHistogramBuckets(t *testing.T) { {CumulativeCount: ptr(uint64(1)), UpperBound: ptr(30.0)}, {CumulativeCount: ptr(uint64(1)), UpperBound: ptr(math.Inf(1))}, }, - labels: append([]*promClient.LabelPair{ - {Name: ptr("a"), Value: ptr("b")}, - {Name: ptr("job"), Value: &svcName}, - {Name: ptr("instance"), Value: &svcInstanceName}, - }, scenario.additionalLabels...), + labels: append( + scenario.additionalLabels, + &promClient.LabelPair{Name: ptr("a"), Value: ptr("b")}, + &promClient.LabelPair{Name: ptr("job"), Value: &svcName}, + &promClient.LabelPair{Name: ptr("service_name"), Value: &svcName}, + ), }) requireHistogramEqual(t, metrics["bar"], histogram{ @@ -352,11 +369,12 @@ func TestHistogramBuckets(t *testing.T) { {CumulativeCount: ptr(uint64(1)), UpperBound: ptr(60.0)}, {CumulativeCount: ptr(uint64(1)), UpperBound: ptr(math.Inf(1))}, }, - labels: append([]*promClient.LabelPair{ - {Name: ptr("c"), Value: ptr("d")}, - {Name: ptr("job"), Value: &svcName}, - {Name: ptr("instance"), Value: &svcInstanceName}, - }, scenario.additionalLabels...), + labels: append( + scenario.additionalLabels, + &promClient.LabelPair{Name: ptr("c"), Value: ptr("d")}, + &promClient.LabelPair{Name: ptr("job"), Value: &svcName}, + &promClient.LabelPair{Name: ptr("service_name"), Value: &svcName}, + ), }) requireHistogramEqual(t, metrics["baz"], histogram{ @@ -367,11 +385,12 @@ func TestHistogramBuckets(t *testing.T) { {CumulativeCount: ptr(uint64(1)), UpperBound: ptr(90.0)}, {CumulativeCount: ptr(uint64(1)), UpperBound: ptr(math.Inf(1))}, }, - labels: append([]*promClient.LabelPair{ - {Name: ptr("e"), Value: ptr("f")}, - {Name: ptr("job"), Value: &svcName}, - {Name: ptr("instance"), Value: &svcInstanceName}, - }, scenario.additionalLabels...), + labels: append( + scenario.additionalLabels, + &promClient.LabelPair{Name: ptr("e"), Value: ptr("f")}, + &promClient.LabelPair{Name: ptr("job"), Value: &svcName}, + &promClient.LabelPair{Name: ptr("service_name"), Value: &svcName}, + ), }) }) }) @@ -404,7 +423,7 @@ func TestCollectorGlobals(t *testing.T) { ctx = context.Background() endpoint = fmt.Sprintf("localhost:%d", grpcPort) ) - res, err := NewResource(t.Name(), "my-instance-id", "1.0.0") + res, err := NewResource(t.Name(), "v1.2.3", attribute.String("instanceName", "my-instance-id")) require.NoError(t, err) tp, mp, err := om.Setup(ctx, res, WithInsecure(), @@ -421,7 +440,9 @@ func TestNonBlockingConnection(t *testing.T) { grpcPort, err := testhelper.GetFreePort() require.NoError(t, err) - res, err := NewResource(t.Name(), "my-instance-id", "1.0.0") + res, err := NewResource(t.Name(), "v1.2.3", + attribute.String("instanceName", "my-instance-id"), + ) require.NoError(t, err) var ( @@ -465,28 +486,24 @@ func TestNonBlockingConnection(t *testing.T) { metricsEndpoint := fmt.Sprintf("http://localhost:%d/metrics", dt.GetHostPort(t, metricsPort, container)) metrics := requireMetrics(t, metricsEndpoint, "foo", "bar") + defaultAttrs := append(globalGRPCDefaultAttrs, + &promClient.LabelPair{Name: ptr("job"), Value: ptr("TestNonBlockingConnection")}, + &promClient.LabelPair{Name: ptr("service_name"), Value: ptr("TestNonBlockingConnection")}, + ) + require.EqualValues(t, ptr("foo"), metrics["foo"].Name) require.EqualValues(t, ptr(promClient.MetricType_COUNTER), metrics["foo"].Type) require.Len(t, metrics["foo"].Metric, 1) require.EqualValues(t, &promClient.Counter{Value: ptr(123.0)}, metrics["foo"].Metric[0].Counter) - require.ElementsMatch(t, []*promClient.LabelPair{ - // the label1=value1 is coming from the otel-collector-config.yaml (see const_labels) - {Name: ptr("label1"), Value: ptr("value1")}, - {Name: ptr("hello"), Value: ptr("world")}, - {Name: ptr("job"), Value: ptr("TestNonBlockingConnection")}, - {Name: ptr("instance"), Value: ptr("my-instance-id")}, - }, metrics["foo"].Metric[0].Label) + require.ElementsMatch(t, append(defaultAttrs, + &promClient.LabelPair{Name: ptr("hello"), Value: ptr("world")}, + ), metrics["foo"].Metric[0].Label) require.EqualValues(t, ptr("bar"), metrics["bar"].Name) require.EqualValues(t, ptr(promClient.MetricType_COUNTER), metrics["bar"].Type) require.Len(t, metrics["bar"].Metric, 1) require.EqualValues(t, &promClient.Counter{Value: ptr(456.0)}, metrics["bar"].Metric[0].Counter) - require.ElementsMatch(t, []*promClient.LabelPair{ - // the label1=value1 is coming from the otel-collector-config.yaml (see const_labels) - {Name: ptr("label1"), Value: ptr("value1")}, - {Name: ptr("job"), Value: ptr("TestNonBlockingConnection")}, - {Name: ptr("instance"), Value: ptr("my-instance-id")}, - }, metrics["bar"].Metric[0].Label) + require.ElementsMatch(t, defaultAttrs, metrics["bar"].Metric[0].Label) } func requireMetrics( diff --git a/stats/internal/otel/prometheus/config.go b/stats/internal/otel/prometheus/config.go index 4274df2e..e95c9b08 100644 --- a/stats/internal/otel/prometheus/config.go +++ b/stats/internal/otel/prometheus/config.go @@ -16,7 +16,6 @@ type config struct { disableTargetInfo bool withoutUnits bool aggregation metric.AggregationSelector - disableScopeInfo bool logger logger } @@ -102,16 +101,6 @@ func WithoutUnits() Option { }) } -// WithoutScopeInfo configures the Exporter to not export the otel_scope_info metric. -// If not specified, the Exporter will create a otel_scope_info metric containing -// the metrics' Instrumentation Scope, and also add labels about Instrumentation Scope to all metric points. -func WithoutScopeInfo() Option { - return optionFunc(func(cfg config) config { - cfg.disableScopeInfo = true - return cfg - }) -} - // WithLogger enables the logger func WithLogger(l logger) Option { return optionFunc(func(cfg config) config { diff --git a/stats/internal/otel/prometheus/exporter.go b/stats/internal/otel/prometheus/exporter.go index eae0d483..b2e94163 100644 --- a/stats/internal/otel/prometheus/exporter.go +++ b/stats/internal/otel/prometheus/exporter.go @@ -13,6 +13,8 @@ // // 3. a global logger was used, we made it injectable via options // see here: https://github.com/open-telemetry/opentelemetry-go/blob/v1.14.0/exporters/prometheus/exporter.go#L393 +// +// 4. removed unnecessary otel_scope_info metric package prometheus import ( @@ -41,24 +43,6 @@ import ( const ( targetInfoMetricName = "target_info" targetInfoDescription = "Target metadata" - - scopeInfoMetricName = "otel_scope_info" - scopeInfoDescription = "Instrumentation Scope metadata" -) - -var ( - scopeInfoKeys = [2]string{ - string(semconv.ServiceNameKey), - string(semconv.ServiceInstanceIDKey), - } - scopeInfoKeysMapping = map[string]string{ - string(semconv.ServiceNameKey): "job", - string(semconv.ServiceInstanceIDKey): "instance", - } - scopeInfoKeysRenamed = [2]string{ - scopeInfoKeysMapping[scopeInfoKeys[0]], - scopeInfoKeysMapping[scopeInfoKeys[1]], - } ) // Exporter is a Prometheus Exporter that embeds the OTel metric.Reader @@ -77,7 +61,6 @@ type collector struct { disableTargetInfo bool withoutUnits bool targetInfo prometheus.Metric - disableScopeInfo bool createTargetInfoOnce sync.Once scopeInfos map[instrumentation.Scope]prometheus.Metric metricFamilies map[string]*dto.MetricFamily @@ -96,7 +79,6 @@ func New(opts ...Option) (*Exporter, error) { logger: cfg.logger, disableTargetInfo: cfg.disableTargetInfo, withoutUnits: cfg.withoutUnits, - disableScopeInfo: cfg.disableScopeInfo, scopeInfos: make(map[instrumentation.Scope]prometheus.Metric), metricFamilies: make(map[string]*dto.MetricFamily), } @@ -140,37 +122,29 @@ func (c *collector) Collect(ch chan<- prometheus.Metric) { ch <- c.targetInfo } - scopeInfoValues := getScopeInfoValues(metrics.Resource, scopeInfoKeys[:]) - - for _, scopeMetrics := range metrics.ScopeMetrics { - var keys, values [2]string - - if !c.disableScopeInfo { - scopeInfo, ok := c.scopeInfos[scopeMetrics.Scope] - if !ok { - scopeInfo, err = createScopeInfoMetric(scopeMetrics.Scope) - if err != nil { - otel.Handle(err) - } - c.scopeInfos[scopeMetrics.Scope] = scopeInfo - } - ch <- scopeInfo - keys = scopeInfoKeysRenamed - values = [2]string{scopeInfoValues[0], scopeInfoValues[1]} + var scopeKeys, scopeValues []string + for _, attr := range metrics.Resource.Attributes() { + if string(attr.Key) == string(semconv.ServiceNameKey) { + scopeKeys = append(scopeKeys, "job") + scopeValues = append(scopeValues, attr.Value.AsString()) } + scopeKeys = append(scopeKeys, strings.ReplaceAll(string(attr.Key), ".", "_")) + scopeValues = append(scopeValues, attr.Value.AsString()) + } + for _, scopeMetrics := range metrics.ScopeMetrics { for _, m := range scopeMetrics.Metrics { switch v := m.Data.(type) { case metricdata.Histogram: - addHistogramMetric(ch, v, m, keys, values, c.getName(m), c.metricFamilies, c.logger) + addHistogramMetric(ch, v, m, scopeKeys, scopeValues, c.getName(m), c.metricFamilies, c.logger) case metricdata.Sum[int64]: - addSumMetric(ch, v, m, keys, values, c.getName(m), c.metricFamilies, c.logger) + addSumMetric(ch, v, m, scopeKeys, scopeValues, c.getName(m), c.metricFamilies, c.logger) case metricdata.Sum[float64]: - addSumMetric(ch, v, m, keys, values, c.getName(m), c.metricFamilies, c.logger) + addSumMetric(ch, v, m, scopeKeys, scopeValues, c.getName(m), c.metricFamilies, c.logger) case metricdata.Gauge[int64]: - addGaugeMetric(ch, v, m, keys, values, c.getName(m), c.metricFamilies, c.logger) + addGaugeMetric(ch, v, m, scopeKeys, scopeValues, c.getName(m), c.metricFamilies, c.logger) case metricdata.Gauge[float64]: - addGaugeMetric(ch, v, m, keys, values, c.getName(m), c.metricFamilies, c.logger) + addGaugeMetric(ch, v, m, scopeKeys, scopeValues, c.getName(m), c.metricFamilies, c.logger) } } } @@ -178,7 +152,7 @@ func (c *collector) Collect(ch chan<- prometheus.Metric) { func addHistogramMetric( ch chan<- prometheus.Metric, histogram metricdata.Histogram, m metricdata.Metrics, - ks, vs [2]string, name string, mfs map[string]*dto.MetricFamily, l logger, + ks, vs []string, name string, mfs map[string]*dto.MetricFamily, l logger, ) { drop, help := validateMetrics(name, m.Description, dto.MetricType_HISTOGRAM.Enum(), mfs, l) if drop { @@ -210,7 +184,7 @@ func addHistogramMetric( func addSumMetric[N int64 | float64]( ch chan<- prometheus.Metric, sum metricdata.Sum[N], m metricdata.Metrics, - ks, vs [2]string, name string, mfs map[string]*dto.MetricFamily, l logger, + ks, vs []string, name string, mfs map[string]*dto.MetricFamily, l logger, ) { valueType := prometheus.CounterValue metricType := dto.MetricType_COUNTER @@ -242,7 +216,7 @@ func addSumMetric[N int64 | float64]( func addGaugeMetric[N int64 | float64]( ch chan<- prometheus.Metric, gauge metricdata.Gauge[N], m metricdata.Metrics, - ks, vs [2]string, name string, mfs map[string]*dto.MetricFamily, l logger, + ks, vs []string, name string, mfs map[string]*dto.MetricFamily, l logger, ) { drop, help := validateMetrics(name, m.Description, dto.MetricType_GAUGE.Enum(), mfs, l) if drop { @@ -268,7 +242,7 @@ func addGaugeMetric[N int64 | float64]( // getAttrs parses the attribute.Set to two lists of matching Prometheus-style // keys and values. It sanitizes invalid characters and handles duplicate keys // (due to sanitization) by sorting and concatenating the values following the spec. -func getAttrs(attrs attribute.Set, ks, vs [2]string) ([]string, []string) { +func getAttrs(attrs attribute.Set, ks, vs []string) ([]string, []string) { keysMap := make(map[string][]string) itr := attrs.Iter() for itr.Next() { @@ -292,7 +266,7 @@ func getAttrs(attrs attribute.Set, ks, vs [2]string) ([]string, []string) { values = append(values, strings.Join(vals, ";")) } - if ks[0] != "" { + if len(ks) > 0 { keys = append(keys, ks[:]...) values = append(values, vs[:]...) } @@ -300,17 +274,11 @@ func getAttrs(attrs attribute.Set, ks, vs [2]string) ([]string, []string) { } func (c *collector) createInfoMetric(name, description string, res *resource.Resource) (prometheus.Metric, error) { - keys, values := getAttrs(*res.Set(), [2]string{}, [2]string{}) + keys, values := getAttrs(*res.Set(), []string{}, []string{}) desc := prometheus.NewDesc(name, description, keys, nil) return prometheus.NewConstMetric(desc, prometheus.GaugeValue, float64(1), values...) } -func createScopeInfoMetric(scope instrumentation.Scope) (prometheus.Metric, error) { - keys := scopeInfoKeysRenamed[:] - desc := prometheus.NewDesc(scopeInfoMetricName, scopeInfoDescription, keys, nil) - return prometheus.NewConstMetric(desc, prometheus.GaugeValue, float64(1), scope.Name, scope.Version) -} - func sanitizeRune(r rune) rune { if unicode.IsLetter(r) || unicode.IsDigit(r) || r == ':' || r == '_' { return r @@ -423,18 +391,3 @@ func validateMetrics( return false, "" } - -func getScopeInfoValues(res *resource.Resource, scopeInfoKeys []string) []string { - scopeInfoValues := make([]string, len(scopeInfoKeys)) - - for i, key := range scopeInfoKeys { - for _, attr := range res.Attributes() { - if key == string(attr.Key) { - scopeInfoValues[i] = attr.Value.AsString() - break - } - } - } - - return scopeInfoValues -} diff --git a/stats/internal/otel/testdata/otel-collector-config.yaml b/stats/internal/otel/testdata/otel-collector-config.yaml index 9bad23b5..08742fa0 100644 --- a/stats/internal/otel/testdata/otel-collector-config.yaml +++ b/stats/internal/otel/testdata/otel-collector-config.yaml @@ -6,6 +6,8 @@ receivers: exporters: prometheus: endpoint: "0.0.0.0:8889" + resource_to_telemetry_conversion: + enabled: true const_labels: label1: value1 diff --git a/stats/otel.go b/stats/otel.go index cd134bc4..f98e4bb8 100644 --- a/stats/otel.go +++ b/stats/otel.go @@ -65,7 +65,7 @@ func (s *otelStats) Start(ctx context.Context, goFactory GoRoutineFactory) error if s.config.namespaceIdentifier != "" { attrs = append(attrs, attribute.String("namespace", s.config.namespaceIdentifier)) } - res, err := otel.NewResource(s.config.serviceName, s.config.instanceName, s.config.serviceVersion, attrs...) + res, err := otel.NewResource(s.config.serviceName, s.config.serviceVersion, attrs...) if err != nil { return fmt.Errorf("failed to create open telemetry resource: %w", err) } diff --git a/stats/otel_test.go b/stats/otel_test.go index c30a635a..6aae9b3d 100644 --- a/stats/otel_test.go +++ b/stats/otel_test.go @@ -37,6 +37,14 @@ const ( metricsPort = "8889" ) +var globalDefaultAttrs = []*promClient.LabelPair{ + {Name: ptr("instanceName"), Value: ptr("my-instance-id")}, + {Name: ptr("service_version"), Value: ptr("v1.2.3")}, + {Name: ptr("telemetry_sdk_language"), Value: ptr("go")}, + {Name: ptr("telemetry_sdk_name"), Value: ptr("opentelemetry")}, + {Name: ptr("telemetry_sdk_version"), Value: ptr("1.14.0")}, +} + func TestOTelMeasurementInvalidOperations(t *testing.T) { s := &otelStats{meter: global.MeterProvider().Meter(t.Name())} @@ -306,7 +314,7 @@ func TestOTelPeriodicStats(t *testing.T) { prepareFunc(c, m) l := logger.NewFactory(c) - s := NewStats(c, l, m, WithServiceName("TestOTelPeriodicStats")) + s := NewStats(c, l, m, WithServiceName("TestOTelPeriodicStats"), WithServiceVersion("v1.2.3")) // start stats ctx, cancel := context.WithCancel(context.Background()) @@ -345,12 +353,12 @@ func TestOTelPeriodicStats(t *testing.T) { require.EqualValues(t, ptr(promClient.MetricType_GAUGE), metrics[metricName].Type) require.Len(t, metrics[metricName].Metric, 1) - expectedLabels := []*promClient.LabelPair{ + expectedLabels := append(globalDefaultAttrs, // the label1=value1 is coming from the otel-collector-config.yaml (see const_labels) - {Name: ptr("label1"), Value: ptr("value1")}, - {Name: ptr("job"), Value: ptr("TestOTelPeriodicStats")}, - {Name: ptr("instance"), Value: ptr("my-instance-id")}, - } + &promClient.LabelPair{Name: ptr("label1"), Value: ptr("value1")}, + &promClient.LabelPair{Name: ptr("job"), Value: ptr("TestOTelPeriodicStats")}, + &promClient.LabelPair{Name: ptr("service_name"), Value: ptr("TestOTelPeriodicStats")}, + ) if exp.tags != nil { expectedLabels = append(expectedLabels, exp.tags...) } @@ -465,7 +473,7 @@ func TestOTelExcludedTags(t *testing.T) { c.Set("statsExcludedTags", []string{"workspaceId"}) l := logger.NewFactory(c) m := metric.NewManager() - s := NewStats(c, l, m, WithServiceName(t.Name())) + s := NewStats(c, l, m, WithServiceName(t.Name()), WithServiceVersion("v1.2.3")) // start stats ctx, cancel := context.WithCancel(context.Background()) @@ -486,13 +494,13 @@ func TestOTelExcludedTags(t *testing.T) { require.EqualValues(t, ptr(promClient.MetricType_COUNTER), metrics[metricName].Type) require.Len(t, metrics[metricName].Metric, 1) require.EqualValues(t, &promClient.Counter{Value: ptr(1.0)}, metrics[metricName].Metric[0].Counter) - require.ElementsMatchf(t, []*promClient.LabelPair{ + require.ElementsMatchf(t, append(globalDefaultAttrs, // the label1=value1 is coming from the otel-collector-config.yaml (see const_labels) - {Name: ptr("label1"), Value: ptr("value1")}, - {Name: ptr("should_not_be_filtered"), Value: ptr("fancy-value")}, - {Name: ptr("job"), Value: ptr("TestOTelExcludedTags")}, - {Name: ptr("instance"), Value: ptr("my-instance-id")}, - }, metrics[metricName].Metric[0].Label, "Got %+v", metrics[metricName].Metric[0].Label) + &promClient.LabelPair{Name: ptr("label1"), Value: ptr("value1")}, + &promClient.LabelPair{Name: ptr("should_not_be_filtered"), Value: ptr("fancy-value")}, + &promClient.LabelPair{Name: ptr("job"), Value: ptr("TestOTelExcludedTags")}, + &promClient.LabelPair{Name: ptr("service_name"), Value: ptr("TestOTelExcludedTags")}, + ), metrics[metricName].Metric[0].Label, "Got %+v", metrics[metricName].Metric[0].Label) } func TestOTelStartStopError(t *testing.T) { @@ -530,10 +538,10 @@ func TestOTelMeasurementsConsistency(t *testing.T) { scenarios := []testCase{ { name: "grpc", - additionalLabels: []*promClient.LabelPair{ + additionalLabels: append(globalDefaultAttrs, // the label1=value1 is coming from the otel-collector-config.yaml (see const_labels) - {Name: ptr("label1"), Value: ptr("value1")}, - }, + &promClient.LabelPair{Name: ptr("label1"), Value: ptr("value1")}, + ), setupMeterProvider: func(t testing.TB) (Stats, string) { cwd, err := os.Getwd() require.NoError(t, err) @@ -551,6 +559,7 @@ func TestOTelMeasurementsConsistency(t *testing.T) { m := metric.NewManager() s := NewStats(c, l, m, WithServiceName("TestOTelHistogramBuckets"), + WithServiceVersion("v1.2.3"), WithDefaultHistogramBuckets([]float64{10, 20, 30}), WithHistogramBuckets("bar", []float64{40, 50, 60}), ) @@ -560,7 +569,8 @@ func TestOTelMeasurementsConsistency(t *testing.T) { }, }, { - name: "prometheus", + name: "prometheus", + additionalLabels: globalDefaultAttrs, setupMeterProvider: func(t testing.TB) (Stats, string) { freePort, err := testhelper.GetFreePort() require.NoError(t, err) @@ -576,6 +586,7 @@ func TestOTelMeasurementsConsistency(t *testing.T) { m := metric.NewManager() s := NewStats(c, l, m, WithServiceName("TestOTelHistogramBuckets"), + WithServiceVersion("v1.2.3"), WithDefaultHistogramBuckets([]float64{10, 20, 30}), WithHistogramBuckets("bar", []float64{40, 50, 60}), ) @@ -618,7 +629,7 @@ func TestOTelMeasurementsConsistency(t *testing.T) { require.ElementsMatchf(t, append([]*promClient.LabelPair{ {Name: ptr("a"), Value: ptr("b")}, {Name: ptr("job"), Value: ptr("TestOTelHistogramBuckets")}, - {Name: ptr("instance"), Value: ptr("my-instance-id")}, + {Name: ptr("service_name"), Value: ptr("TestOTelHistogramBuckets")}, }, scenario.additionalLabels...), metrics["foo"].Metric[0].Label, "Got %+v", metrics["foo"].Metric[0].Label) require.EqualValues(t, ptr("bar"), metrics["bar"].Name) @@ -635,7 +646,7 @@ func TestOTelMeasurementsConsistency(t *testing.T) { require.ElementsMatchf(t, append([]*promClient.LabelPair{ {Name: ptr("c"), Value: ptr("d")}, {Name: ptr("job"), Value: ptr("TestOTelHistogramBuckets")}, - {Name: ptr("instance"), Value: ptr("my-instance-id")}, + {Name: ptr("service_name"), Value: ptr("TestOTelHistogramBuckets")}, }, scenario.additionalLabels...), metrics["bar"].Metric[0].Label, "Got %+v", metrics["bar"].Metric[0].Label) require.EqualValues(t, ptr("baz"), metrics["baz"].Name) @@ -645,7 +656,7 @@ func TestOTelMeasurementsConsistency(t *testing.T) { require.ElementsMatchf(t, append([]*promClient.LabelPair{ {Name: ptr("e"), Value: ptr("f")}, {Name: ptr("job"), Value: ptr("TestOTelHistogramBuckets")}, - {Name: ptr("instance"), Value: ptr("my-instance-id")}, + {Name: ptr("service_name"), Value: ptr("TestOTelHistogramBuckets")}, }, scenario.additionalLabels...), metrics["baz"].Metric[0].Label, "Got %+v", metrics["baz"].Metric[0].Label) require.EqualValues(t, ptr("qux"), metrics["qux"].Name) @@ -655,7 +666,7 @@ func TestOTelMeasurementsConsistency(t *testing.T) { require.ElementsMatchf(t, append([]*promClient.LabelPair{ {Name: ptr("g"), Value: ptr("h")}, {Name: ptr("job"), Value: ptr("TestOTelHistogramBuckets")}, - {Name: ptr("instance"), Value: ptr("my-instance-id")}, + {Name: ptr("service_name"), Value: ptr("TestOTelHistogramBuckets")}, }, scenario.additionalLabels...), metrics["qux"].Metric[0].Label, "Got %+v", metrics["qux"].Metric[0].Label) require.EqualValues(t, ptr("asd"), metrics["asd"].Name) @@ -670,7 +681,7 @@ func TestOTelMeasurementsConsistency(t *testing.T) { require.ElementsMatchf(t, append([]*promClient.LabelPair{ {Name: ptr("i"), Value: ptr("l")}, {Name: ptr("job"), Value: ptr("TestOTelHistogramBuckets")}, - {Name: ptr("instance"), Value: ptr("my-instance-id")}, + {Name: ptr("service_name"), Value: ptr("TestOTelHistogramBuckets")}, }, scenario.additionalLabels...), metrics["asd"].Metric[0].Label, "Got %+v", metrics["asd"].Metric[0].Label) }) } @@ -694,6 +705,7 @@ func TestPrometheusCustomRegistry(t *testing.T) { r := prometheus.NewRegistry() s := NewStats(c, l, m, WithServiceName("TestPrometheusCustomRegistry"), + WithServiceVersion("v1.2.3"), WithPrometheusRegistry(r, r), ) require.NoError(t, s.Start(context.Background(), DefaultGoRoutineFactory)) @@ -732,11 +744,11 @@ func TestPrometheusCustomRegistry(t *testing.T) { require.EqualValues(t, ptr(promClient.MetricType_COUNTER), metrics[metricName].Type) require.Len(t, metrics[metricName].Metric, 1) require.EqualValues(t, &promClient.Counter{Value: ptr(7.0)}, metrics[metricName].Metric[0].Counter) - require.ElementsMatchf(t, []*promClient.LabelPair{ - {Name: ptr("a"), Value: ptr("b")}, - {Name: ptr("job"), Value: ptr("TestPrometheusCustomRegistry")}, - {Name: ptr("instance"), Value: ptr("my-instance-id")}, - }, metrics[metricName].Metric[0].Label, "Got %+v", metrics[metricName].Metric[0].Label) + require.ElementsMatchf(t, append(globalDefaultAttrs, + &promClient.LabelPair{Name: ptr("a"), Value: ptr("b")}, + &promClient.LabelPair{Name: ptr("job"), Value: ptr("TestPrometheusCustomRegistry")}, + &promClient.LabelPair{Name: ptr("service_name"), Value: ptr("TestPrometheusCustomRegistry")}, + ), metrics[metricName].Metric[0].Label, "Got %+v", metrics[metricName].Metric[0].Label) }) t.Run("collector", func(t *testing.T) { @@ -755,11 +767,11 @@ func TestPrometheusCustomRegistry(t *testing.T) { require.EqualValues(t, metricName, mf.GetName()) require.EqualValues(t, promClient.MetricType_COUNTER, mf.GetType()) require.Len(t, mf.GetMetric(), 1) - require.ElementsMatch(t, []*promClient.LabelPair{ - {Name: ptr("a"), Value: ptr("b")}, - {Name: ptr("job"), Value: ptr("TestPrometheusCustomRegistry")}, - {Name: ptr("instance"), Value: ptr("my-instance-id")}, - }, mf.GetMetric()[0].GetLabel()) + require.ElementsMatch(t, append(globalDefaultAttrs, + &promClient.LabelPair{Name: ptr("a"), Value: ptr("b")}, + &promClient.LabelPair{Name: ptr("job"), Value: ptr("TestPrometheusCustomRegistry")}, + &promClient.LabelPair{Name: ptr("service_name"), Value: ptr("TestPrometheusCustomRegistry")}, + ), mf.GetMetric()[0].GetLabel()) require.EqualValues(t, ptr(7.0), mf.GetMetric()[0].GetCounter().Value) }) } diff --git a/stats/testdata/otel-collector-config.yaml b/stats/testdata/otel-collector-config.yaml index 9bad23b5..08742fa0 100644 --- a/stats/testdata/otel-collector-config.yaml +++ b/stats/testdata/otel-collector-config.yaml @@ -6,6 +6,8 @@ receivers: exporters: prometheus: endpoint: "0.0.0.0:8889" + resource_to_telemetry_conversion: + enabled: true const_labels: label1: value1