Skip to content

Conversation

Vperiodt
Copy link
Contributor

Initial Fix for #14321

Screenshot from 2025-04-12 18-45-46

@SuperQ SuperQ requested a review from machine424 April 23, 2025 20:57
@beorn7
Copy link
Member

beorn7 commented Apr 29, 2025

Also a possible solution for #13647 (maybe to be added in a separate PR).

@Vperiodt
Copy link
Contributor Author

Vperiodt commented May 8, 2025

Also a possible solution for #13647 (maybe to be added in a separate PR).

will address this in different PR.

@Vperiodt Vperiodt requested a review from machine424 May 13, 2025 16:10
@Vperiodt Vperiodt requested a review from machine424 May 21, 2025 10:40
@Vperiodt
Copy link
Contributor Author

Hello! @machine424 I hope this approach works now, I have altered the targetScraper instance within TestRequestHeaderTraceparent function because it must be configured with an http.Client that also uses otelhttp.NewTransport.

Copy link
Member

@machine424 machine424 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can still see the following issues:

  • the otelhttptrace.NewClientTrace setup is duplicated.
  • the test doesn't really test any logic from the source code, it does its own otelhttptrace.NewClientTrace setup.
  • withOTELGlobals is a little big complicated.

here is my suggestion (providing the diff would make everyone life easier)

  • put the setup in a newScrapeClient that we can really test.
  • use t.Cleanup to simplify the util.
  • other miscs
diff --git a/scrape/scrape.go b/scrape/scrape.go
index 02be3c519..85a09407d 100644
--- a/scrape/scrape.go
+++ b/scrape/scrape.go
@@ -149,16 +149,10 @@ func newScrapePool(cfg *config.ScrapeConfig, app storage.Appendable, offsetSeed
                logger = promslog.NewNopLogger()
        }
 
-       client, err := config_util.NewClientFromConfig(cfg.HTTPClientConfig, cfg.JobName, options.HTTPClientOptions...)
+       client, err := newScrapeClient(cfg.HTTPClientConfig, cfg.JobName, options.HTTPClientOptions...)
        if err != nil {
-               return nil, fmt.Errorf("error creating HTTP client: %w", err)
+               return nil, err
        }
