Skip to content
This repository has been archived by the owner on Jul 31, 2023. It is now read-only.

Commit

Permalink
#8 Jaeger propagator (#9)
Browse files Browse the repository at this point in the history
* Added jaeger uber-trace-id propagation implementation.

* Changed module name for testing purpose

* Switched order of bytes.

* Revert package name

* Fixed lint error

* Added header length checks in order to not process large invalid headers.
Added testcase for 128bit headers.
Fixed comments.
  • Loading branch information
Nighthawk22 authored and songy23 committed May 16, 2019
1 parent e8b5594 commit 540daef
Show file tree
Hide file tree
Showing 2 changed files with 170 additions and 0 deletions.
90 changes: 90 additions & 0 deletions propagation/http.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
// Copyright 2019, OpenCensus 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 propagation implement uber-trace-id header propagation used
// by Jaeger.
package propagation

import (
"encoding/hex"
"fmt"
"net/http"
"strconv"
"strings"

"go.opencensus.io/trace"
"go.opencensus.io/trace/propagation"
)

const (
httpHeader = `uber-trace-id`
maxLenHeader = 200
)

var _ propagation.HTTPFormat = (*HTTPFormat)(nil)

// HTTPFormat implements propagation.HTTPFormat to propagate
// traces in HTTP headers for Jaeger traces.
type HTTPFormat struct{}

// SpanContextFromRequest extracts a Jaeger Trace span context from incoming requests.
func (f *HTTPFormat) SpanContextFromRequest(req *http.Request) (sc trace.SpanContext, ok bool) {
h := req.Header.Get(httpHeader)

if h == "" || len(h) > maxLenHeader {
return trace.SpanContext{}, false
}

// Parse the trace id field.
traceHeaderParts := strings.Split(h, `:`)
if len(traceHeaderParts) != 4 {
return trace.SpanContext{}, false
}

traceID, err := hex.DecodeString(traceHeaderParts[0])
if err != nil {
return trace.SpanContext{}, false
}
if len(traceID) == 8 {
copy(sc.TraceID[8:16], traceID)
} else {
copy(sc.TraceID[:], traceID)
}

spanID, err := hex.DecodeString(traceHeaderParts[1])
if err != nil {
return trace.SpanContext{}, false
}
copy(sc.SpanID[:], spanID)

opt, err := strconv.Atoi(traceHeaderParts[3])

if err != nil {
return trace.SpanContext{}, false
}

sc.TraceOptions = trace.TraceOptions(opt)

return sc, true
}

// SpanContextToRequest modifies the given request to include a Jaeger Trace header.
func (f *HTTPFormat) SpanContextToRequest(sc trace.SpanContext, req *http.Request) {
header := fmt.Sprintf("%s:%s:%s:%d",
strings.Replace(sc.TraceID.String(), "0000000000000000", "", 1), //Replacing 0 if string is 8bit
sc.SpanID.String(),
"", //Parent span deprecated and will therefore be ignored.
int64(sc.TraceOptions))
req.Header.Set(httpHeader, header)
}
80 changes: 80 additions & 0 deletions propagation/http_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
// Copyright 2019, OpenCensus 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 propagation

import (
"net/http"
"reflect"
"testing"

"go.opencensus.io/trace"
)

func TestHTTPFormat(t *testing.T) {
format := &HTTPFormat{}
traceID := [16]byte{0, 0, 0, 0, 0, 0, 0, 0, 66, 179, 103, 245, 105, 105, 242, 156}
traceID2 := [16]byte{66, 179, 103, 245, 105, 105, 242, 156, 66, 179, 103, 245, 105, 105, 242, 156}
spanID1 := [8]byte{104, 185, 184, 89, 243, 185, 19, 51}
spanID2 := [8]byte{67, 211, 230, 84, 180, 39, 182, 139}
tests := []struct {
incoming string
wantSpanContext trace.SpanContext
}{
{
incoming: "42b367f56969f29c:68b9b859f3b91333::1",
wantSpanContext: trace.SpanContext{
TraceID: traceID,
SpanID: spanID1,
TraceOptions: 1,
},
},
{
incoming: "42b367f56969f29c:43d3e654b427b68b::0",
wantSpanContext: trace.SpanContext{
TraceID: traceID,
SpanID: spanID2,
TraceOptions: 0,
},
},
{
incoming: "42b367f56969f29c42b367f56969f29c:43d3e654b427b68b::0",
wantSpanContext: trace.SpanContext{
TraceID: traceID2,
SpanID: spanID2,
TraceOptions: 0,
},
},
}
for _, tt := range tests {
t.Run(tt.incoming, func(t *testing.T) {
req, _ := http.NewRequest("GET", "http://example.com", nil)
req.Header.Add(httpHeader, tt.incoming)
sc, ok := format.SpanContextFromRequest(req)
if !ok {
t.Errorf("exporter.SpanContextFromRequest() = false; want true")
}
if got, want := sc, tt.wantSpanContext; !reflect.DeepEqual(got, want) {
t.Errorf("exporter.SpanContextFromRequest() returned span context %v; want %v", got, want)
}

req, _ = http.NewRequest("GET", "http://example.com", nil)
format.SpanContextToRequest(sc, req)

if got, want := req.Header.Get(httpHeader), tt.incoming; got != want {
t.Errorf("exporter.SpanContextToRequest() returned header %q; want %q", got, want)
}
})
}
}

0 comments on commit 540daef

Please sign in to comment.