From f0d79dda8a66f076445b8b078e0d34c89960257c Mon Sep 17 00:00:00 2001 From: rogerogers Date: Thu, 6 Oct 2022 14:42:29 +0800 Subject: [PATCH] Add User-Agent header to OTLP exporter requests Signed-off-by: rogerogers --- exporters/otlp/internal/header.go | 24 +++++++++++++++++ exporters/otlp/internal/header_test.go | 26 +++++++++++++++++++ .../otlp/otlpmetric/otlpmetricgrpc/client.go | 3 +++ .../otlpmetric/otlpmetricgrpc/client_test.go | 1 + .../otlp/otlpmetric/otlpmetrichttp/client.go | 3 +++ .../otlpmetric/otlpmetrichttp/client_test.go | 1 + .../otlp/otlptrace/otlptracegrpc/client.go | 2 ++ .../otlptrace/otlptracegrpc/client_test.go | 1 + .../otlp/otlptrace/otlptracehttp/client.go | 2 ++ 9 files changed, 63 insertions(+) create mode 100644 exporters/otlp/internal/header.go create mode 100644 exporters/otlp/internal/header_test.go diff --git a/exporters/otlp/internal/header.go b/exporters/otlp/internal/header.go new file mode 100644 index 000000000000..9aa62ed9e8e9 --- /dev/null +++ b/exporters/otlp/internal/header.go @@ -0,0 +1,24 @@ +// 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 internal contains common functionality for all OTLP exporters. +package internal // import "go.opentelemetry.io/otel/exporters/otlp/internal" + +import "go.opentelemetry.io/otel" + +// GetUserAgentHeader return an OTLP header value form "OTel OTLP Exporter Go/{{ .Version }}" +// https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/protocol/exporter.md#user-agent +func GetUserAgentHeader() string { + return "OTel OTLP Exporter Go/" + otel.Version() +} diff --git a/exporters/otlp/internal/header_test.go b/exporters/otlp/internal/header_test.go new file mode 100644 index 000000000000..ecca1a9490e7 --- /dev/null +++ b/exporters/otlp/internal/header_test.go @@ -0,0 +1,26 @@ +// 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 internal contains common functionality for all OTLP exporters. +package internal // import "go.opentelemetry.io/otel/exporters/otlp/internal" + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestGetUserAgentHeader(t *testing.T) { + require.Regexp(t, "OTel OTLP Exporter Go/1\\..*", GetUserAgentHeader()) +} diff --git a/exporters/otlp/otlpmetric/otlpmetricgrpc/client.go b/exporters/otlp/otlpmetric/otlpmetricgrpc/client.go index 4c5beb8f384d..d2f4be38bce7 100644 --- a/exporters/otlp/otlpmetric/otlpmetricgrpc/client.go +++ b/exporters/otlp/otlpmetric/otlpmetricgrpc/client.go @@ -24,6 +24,7 @@ import ( "google.golang.org/grpc/metadata" "google.golang.org/grpc/status" + "go.opentelemetry.io/otel/exporters/otlp/internal" "go.opentelemetry.io/otel/exporters/otlp/internal/retry" "go.opentelemetry.io/otel/exporters/otlp/otlpmetric" "go.opentelemetry.io/otel/exporters/otlp/otlpmetric/internal/oconf" @@ -77,6 +78,8 @@ func newClient(ctx context.Context, options ...Option) (otlpmetric.Client, error } if c.conn == nil { + // Add a User-Agent header when no ClientConn was provided + cfg.DialOptions = append(cfg.DialOptions, grpc.WithUserAgent(internal.GetUserAgentHeader())) // If the caller did not provide a ClientConn when the client was // created, create one using the configuration they did provide. conn, err := grpc.DialContext(ctx, cfg.Metrics.Endpoint, cfg.DialOptions...) diff --git a/exporters/otlp/otlpmetric/otlpmetricgrpc/client_test.go b/exporters/otlp/otlpmetric/otlpmetricgrpc/client_test.go index 1cb8cd815e36..7d949a16c4ec 100644 --- a/exporters/otlp/otlpmetric/otlpmetricgrpc/client_test.go +++ b/exporters/otlp/otlpmetric/otlpmetricgrpc/client_test.go @@ -169,6 +169,7 @@ func TestConfig(t *testing.T) { require.NoError(t, exp.Shutdown(ctx)) got := coll.Headers() + require.Regexp(t, "OTel OTLP Exporter Go/1\\..*", got) require.Contains(t, got, key) assert.Equal(t, got[key], []string{headers[key]}) }) diff --git a/exporters/otlp/otlpmetric/otlpmetrichttp/client.go b/exporters/otlp/otlpmetric/otlpmetrichttp/client.go index 7a8d7e147071..0840a2c8fa61 100644 --- a/exporters/otlp/otlpmetric/otlpmetrichttp/client.go +++ b/exporters/otlp/otlpmetric/otlpmetrichttp/client.go @@ -29,6 +29,7 @@ import ( "google.golang.org/protobuf/proto" + "go.opentelemetry.io/otel/exporters/otlp/internal" "go.opentelemetry.io/otel/exporters/otlp/internal/retry" "go.opentelemetry.io/otel/exporters/otlp/otlpmetric" "go.opentelemetry.io/otel/exporters/otlp/otlpmetric/internal/oconf" @@ -101,6 +102,8 @@ func newClient(opts ...Option) (otlpmetric.Client, error) { return nil, err } + req.Header.Set("User-Agent", internal.GetUserAgentHeader()) + if n := len(cfg.Metrics.Headers); n > 0 { for k, v := range cfg.Metrics.Headers { req.Header.Set(k, v) diff --git a/exporters/otlp/otlpmetric/otlpmetrichttp/client_test.go b/exporters/otlp/otlpmetric/otlpmetrichttp/client_test.go index 22740252b024..bc78656bbdad 100644 --- a/exporters/otlp/otlpmetric/otlpmetrichttp/client_test.go +++ b/exporters/otlp/otlpmetric/otlpmetrichttp/client_test.go @@ -75,6 +75,7 @@ func TestConfig(t *testing.T) { require.NoError(t, exp.Shutdown(ctx)) got := coll.Headers() + require.Regexp(t, "OTel OTLP Exporter Go/1\\..*", got) require.Contains(t, got, key) assert.Equal(t, got[key], []string{headers[key]}) }) diff --git a/exporters/otlp/otlptrace/otlptracegrpc/client.go b/exporters/otlp/otlptrace/otlptracegrpc/client.go index 9d6e1898b14d..0eb3cd29c66c 100644 --- a/exporters/otlp/otlptrace/otlptracegrpc/client.go +++ b/exporters/otlp/otlptrace/otlptracegrpc/client.go @@ -91,6 +91,8 @@ func newClient(opts ...Option) *client { // Start establishes a gRPC connection to the collector. func (c *client) Start(ctx context.Context) error { if c.conn == nil { + // Add a User-Agent header when no ClientConn was provided + c.dialOpts = append(c.dialOpts, grpc.WithUserAgent(internal.GetUserAgentHeader())) // If the caller did not provide a ClientConn when the client was // created, create one using the configuration they did provide. conn, err := grpc.DialContext(ctx, c.endpoint, c.dialOpts...) diff --git a/exporters/otlp/otlptrace/otlptracegrpc/client_test.go b/exporters/otlp/otlptrace/otlptracegrpc/client_test.go index d11111ed126c..063c4a987b5d 100644 --- a/exporters/otlp/otlptrace/otlptracegrpc/client_test.go +++ b/exporters/otlp/otlptrace/otlptracegrpc/client_test.go @@ -212,6 +212,7 @@ func TestNewWithHeaders(t *testing.T) { require.NoError(t, exp.ExportSpans(ctx, roSpans)) headers := mc.getHeaders() + require.Regexp(t, "OTel OTLP Exporter Go/1\\..*", headers.Get("user-agent")) require.Len(t, headers.Get("header1"), 1) assert.Equal(t, "value1", headers.Get("header1")[0]) } diff --git a/exporters/otlp/otlptrace/otlptracehttp/client.go b/exporters/otlp/otlptrace/otlptracehttp/client.go index 745b6541d422..8f742dfc1bd3 100644 --- a/exporters/otlp/otlptrace/otlptracehttp/client.go +++ b/exporters/otlp/otlptrace/otlptracehttp/client.go @@ -208,6 +208,8 @@ func (d *client) newRequest(body []byte) (request, error) { return request{Request: r}, err } + r.Header.Set("User-Agent", internal.GetUserAgentHeader()) + for k, v := range d.cfg.Headers { r.Header.Set(k, v) }