mirror of
https://github.com/open-telemetry/opentelemetry-go.git
synced 2025-01-12 02:28:07 +02:00
Add User-Agent header to OTLP exporter requests (#3261)
* Add User-Agent header to OTLP exporter requests * allow override grpc user-agent Signed-off-by: rogerogers <rogers@rogerogers.com>
This commit is contained in:
parent
c5ebbc4d4b
commit
4ec2ae60f2
@ -11,6 +11,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
|
||||
### Added
|
||||
|
||||
- Added an example of using metric views to customize instruments. (#3177)
|
||||
- Add default User-Agent header to OTLP exporter requests (`go.opentelemetry.io/otel/exporters/otlpmetric/otlpmetricgrpc`, `go.opentelemetry.io/otel/exporters/otlpmetric/otlpmetrichttp`, `go.opentelemetry.io/otel/exporters/otlptrace/otlptracegrpc` and `go.opentelemetry.io/otel/exporters/otlptrace/otlptracehttp`). (#3261)
|
||||
|
||||
### Changed
|
||||
|
||||
|
24
exporters/otlp/internal/header.go
Normal file
24
exporters/otlp/internal/header.go
Normal file
@ -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()
|
||||
}
|
26
exporters/otlp/internal/header_test.go
Normal file
26
exporters/otlp/internal/header_test.go
Normal file
@ -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())
|
||||
}
|
@ -104,6 +104,7 @@ func NewGRPCConfig(opts ...GRPCOption) Config {
|
||||
Timeout: DefaultTimeout,
|
||||
},
|
||||
RetryConfig: retry.DefaultConfig,
|
||||
DialOptions: []grpc.DialOption{grpc.WithUserAgent(internal.GetUserAgentHeader())},
|
||||
}
|
||||
cfg = ApplyGRPCEnvConfigs(cfg)
|
||||
for _, opt := range opts {
|
||||
|
@ -22,6 +22,7 @@ import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"google.golang.org/genproto/googleapis/rpc/errdetails"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
"google.golang.org/protobuf/types/known/durationpb"
|
||||
@ -169,6 +170,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]})
|
||||
})
|
||||
@ -188,4 +190,18 @@ func TestConfig(t *testing.T) {
|
||||
err := exp.Export(ctx, metricdata.ResourceMetrics{})
|
||||
assert.ErrorContains(t, err, context.DeadlineExceeded.Error())
|
||||
})
|
||||
|
||||
t.Run("WithCustomUserAgent", func(t *testing.T) {
|
||||
key := "user-agent"
|
||||
customerUserAgent := "custom-user-agent"
|
||||
exp, coll := factoryFunc(nil, WithDialOption(grpc.WithUserAgent(customerUserAgent)))
|
||||
t.Cleanup(coll.Shutdown)
|
||||
ctx := context.Background()
|
||||
require.NoError(t, exp.Export(ctx, metricdata.ResourceMetrics{}))
|
||||
// Ensure everything is flushed.
|
||||
require.NoError(t, exp.Shutdown(ctx))
|
||||
|
||||
got := coll.Headers()
|
||||
assert.Contains(t, got[key][0], customerUserAgent)
|
||||
})
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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]})
|
||||
})
|
||||
@ -161,4 +162,19 @@ func TestConfig(t *testing.T) {
|
||||
assert.NoError(t, exp.Export(ctx, metricdata.ResourceMetrics{}))
|
||||
assert.Len(t, coll.Collect().Dump(), 1)
|
||||
})
|
||||
|
||||
t.Run("WithCustomUserAgent", func(t *testing.T) {
|
||||
key := http.CanonicalHeaderKey("user-agent")
|
||||
headers := map[string]string{key: "custom-user-agent"}
|
||||
exp, coll := factoryFunc("", nil, WithHeaders(headers))
|
||||
ctx := context.Background()
|
||||
t.Cleanup(func() { require.NoError(t, coll.Shutdown(ctx)) })
|
||||
require.NoError(t, exp.Export(ctx, metricdata.ResourceMetrics{}))
|
||||
// Ensure everything is flushed.
|
||||
require.NoError(t, exp.Shutdown(ctx))
|
||||
|
||||
got := coll.Headers()
|
||||
require.Contains(t, got, key)
|
||||
assert.Equal(t, got[key], []string{headers[key]})
|
||||
})
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ go 1.18
|
||||
|
||||
require (
|
||||
github.com/stretchr/testify v1.7.1
|
||||
go.opentelemetry.io/otel v1.10.0
|
||||
go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.10.0
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric v0.32.1
|
||||
go.opentelemetry.io/otel/metric v0.32.1
|
||||
@ -21,7 +22,6 @@ require (
|
||||
github.com/google/go-cmp v0.5.8 // indirect
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
go.opentelemetry.io/otel v1.10.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk v1.10.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.10.0 // indirect
|
||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4 // indirect
|
||||
|
@ -97,6 +97,7 @@ func NewGRPCConfig(opts ...GRPCOption) Config {
|
||||
Timeout: DefaultTimeout,
|
||||
},
|
||||
RetryConfig: retry.DefaultConfig,
|
||||
DialOptions: []grpc.DialOption{grpc.WithUserAgent(internal.GetUserAgentHeader())},
|
||||
}
|
||||
cfg = ApplyGRPCEnvConfigs(cfg)
|
||||
for _, opt := range opts {
|
||||
|
@ -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])
|
||||
}
|
||||
@ -411,3 +412,18 @@ func TestPartialSuccess(t *testing.T) {
|
||||
require.Contains(t, errors[0].Error(), "partially successful")
|
||||
require.Contains(t, errors[0].Error(), "2 spans rejected")
|
||||
}
|
||||
|
||||
func TestCustomUserAgent(t *testing.T) {
|
||||
customUserAgent := "custom-user-agent"
|
||||
mc := runMockCollector(t)
|
||||
t.Cleanup(func() { require.NoError(t, mc.stop()) })
|
||||
|
||||
ctx := context.Background()
|
||||
exp := newGRPCExporter(t, ctx, mc.endpoint,
|
||||
otlptracegrpc.WithDialOption(grpc.WithUserAgent(customUserAgent)))
|
||||
t.Cleanup(func() { require.NoError(t, exp.Shutdown(ctx)) })
|
||||
require.NoError(t, exp.ExportSpans(ctx, roSpans))
|
||||
|
||||
headers := mc.getHeaders()
|
||||
require.Contains(t, headers.Get("user-agent")[0], customUserAgent)
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -42,6 +42,10 @@ var (
|
||||
"Otel-Go-Key-1": "somevalue",
|
||||
"Otel-Go-Key-2": "someothervalue",
|
||||
}
|
||||
|
||||
customUserAgentHeader = map[string]string{
|
||||
"user-agent": "custome-user-agent",
|
||||
}
|
||||
)
|
||||
|
||||
func TestEndToEnd(t *testing.T) {
|
||||
@ -142,6 +146,15 @@ func TestEndToEnd(t *testing.T) {
|
||||
ExpectedHeaders: testHeaders,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "with custom user agent",
|
||||
opts: []otlptracehttp.Option{
|
||||
otlptracehttp.WithHeaders(customUserAgentHeader),
|
||||
},
|
||||
mcCfg: mockCollectorConfig{
|
||||
ExpectedHeaders: customUserAgentHeader,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
|
Loading…
Reference in New Issue
Block a user