From 5cd1611cda7ea0de4b6af2caac4ab05762074e6d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20Paj=C4=85k?= Date: Tue, 13 May 2025 21:06:01 +0200 Subject: [PATCH] otlptracehttp: Add WithHTTPClient option (#6751) Follows https://github.com/open-telemetry/opentelemetry-go/pull/6688 Towards (for OTLP trace exporter): - https://github.com/open-telemetry/opentelemetry-go/issues/4536 - https://github.com/open-telemetry/opentelemetry-go/issues/5129 - https://github.com/open-telemetry/opentelemetry-go/issues/2632 --- CHANGELOG.md | 3 +- .../internal/otlpconfig/options.go | 11 +++++++- .../internal/otlpconfig/options_test.go | 18 ++++++++++++ .../otlp/otlptrace/otlptracehttp/client.go | 28 +++++++++++-------- .../internal/otlpconfig/options.go | 11 +++++++- .../internal/otlpconfig/options_test.go | 18 ++++++++++++ .../otlp/otlptrace/otlptracehttp/options.go | 16 +++++++++++ .../otlp/otlptrace/otlpconfig/options.go.tmpl | 11 +++++++- .../otlptrace/otlpconfig/options_test.go.tmpl | 18 ++++++++++++ 9 files changed, 118 insertions(+), 16 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9dfaecbdd..5ccf0dd62 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,8 +15,9 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm The package contains semantic conventions from the `v1.31.0` version of the OpenTelemetry Semantic Conventions. See the [migration documentation](./semconv/v1.31.0/MIGRATION.md) for information on how to upgrade from `go.opentelemetry.io/otel/semconv/v1.30.0`(#6479) - Add `Recording`, `Scope`, and `Record` types in `go.opentelemetry.io/otel/log/logtest`. (#6507) -- Add `WithHTTPClient` option to configure the `http.Client` used by `go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp`. (#6688) +- Add `WithHTTPClient` option to configure the `http.Client` used by `go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp`. (#6751) - Add `WithHTTPClient` option to configure the `http.Client` used by `go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp`. (#6752) +- Add `WithHTTPClient` option to configure the `http.Client` used by `go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp`. (#6688) ### Removed diff --git a/exporters/otlp/otlptrace/otlptracegrpc/internal/otlpconfig/options.go b/exporters/otlp/otlptrace/otlptracegrpc/internal/otlpconfig/options.go index 7c4c5479a..506ca00b6 100644 --- a/exporters/otlp/otlptrace/otlptracegrpc/internal/otlpconfig/options.go +++ b/exporters/otlp/otlptrace/otlptracegrpc/internal/otlpconfig/options.go @@ -53,7 +53,9 @@ type ( // gRPC configurations GRPCCredentials credentials.TransportCredentials - Proxy HTTPTransportProxyFunc + // HTTP configurations + Proxy HTTPTransportProxyFunc + HTTPClient *http.Client } Config struct { @@ -350,3 +352,10 @@ func WithProxy(pf HTTPTransportProxyFunc) GenericOption { return cfg }) } + +func WithHTTPClient(c *http.Client) GenericOption { + return newGenericOption(func(cfg Config) Config { + cfg.Traces.HTTPClient = c + return cfg + }) +} diff --git a/exporters/otlp/otlptrace/otlptracegrpc/internal/otlpconfig/options_test.go b/exporters/otlp/otlptrace/otlptracegrpc/internal/otlpconfig/options_test.go index b3f8bf35f..d49f9265f 100644 --- a/exporters/otlp/otlptrace/otlptracegrpc/internal/otlpconfig/options_test.go +++ b/exporters/otlp/otlptrace/otlptracegrpc/internal/otlpconfig/options_test.go @@ -483,6 +483,24 @@ func TestConfigs(t *testing.T) { assert.Nil(t, c.Traces.Proxy) }, }, + + // HTTP Client Tests + { + name: "Test With HTTP Client", + opts: []GenericOption{ + WithHTTPClient(http.DefaultClient), + }, + asserts: func(t *testing.T, c *Config, grpcOption bool) { + assert.Equal(t, http.DefaultClient, c.Traces.HTTPClient) + }, + }, + { + name: "Test Without HTTP Client", + opts: []GenericOption{}, + asserts: func(t *testing.T, c *Config, grpcOption bool) { + assert.Nil(t, c.Traces.HTTPClient) + }, + }, } for _, tt := range tests { diff --git a/exporters/otlp/otlptrace/otlptracehttp/client.go b/exporters/otlp/otlptrace/otlptracehttp/client.go index 3912d6203..583a8f867 100644 --- a/exporters/otlp/otlptrace/otlptracehttp/client.go +++ b/exporters/otlp/otlptrace/otlptracehttp/client.go @@ -71,20 +71,24 @@ var _ otlptrace.Client = (*client)(nil) func NewClient(opts ...Option) otlptrace.Client { cfg := otlpconfig.NewHTTPConfig(asHTTPOptions(opts)...) - httpClient := &http.Client{ - Transport: ourTransport, - Timeout: cfg.Traces.Timeout, - } + httpClient := cfg.Traces.HTTPClient - if cfg.Traces.TLSCfg != nil || cfg.Traces.Proxy != nil { - clonedTransport := ourTransport.Clone() - httpClient.Transport = clonedTransport - - if cfg.Traces.TLSCfg != nil { - clonedTransport.TLSClientConfig = cfg.Traces.TLSCfg + if httpClient == nil { + httpClient = &http.Client{ + Transport: ourTransport, + Timeout: cfg.Traces.Timeout, } - if cfg.Traces.Proxy != nil { - clonedTransport.Proxy = cfg.Traces.Proxy + + if cfg.Traces.TLSCfg != nil || cfg.Traces.Proxy != nil { + clonedTransport := ourTransport.Clone() + httpClient.Transport = clonedTransport + + if cfg.Traces.TLSCfg != nil { + clonedTransport.TLSClientConfig = cfg.Traces.TLSCfg + } + if cfg.Traces.Proxy != nil { + clonedTransport.Proxy = cfg.Traces.Proxy + } } } diff --git a/exporters/otlp/otlptrace/otlptracehttp/internal/otlpconfig/options.go b/exporters/otlp/otlptrace/otlptracehttp/internal/otlpconfig/options.go index db2a48158..c857db056 100644 --- a/exporters/otlp/otlptrace/otlptracehttp/internal/otlpconfig/options.go +++ b/exporters/otlp/otlptrace/otlptracehttp/internal/otlpconfig/options.go @@ -53,7 +53,9 @@ type ( // gRPC configurations GRPCCredentials credentials.TransportCredentials - Proxy HTTPTransportProxyFunc + // HTTP configurations + Proxy HTTPTransportProxyFunc + HTTPClient *http.Client } Config struct { @@ -350,3 +352,10 @@ func WithProxy(pf HTTPTransportProxyFunc) GenericOption { return cfg }) } + +func WithHTTPClient(c *http.Client) GenericOption { + return newGenericOption(func(cfg Config) Config { + cfg.Traces.HTTPClient = c + return cfg + }) +} diff --git a/exporters/otlp/otlptrace/otlptracehttp/internal/otlpconfig/options_test.go b/exporters/otlp/otlptrace/otlptracehttp/internal/otlpconfig/options_test.go index 411cf3026..8fb806819 100644 --- a/exporters/otlp/otlptrace/otlptracehttp/internal/otlpconfig/options_test.go +++ b/exporters/otlp/otlptrace/otlptracehttp/internal/otlpconfig/options_test.go @@ -483,6 +483,24 @@ func TestConfigs(t *testing.T) { assert.Nil(t, c.Traces.Proxy) }, }, + + // HTTP Client Tests + { + name: "Test With HTTP Client", + opts: []GenericOption{ + WithHTTPClient(http.DefaultClient), + }, + asserts: func(t *testing.T, c *Config, grpcOption bool) { + assert.Equal(t, http.DefaultClient, c.Traces.HTTPClient) + }, + }, + { + name: "Test Without HTTP Client", + opts: []GenericOption{}, + asserts: func(t *testing.T, c *Config, grpcOption bool) { + assert.Nil(t, c.Traces.HTTPClient) + }, + }, } for _, tt := range tests { diff --git a/exporters/otlp/otlptrace/otlptracehttp/options.go b/exporters/otlp/otlptrace/otlptracehttp/options.go index 3559c5664..cfe21dbfb 100644 --- a/exporters/otlp/otlptrace/otlptracehttp/options.go +++ b/exporters/otlp/otlptrace/otlptracehttp/options.go @@ -153,3 +153,19 @@ func WithRetry(rc RetryConfig) Option { func WithProxy(pf HTTPTransportProxyFunc) Option { return wrappedOption{otlpconfig.WithProxy(otlpconfig.HTTPTransportProxyFunc(pf))} } + +// WithHTTPClient sets the HTTP client to used by the exporter. +// +// This option will take precedence over [WithProxy], [WithTimeout], +// [WithTLSClientConfig] options as well as OTEL_EXPORTER_OTLP_CERTIFICATE, +// OTEL_EXPORTER_OTLP_TRACES_CERTIFICATE, OTEL_EXPORTER_OTLP_TIMEOUT, +// OTEL_EXPORTER_OTLP_TRACES_TIMEOUT environment variables. +// +// Timeout and all other fields of the passed [http.Client] are left intact. +// +// Be aware that passing an HTTP client with transport like +// [go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp.NewTransport] can +// cause the client to be instrumented twice and cause infinite recursion. +func WithHTTPClient(c *http.Client) Option { + return wrappedOption{otlpconfig.WithHTTPClient(c)} +} diff --git a/internal/shared/otlp/otlptrace/otlpconfig/options.go.tmpl b/internal/shared/otlp/otlptrace/otlpconfig/options.go.tmpl index 5081855db..e05a9782c 100644 --- a/internal/shared/otlp/otlptrace/otlpconfig/options.go.tmpl +++ b/internal/shared/otlp/otlptrace/otlpconfig/options.go.tmpl @@ -53,7 +53,9 @@ type ( // gRPC configurations GRPCCredentials credentials.TransportCredentials - Proxy HTTPTransportProxyFunc + // HTTP configurations + Proxy HTTPTransportProxyFunc + HTTPClient *http.Client } Config struct { @@ -350,3 +352,10 @@ func WithProxy(pf HTTPTransportProxyFunc) GenericOption { return cfg }) } + +func WithHTTPClient(c *http.Client) GenericOption { + return newGenericOption(func(cfg Config) Config { + cfg.Traces.HTTPClient = c + return cfg + }) +} diff --git a/internal/shared/otlp/otlptrace/otlpconfig/options_test.go.tmpl b/internal/shared/otlp/otlptrace/otlpconfig/options_test.go.tmpl index ec66907cc..e30c32f7a 100644 --- a/internal/shared/otlp/otlptrace/otlpconfig/options_test.go.tmpl +++ b/internal/shared/otlp/otlptrace/otlpconfig/options_test.go.tmpl @@ -483,6 +483,24 @@ func TestConfigs(t *testing.T) { assert.Nil(t, c.Traces.Proxy) }, }, + + // HTTP Client Tests + { + name: "Test With HTTP Client", + opts: []GenericOption{ + WithHTTPClient(http.DefaultClient), + }, + asserts: func(t *testing.T, c *Config, grpcOption bool) { + assert.Equal(t, http.DefaultClient, c.Traces.HTTPClient) + }, + }, + { + name: "Test Without HTTP Client", + opts: []GenericOption{}, + asserts: func(t *testing.T, c *Config, grpcOption bool) { + assert.Nil(t, c.Traces.HTTPClient) + }, + }, } for _, tt := range tests {