-       t := client.Transport
-       client.Transport = otelhttp.NewTransport(
-               t,
-               otelhttp.WithClientTrace(func(ctx context.Context) *httptrace.ClientTrace {
-                       return otelhttptrace.NewClientTrace(ctx, otelhttptrace.WithoutSubSpans())
-               }))
        validationScheme, err := config.ToValidationScheme(cfg.MetricNameValidationScheme)
        if err != nil {
                return nil, fmt.Errorf("invalid metric name validation scheme: %w", err)
@@ -325,17 +319,11 @@ func (sp *scrapePool) reload(cfg *config.ScrapeConfig) error {
        sp.metrics.targetScrapePoolReloads.Inc()
        start := time.Now()
 
-       client, err := config_util.NewClientFromConfig(cfg.HTTPClientConfig, cfg.JobName, sp.httpOpts...)
+       client, err := newScrapeClient(cfg.HTTPClientConfig, cfg.JobName, sp.httpOpts...)
        if err != nil {
                sp.metrics.targetScrapePoolReloadsFailed.Inc()
-               return fmt.Errorf("error creating HTTP client: %w", err)
+               return err
        }
-       t := client.Transport
-       client.Transport = otelhttp.NewTransport(
-               t,
-               otelhttp.WithClientTrace(func(ctx context.Context) *httptrace.ClientTrace {
-                       return otelhttptrace.NewClientTrace(ctx, otelhttptrace.WithoutSubSpans())
-               }))
 
        reuseCache := reusableCache(sp.config, cfg)
        sp.config = cfg
@@ -2294,3 +2282,16 @@ func pickSchema(bucketFactor float64) int32 {
                return int32(floor)
        }
 }
+
+func newScrapeClient(cfg config_util.HTTPClientConfig, name string, optFuncs ...config_util.HTTPClientOption) (*http.Client, error) {
+       client, err := config_util.NewClientFromConfig(cfg, name, optFuncs...)
+       if err != nil {
+               return nil, fmt.Errorf("error creating HTTP client: %w", err)
+       }
+       client.Transport = otelhttp.NewTransport(
+               client.Transport,
+               otelhttp.WithClientTrace(func(ctx context.Context) *httptrace.ClientTrace {
+                       return otelhttptrace.NewClientTrace(ctx, otelhttptrace.WithoutSubSpans())
+               }))
+       return client, nil
+}
diff --git a/scrape/scrape_test.go b/scrape/scrape_test.go
index c01931626..fce7f3003 100644
--- a/scrape/scrape_test.go
+++ b/scrape/scrape_test.go
@@ -24,7 +24,6 @@ import (
        "math"
        "net/http"
        "net/http/httptest"
-       "net/http/httptrace"
        "net/url"
        "os"
        "sort"
@@ -38,14 +37,6 @@ import (
        "github.com/gogo/protobuf/proto"
        "github.com/google/go-cmp/cmp"
        "github.com/grafana/regexp"
-       "github.com/stretchr/testify/require"
-       "go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace"
-       "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
-       "go.opentelemetry.io/otel"
-       "go.opentelemetry.io/otel/propagation"
-       sdktrace "go.opentelemetry.io/otel/sdk/trace"
-       "go.opentelemetry.io/otel/trace"
-
        "github.com/prometheus/client_golang/prometheus"
        prom_testutil "github.com/prometheus/client_golang/prometheus/testutil"
        dto "github.com/prometheus/client_model/go"
@@ -53,6 +44,11 @@ import (
        "github.com/prometheus/common/expfmt"
        "github.com/prometheus/common/model"
        "github.com/prometheus/common/promslog"
+       "github.com/stretchr/testify/require"
+       "go.opentelemetry.io/otel"
+       "go.opentelemetry.io/otel/propagation"
+       sdktrace "go.opentelemetry.io/otel/sdk/trace"
+
        "github.com/prometheus/prometheus/config"
        "github.com/prometheus/prometheus/discovery"
        "github.com/prometheus/prometheus/discovery/targetgroup"
@@ -3119,63 +3115,55 @@ func TestAcceptHeader(t *testing.T) {
        }
 }
 
-// withOTELGlobals temporarily sets the global TracerProvider and Propagator
+// setupTracing temporarily sets the global TracerProvider and Propagator
 // and restores the original state after the test completes.
-func withOTELGlobals(t *testing.T, tp trace.TracerProvider, propagator propagation.TextMapPropagator, testFunc func()) {
+func setupTracing(t *testing.T) {
        t.Helper()
 
        origTracerProvider := otel.GetTracerProvider()
        origPropagator := otel.GetTextMapPropagator()
 
+       tp := sdktrace.NewTracerProvider(sdktrace.WithSampler(sdktrace.AlwaysSample()))
        otel.SetTracerProvider(tp)
-       otel.SetTextMapPropagator(propagator)
+       otel.SetTextMapPropagator(propagation.TraceContext{})
 
-       defer func() {
+       t.Cleanup(func() {
                otel.SetTracerProvider(origTracerProvider)
                otel.SetTextMapPropagator(origPropagator)
-       }()
-
-       testFunc()
+       })
 }
 
-// verifies that the HTTP client used by the target scraper propagates the OpenTelemetry "traceparent" header correctly.
-func TestRequestHeaderTraceparent(t *testing.T) {
-       tp := sdktrace.NewTracerProvider(sdktrace.WithSampler(sdktrace.AlwaysSample()))
-       withOTELGlobals(t, tp, propagation.TraceContext{}, func() {
-               server := httptest.NewServer(http.HandlerFunc(func(_ http.ResponseWriter, r *http.Request) {
-                       traceparent := r.Header.Get("traceparent")
-                       require.NotEmpty(t, traceparent)
-               }))
-               defer server.Close()
+// TestRequestTraceparentHeader verifies that the HTTP client used by the target scraper
+// propagates the OpenTelemetry "traceparent" header correctly.
+func TestRequestTraceparentHeader(t *testing.T) {
+       setupTracing(t)
 
-               serverURL, err := url.Parse(server.URL)
-               require.NoError(t, err)
+       server := httptest.NewServer(http.HandlerFunc(func(_ http.ResponseWriter, r *http.Request) {
+               // the traceparent header is sent.
+               require.NotEmpty(t, r.Header.Get("traceparent"))
+       }))
+       defer server.Close()
+       serverURL, err := url.Parse(server.URL)
+       require.NoError(t, err)
 
-               client := &http.Client{
-                       Transport: otelhttp.NewTransport(
-                               http.DefaultTransport,
-                               otelhttp.WithClientTrace(func(ctx context.Context) *httptrace.ClientTrace {
-                                       return otelhttptrace.NewClientTrace(ctx, otelhttptrace.WithoutSubSpans())
-                               }),
-                       ),
-               }
+       client, err := newScrapeClient(config_util.DefaultHTTPClientConfig, "test")
+       require.NoError(t, err)
 
-               ts := &targetScraper{
-                       Target: &Target{
-                               labels: labels.FromStrings(
-                                       model.SchemeLabel, serverURL.Scheme,
-                                       model.AddressLabel, serverURL.Host,
-                               ),
-                               scrapeConfig: &config.ScrapeConfig{},
-                       },
-                       client: client,
-               }
+       ts := &targetScraper{
+               Target: &Target{
+                       labels: labels.FromStrings(
+                               model.SchemeLabel, serverURL.Scheme,
+                               model.AddressLabel, serverURL.Host,
+                       ),
+                       scrapeConfig: &config.ScrapeConfig{},
+               },
+               client: client,
+       }
 
-               resp, err := ts.scrape(context.Background())
-               require.NoError(t, err)
-               require.NotNil(t, resp)
-               defer resp.Body.Close()
-       })
+       resp, err := ts.scrape(context.Background())
+       require.NoError(t, err)
+       require.NotNil(t, resp)
+       defer resp.Body.Close()
 }
 
 func TestTargetScraperScrapeOK(t *testing.T) {

Vperiodt added 4 commits July 8, 2025 13:23
Signed-off-by: Vanshikav123 <vanshikav928@gmail.com>
(cherry picked from commit 44a620d)
Signed-off-by: Vanshikav123 <vanshikav928@gmail.com>
Signed-off-by: Vanshikav123 <vanshikav928@gmail.com>
(cherry picked from commit 6e98a77)
Signed-off-by: Vanshikav123 <vanshikav928@gmail.com>
Signed-off-by: Vanshikav123 <vanshikav928@gmail.com>
(cherry picked from commit 97f288a)
Signed-off-by: Vanshikav123 <vanshikav928@gmail.com>
Signed-off-by: Vanshikav123 <vanshikav928@gmail.com>
(cherry picked from commit d5dd861)
Signed-off-by: Vanshikav123 <vanshikav928@gmail.com>
Vperiodt added 2 commits July 8, 2025 13:47
Signed-off-by: Vanshikav123 <vanshikav928@gmail.com>
(cherry picked from commit 3cd8092)
Signed-off-by: Vanshikav123 <vanshikav928@gmail.com>
Signed-off-by: Vanshikav123 <vanshikav928@gmail.com>
Signed-off-by: Vanshikav123 <vanshikav928@gmail.com>
@metalmatze metalmatze removed their request for review July 8, 2025 08:48
Signed-off-by: Vanshikav123 <vanshikav928@gmail.com>
@Nexucis Nexucis removed request for juliusv and Nexucis July 8, 2025 09:44
@Vperiodt Vperiodt requested a review from machine424 July 10, 2025 07:40
@machine424
Copy link
Member

could you elaborate on why the Scrape span via

	ctx, span := otel.Tracer("").Start(ctx, "Scrape", trace.WithSpanKind(trace.SpanKindClient))
	defer span.End()

was removed?

@Vperiodt
Copy link
Contributor Author

Vperiodt commented Jul 10, 2025

could you elaborate on why the Scrape span via

	ctx, span := otel.Tracer("").Start(ctx, "Scrape", trace.WithSpanKind(trace.SpanKindClient))
	defer span.End()

was removed?

while analyzing the code flow, i thought the core of the instrumentation is applied when the http.Client is first created for the scrapePool in the newScrapeClient function, the sync() method does not create a new client. Instead, it injects pre-instrumented client (sp.client) into each targetScraper instance? 🤔

@machine424
Copy link
Member

We want to add the parent/wrapping Scrape span as we do here

ctx, span := otel.Tracer("").Start(ctx, "Remote Store", trace.WithSpanKind(trace.SpanKindClient))
e.g.
To have more context about what the automatically created spans. We would get a GET /metrics span, and we wouldn't know what for.

Vperiodt and others added 2 commits July 11, 2025 23:57
Signed-off-by: Vanshikav123 <vanshikav928@gmail.com>
@Vperiodt
Copy link
Contributor Author

We want to add the parent/wrapping Scrape span as we do here

ctx, span := otel.Tracer("").Start(ctx, "Remote Store", trace.WithSpanKind(trace.SpanKindClient))

e.g.
To have more context about what the automatically created spans. We would get a GET /metrics span, and we wouldn't know what for.

oh okay ! review addressed , thank you.

Copy link
Member

@machine424 machine424 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm, thanks for this.

@machine424 machine424 merged commit 0fc5e75 into prometheus:main Jul 15, 2025
27 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants