Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add WithHeaders option for Zipkin exporter #5530

Merged
merged 12 commits into from
Jun 26, 2024
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
- Store and provide the emitted `context.Context` in `ScopeRecords` of `go.opentelemetry.io/otel/sdk/log/logtest`. (#5468)
- `SimpleProcessor.OnEmit` in `go.opentelemetry.io/otel/sdk/log` no longer allocates a slice which makes it possible to have a zero-allocation log processing using `SimpleProcessor`. (#5493)
- The `AssertRecordEqual` method to `go.opentelemetry.io/otel/log/logtest` to allow comparison of two log records in tests. (#5499)
- The `WithHeaders` option to `go.opentelemetry.io/otel/exporters/zipkin` to allow configuring custom http headers while exporting spans. (#5530)

### Changed

Expand Down
36 changes: 28 additions & 8 deletions exporters/zipkin/zipkin.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,10 @@ const (

// Exporter exports spans to the zipkin collector.
type Exporter struct {
url string
client *http.Client
logger logr.Logger
url string
client *http.Client
logger logr.Logger
headers map[string]string

stoppedMu sync.RWMutex
stopped bool
Expand All @@ -40,8 +41,9 @@ var emptyLogger = logr.Logger{}

// Options contains configuration for the exporter.
type config struct {
client *http.Client
logger logr.Logger
client *http.Client
logger logr.Logger
headers map[string]string
}

// Option defines a function that configures the exporter.
Expand Down Expand Up @@ -70,6 +72,14 @@ func WithLogr(logger logr.Logger) Option {
})
}

// WithHeaders configures the exporter to use the passed http request headers.
srijan-27 marked this conversation as resolved.
Show resolved Hide resolved
func WithHeaders(headers map[string]string) Option {
return optionFunc(func(cfg config) config {
cfg.headers = headers
return cfg
})
}

// WithClient configures the exporter to use the passed HTTP client.
func WithClient(client *http.Client) Option {
return optionFunc(func(cfg config) config {
Expand Down Expand Up @@ -101,9 +111,10 @@ func New(collectorURL string, opts ...Option) (*Exporter, error) {
cfg.client = http.DefaultClient
}
return &Exporter{
url: collectorURL,
client: cfg.client,
logger: cfg.logger,
url: collectorURL,
client: cfg.client,
logger: cfg.logger,
headers: cfg.headers,
}, nil
}

Expand Down Expand Up @@ -132,6 +143,15 @@ func (e *Exporter) ExportSpans(ctx context.Context, spans []sdktrace.ReadOnlySpa
return e.errf("failed to create request to %s: %v", e.url, err)
}
req.Header.Set("Content-Type", "application/json")

for k, v := range e.headers {
if k == "host" {
srijan-27 marked this conversation as resolved.
Show resolved Hide resolved
req.Host = v
} else {
req.Header.Set(k, v)
}
}

resp, err := e.client.Do(req)
if err != nil {
return e.errf("request to %s failed: %v", e.url, err)
Expand Down
38 changes: 38 additions & 0 deletions exporters/zipkin/zipkin_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"log"
"net"
"net/http"
"net/http/httptest"
"sync"
"testing"
"time"
Expand Down Expand Up @@ -375,3 +376,40 @@ func TestLogrFormatting(t *testing.T) {
got := buf.String()
assert.Equal(t, want, got)
}

func TestWithHeaders(t *testing.T) {
srijan-27 marked this conversation as resolved.
Show resolved Hide resolved
headers := map[string]string{
"name1": "value1",
"name2": "value2",
"host": "example",
}

exp, err := New("", WithHeaders(headers))
require.NoError(t, err)

want := headers
got := exp.headers
assert.Equal(t, want, got)

ch := make(chan *http.Request, 1)
handler := func(w http.ResponseWriter, r *http.Request) {
ch <- r
w.WriteHeader(http.StatusOK)
}

srv := httptest.NewServer(http.HandlerFunc(handler))
defer srv.Close()

e := &Exporter{
url: srv.URL,
client: srv.Client(),
headers: headers,
}

_ = e.ExportSpans(context.Background(), tracetest.SpanStubs{}.Snapshots())
req := <-ch
srijan-27 marked this conversation as resolved.
Show resolved Hide resolved

assert.Equal(t, headers["host"], req.Host)
assert.Equal(t, headers["name1"], req.Header.Get("name1"))
assert.Equal(t, headers["name2"], req.Header.Get("name2"))
}
Loading