diff --git a/CHANGELOG.md b/CHANGELOG.md index 278a33a412b..96f6e2ddcc8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,6 +27,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm ### Fixed +- Fix `go.opentelemetry.io/contrib/otelconf` Prometheus reader converting OTel dot-style label names (e.g. `service.name`) to underscore-style (`service_name`) in `target_info` when both `without_type_suffix` and `without_units` are set. Use `NoTranslation` instead of `UnderscoreEscapingWithoutSuffixes` to preserve dot-style label names while still suppressing metric name suffixes. (#8763) - Limit the request body size at 1MB in `go.opentelemetry.io/contrib/zpages`. (#8656) - Fix server spans using the client's address and port for `server.address` and `server.port` attributes in `go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc`. (#8723) diff --git a/otelconf/v0.2.0/metric.go b/otelconf/v0.2.0/metric.go index 82ca13e0bd9..175949e937c 100644 --- a/otelconf/v0.2.0/metric.go +++ b/otelconf/v0.2.0/metric.go @@ -275,7 +275,11 @@ func prometheusReader(ctx context.Context, prometheusConfig *Prometheus) (sdkmet } if prometheusConfig.WithoutTypeSuffix != nil && *prometheusConfig.WithoutTypeSuffix && prometheusConfig.WithoutUnits != nil && *prometheusConfig.WithoutUnits { - opts = append(opts, otelprom.WithTranslationStrategy(otlptranslator.UnderscoreEscapingWithoutSuffixes)) + // NoTranslation preserves OTel dot-style label names (e.g. service.name) + // and suppresses metric name suffixes. The exporter's newConfig will + // automatically set withoutCounterSuffixes and withoutUnits when + // ShouldAddSuffixes() is false. + opts = append(opts, otelprom.WithTranslationStrategy(otlptranslator.NoTranslation)) } else { opts = append(opts, otelprom.WithTranslationStrategy(otlptranslator.NoUTF8EscapingWithSuffixes)) } diff --git a/otelconf/v0.2.0/metric_test.go b/otelconf/v0.2.0/metric_test.go index ac4e463a97e..678a6478e36 100644 --- a/otelconf/v0.2.0/metric_test.go +++ b/otelconf/v0.2.0/metric_test.go @@ -4,6 +4,7 @@ package otelconf import ( + "bytes" "context" "errors" "net/http" @@ -1241,6 +1242,47 @@ func TestPrometheusReaderConfigurationOptions(t *testing.T) { assert.Equal(t, http.StatusOK, resp.StatusCode) } +// TestPrometheusReaderDotStyleLabels verifies that OTel dot-style resource +// attribute names (e.g. service.name) are preserved in target_info when both +// without_type_suffix and without_units are set which is default config for OTel Collector. +func TestPrometheusReaderDotStyleLabels(t *testing.T) { + host := "localhost" + port := 0 + reader, err := prometheusReader(t.Context(), &Prometheus{ + Host: &host, + Port: &port, + WithoutTypeSuffix: ptr(true), + WithoutUnits: ptr(true), + }) + require.NoError(t, err) + + res := resource.NewWithAttributes("", attribute.String("service.name", "test-svc")) + mp := sdkmetric.NewMeterProvider(sdkmetric.WithReader(reader), sdkmetric.WithResource(res)) + t.Cleanup(func() { + //nolint:usetesting // required to avoid getting a canceled context at cleanup. + require.NoError(t, mp.Shutdown(context.Background())) + }) + c, err := mp.Meter("test").Int64Counter("test.counter") + require.NoError(t, err) + c.Add(t.Context(), 1) + + addr := reader.(readerWithServer).server.Addr + req, err := http.NewRequestWithContext(t.Context(), http.MethodGet, "http://"+addr+"/metrics", http.NoBody) + require.NoError(t, err) + req.Header.Set("Accept", "application/openmetrics-text; version=1.0.0; escaping=allow-utf-8") + resp, err := http.DefaultClient.Do(req) + require.NoError(t, err) + defer resp.Body.Close() + + var buf bytes.Buffer + _, err = buf.ReadFrom(resp.Body) + require.NoError(t, err) + body := buf.String() + + assert.Contains(t, body, `target_info{"service.name"="test-svc"}`) + assert.NotContains(t, body, "service_name") +} + func TestPrometheusReaderHostParsing(t *testing.T) { tests := []struct { name string diff --git a/otelconf/v0.3.0/metric.go b/otelconf/v0.3.0/metric.go index ca8059d3954..6233304c8be 100644 --- a/otelconf/v0.3.0/metric.go +++ b/otelconf/v0.3.0/metric.go @@ -387,7 +387,11 @@ func prometheusReaderOpts(prometheusConfig *Prometheus) ([]otelprom.Option, erro } if prometheusConfig.WithoutTypeSuffix != nil && *prometheusConfig.WithoutTypeSuffix && prometheusConfig.WithoutUnits != nil && *prometheusConfig.WithoutUnits { - opts = append(opts, otelprom.WithTranslationStrategy(otlptranslator.UnderscoreEscapingWithoutSuffixes)) + // NoTranslation preserves OTel dot-style label names (e.g. service.name) + // and suppresses metric name suffixes. The exporter's newConfig will + // automatically set withoutCounterSuffixes and withoutUnits when + // ShouldAddSuffixes() is false. + opts = append(opts, otelprom.WithTranslationStrategy(otlptranslator.NoTranslation)) } else { opts = append(opts, otelprom.WithTranslationStrategy(otlptranslator.NoUTF8EscapingWithSuffixes)) } diff --git a/otelconf/v0.3.0/metric_test.go b/otelconf/v0.3.0/metric_test.go index 9d7e52a37a8..93fa50c96e4 100644 --- a/otelconf/v0.3.0/metric_test.go +++ b/otelconf/v0.3.0/metric_test.go @@ -1594,6 +1594,48 @@ func TestPrometheusReaderConfigurationOptions(t *testing.T) { assert.Equal(t, http.StatusOK, resp.StatusCode) } +// TestPrometheusReaderDotStyleLabels verifies that OTel dot-style resource +// attribute names (e.g. service.name) are preserved in target_info when both +// without_type_suffix and without_units are set which is default config for OTel Collector. +func TestPrometheusReaderDotStyleLabels(t *testing.T) { + host := "localhost" + port := 0 + reader, err := prometheusReader(t.Context(), &Prometheus{ + Host: &host, + Port: &port, + WithoutTypeSuffix: ptr(true), + WithoutUnits: ptr(true), + }) + require.NoError(t, err) + + res := resource.NewWithAttributes("", attribute.String("service.name", "test-svc")) + mp := sdkmetric.NewMeterProvider(sdkmetric.WithReader(reader), sdkmetric.WithResource(res)) + t.Cleanup(func() { + //nolint:usetesting // required to avoid getting a canceled context at cleanup. + require.NoError(t, mp.Shutdown(context.Background())) + }) + c, err := mp.Meter("test").Int64Counter("test.counter") + require.NoError(t, err) + c.Add(t.Context(), 1) + + addr := reader.(readerWithServer).server.Addr + req, err := http.NewRequestWithContext(t.Context(), http.MethodGet, "http://"+addr+"/metrics", http.NoBody) + require.NoError(t, err) + req.Header.Set("Accept", "application/openmetrics-text; version=1.0.0; escaping=allow-utf-8") + resp, err := http.DefaultClient.Do(req) + require.NoError(t, err) + defer resp.Body.Close() + + var buf bytes.Buffer + _, err = buf.ReadFrom(resp.Body) + require.NoError(t, err) + body := buf.String() + + assert.Contains(t, body, `target_info{"service.name"="test-svc"}`) + assert.NotContains(t, body, "service_name") + assert.NotContains(t, body, "target.info") +} + func Test_otlpGRPCMetricExporter(t *testing.T) { if runtime.GOOS == "windows" || runtime.GOOS == "darwin" { // TODO (#8115): Fix the flakiness on Windows and MacOS.