Skip to content

Commit

Permalink
HTTPReporter: Adds b3:0 header and unit test for custom headers using…
Browse files Browse the repository at this point in the history
… HTTPDoer (#176)

* add b3:0 to http reporter and added test for custom headers using HTTPDoer

* Fix: Go < 1.14 does not support http.Header.Values()
  • Loading branch information
basvanbeek committed Sep 9, 2020
1 parent dc18516 commit f2e3397
Show file tree
Hide file tree
Showing 2 changed files with 105 additions and 0 deletions.
16 changes: 16 additions & 0 deletions reporter/http/http.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ type httpReporter struct {
reqCallback RequestCallbackFn
reqTimeout time.Duration
serializer reporter.SpanSerializer
doNotSample bool
}

// Send implements reporter
Expand Down Expand Up @@ -152,6 +153,9 @@ func (r *httpReporter) sendBatch() error {
r.logger.Printf("failed when creating the request: %s\n", err.Error())
return err
}
if r.doNotSample {
req.Header.Set("b3", "0")
}
req.Header.Set("Content-Type", r.serializer.ContentType())
if r.reqCallback != nil {
r.reqCallback(req)
Expand Down Expand Up @@ -237,6 +241,17 @@ func Serializer(serializer reporter.SpanSerializer) ReporterOption {
}
}

// AllowSamplingReporterCalls if set to true will remove the b3:0 header on
// outgoing calls to the Zipkin collector.
// By default we send b3:0 header to mitigate trace reporting amplification in
// service mesh environments where the sidecar proxies might trace the call
// we do here towards the Zipkin collector.
func AllowSamplingReporterCalls(allow bool) ReporterOption {
return func(r *httpReporter) {
r.doNotSample = !allow
}
}

// NewReporter returns a new HTTP Reporter.
// url should be the endpoint to send the spans to, e.g.
// http://localhost:9411/api/v2/spans
Expand All @@ -256,6 +271,7 @@ func NewReporter(url string, opts ...ReporterOption) reporter.Reporter {
batchMtx: &sync.Mutex{},
serializer: reporter.JSONSerializer{},
reqTimeout: defaultTimeout,
doNotSample: true,
}

for _, opt := range opts {
Expand Down
89 changes: 89 additions & 0 deletions reporter/http/http_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"io/ioutil"
"net/http"
"net/http/httptest"
"reflect"
"sync/atomic"
"testing"
"time"
Expand Down Expand Up @@ -167,3 +168,91 @@ func TestSpanIsReportedAfterBatchSize(t *testing.T) {
t.Errorf("unexpected number of spans received\nhave: %d, want: %d", aNumSpans, eNumSpans)
}
}

func TestSpanCustomHeaders(t *testing.T) {
serializer := reporter.JSONSerializer{}

hc := headerClient{
headers: http.Header{
"Key1": []string{"val1a", "val1b"},
"Key2": []string{"val2"},
},
}
var haveHeaders http.Header
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
haveHeaders = r.Header
}))
defer ts.Close()

spans := generateSpans(1)

rep := zipkinhttp.NewReporter(
ts.URL,
zipkinhttp.Serializer(serializer),
zipkinhttp.Client(hc),
)
for _, span := range spans {
rep.Send(*span)
}
rep.Close()

for _, key := range []string{"Key1", "Key2"} {
if want, have := hc.headers[key], haveHeaders[key]; !reflect.DeepEqual(want, have) {
t.Errorf("header %s: want: %v, have: %v\n", key, want, have)
}
}
}

func TestB3SamplingHeader(t *testing.T) {
serializer := reporter.JSONSerializer{}

var haveHeaders map[string][]string
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
haveHeaders = r.Header
}))
defer ts.Close()

spans := generateSpans(1)

rep := zipkinhttp.NewReporter(
ts.URL,
zipkinhttp.Serializer(serializer),
zipkinhttp.AllowSamplingReporterCalls(true),
)
for _, span := range spans {
rep.Send(*span)
}
rep.Close()

if len(haveHeaders["B3"]) > 0 {
t.Errorf("Expected B3 header to not exist, got %v", haveHeaders["B3"])
}

rep = zipkinhttp.NewReporter(
ts.URL,
zipkinhttp.Serializer(serializer),
)
for _, span := range spans {
rep.Send(*span)
}
rep.Close()

if want, have := []string{"0"}, haveHeaders["B3"]; !reflect.DeepEqual(want, have) {
t.Errorf("B3 header: want: %v, have %v", want, have)
}

}

type headerClient struct {
client http.Client
headers map[string][]string
}

func (h headerClient) Do(req *http.Request) (*http.Response, error) {
for key, item := range h.headers {
for _, val := range item {
req.Header.Add(key, val)
}
}
return h.client.Do(req)
}

0 comments on commit f2e3397

Please sign in to comment.