mirror of
https://github.com/open-telemetry/opentelemetry-go.git
synced 2024-12-24 20:14:40 +02:00
Add propagator interface and W3C propagator (#85)
* add propagation api. * add http propagator interface and w3c propagator implementation. * remove Extract api from trace. * remove Extract interface for tracer. * fix copyright. * fix variable names and comments. * move inject/extract out of trace. * replace INVALID_SPAN_CONTEXT with EmptySpanContext function. * fix tag.Map. * make carrier as interface instead of http.Request. * rename structs and update doc comments.. * add doc.go * update doc. * add noop propagator. * add new propagation api with Supplier interface. - added Default Tracer which simply propagates SpanContext. - added CopyOfRemote option to simply create remote span. * remove old propagator. * rename propagator to TextFormatPropagator. * rename default tracer/span as pass_through tracer/span. * add test for pass through tracer. * add missing interface to pass through tracer. * return SpanContext instead of contex.Context from Extract interface. - also remove PassThroughTracer * fix review comments. * add more test cases for traceContext extraction. * remove tidy temporarily from circle-ci target to avoid build failure. * allow header ending in dash '-'. * add inject test for non-zero value other than 01 for traceoption * add AddLink and Link interface to MockSpan * fix running go mod tidy on every build.
This commit is contained in:
parent
b13362e37c
commit
83935b2558
16
api/propagation/doc.go
Normal file
16
api/propagation/doc.go
Normal file
@ -0,0 +1,16 @@
|
||||
// Copyright 2019, 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 propagation contains interface definition for Binary and TextFormat propagators.
|
||||
package propagation // import "go.opentelemetry.io/api/propagation"
|
40
api/propagation/noop_propagator.go
Normal file
40
api/propagation/noop_propagator.go
Normal file
@ -0,0 +1,40 @@
|
||||
// Copyright 2019, 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 propagation
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"go.opentelemetry.io/api/core"
|
||||
)
|
||||
|
||||
// NoopTextFormatPropagator implements TextFormatPropagator that does nothing.
|
||||
type NoopTextFormatPropagator struct{}
|
||||
|
||||
var _ TextFormatPropagator = NoopTextFormatPropagator{}
|
||||
|
||||
// Inject does nothing.
|
||||
func (np NoopTextFormatPropagator) Inject(ctx context.Context, supplier Supplier) {
|
||||
}
|
||||
|
||||
// Extract does nothing and returns an empty SpanContext
|
||||
func (np NoopTextFormatPropagator) Extract(ctx context.Context, supplier Supplier) core.SpanContext {
|
||||
return core.EmptySpanContext()
|
||||
}
|
||||
|
||||
// GetAllKeys returns empty list of strings.
|
||||
func (np NoopTextFormatPropagator) GetAllKeys() []string {
|
||||
return []string{}
|
||||
}
|
52
api/propagation/propagator.go
Normal file
52
api/propagation/propagator.go
Normal file
@ -0,0 +1,52 @@
|
||||
// Copyright 2019, 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 propagation
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"go.opentelemetry.io/api/core"
|
||||
)
|
||||
|
||||
// TextFormatPropagator is an interface that specifies methods to inject and extract SpanContext
|
||||
// into/from a carrier using Supplier interface.
|
||||
// For example, HTTP Trace Context propagator would encode SpanContext into W3C Trace
|
||||
// Context Header and set the header into HttpRequest.
|
||||
type TextFormatPropagator interface {
|
||||
// Inject method retrieves current SpanContext from the ctx, encodes it into propagator
|
||||
// specific format and then injects the encoded SpanContext using supplier into a carrier
|
||||
// associated with the supplier.
|
||||
Inject(ctx context.Context, supplier Supplier)
|
||||
|
||||
// Extract method retrieves encoded SpanContext using supplier from the associated carrier.
|
||||
// It decodes the SpanContext and returns it. If no SpanContext was retrieved OR
|
||||
// if the retrieved SpanContext is invalid then an empty SpanContext is returned.
|
||||
Extract(ctx context.Context, supplier Supplier) core.SpanContext
|
||||
|
||||
// GetAllKeys returns all the keys that this propagator injects/extracts into/from a
|
||||
// carrier. The use cases for this are
|
||||
// * allow pre-allocation of fields, especially in systems like gRPC Metadata
|
||||
// * allow a single-pass over an iterator (ex OpenTracing has no getter in TextMap)
|
||||
GetAllKeys() []string
|
||||
}
|
||||
|
||||
// Supplier is an interface that specifies methods to retrieve and store
|
||||
// value for a key to an associated carrier.
|
||||
// Get method retrieves the value for a given key.
|
||||
// Set method stores the value for a given key.
|
||||
type Supplier interface {
|
||||
Get(key string) string
|
||||
Set(key string, value string)
|
||||
}
|
@ -44,9 +44,6 @@ type Tracer interface {
|
||||
|
||||
// WithResources attaches resource attributes to the Tracer.
|
||||
WithResources(res ...core.KeyValue) Tracer
|
||||
|
||||
// Note: see https://github.com/opentracing/opentracing-go/issues/127
|
||||
Inject(context.Context, Span, Injector)
|
||||
}
|
||||
|
||||
type FinishOptions struct {
|
||||
@ -102,13 +99,6 @@ type Span interface {
|
||||
ModifyAttributes(...tag.Mutator)
|
||||
}
|
||||
|
||||
type Injector interface {
|
||||
// Inject serializes span context and tag.Map and inserts them in to
|
||||
// carrier associated with the injector. For example in case of http request,
|
||||
// span context could added to the request (carrier) as W3C Trace context header.
|
||||
Inject(core.SpanContext, tag.Map)
|
||||
}
|
||||
|
||||
// SpanOption apply changes to SpanOptions.
|
||||
type SpanOption func(*SpanOptions)
|
||||
|
||||
@ -157,18 +147,6 @@ func Start(ctx context.Context, name string, opts ...SpanOption) (context.Contex
|
||||
return GlobalTracer().Start(ctx, name, opts...)
|
||||
}
|
||||
|
||||
// Inject is convenient function to inject current span context using injector.
|
||||
// Injector is expected to serialize span context and inject it in to a carrier.
|
||||
// An example of a carrier is http request.
|
||||
func Inject(ctx context.Context, injector Injector) {
|
||||
span := CurrentSpan(ctx)
|
||||
if span == nil {
|
||||
return
|
||||
}
|
||||
|
||||
span.Tracer().Inject(ctx, span, injector)
|
||||
}
|
||||
|
||||
// WithStartTime sets the start time of the span to provided time t, when it is started.
|
||||
// In absensce of this option, wall clock time is used as start time.
|
||||
// This option is typically used when starting of the span is delayed.
|
||||
|
@ -49,7 +49,3 @@ func (NoopTracer) Start(ctx context.Context, name string, opts ...SpanOption) (c
|
||||
span := NoopSpan{}
|
||||
return SetCurrentSpan(ctx, span), span
|
||||
}
|
||||
|
||||
// Inject does nothing.
|
||||
func (NoopTracer) Inject(ctx context.Context, span Span, injector Injector) {
|
||||
}
|
||||
|
@ -77,7 +77,6 @@ github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgo
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/lightstep/tracecontext.go v0.0.0-20181129014701-1757c391b1ac/go.mod h1:Frd2bnT3w5FB5q49ENTfVlztJES+1k/7lyWX2+9gq/M=
|
||||
github.com/logrusorgru/aurora v0.0.0-20181002194514-a7b3b318ed4e/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4=
|
||||
github.com/magiconair/properties v1.7.6/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||
|
@ -17,6 +17,7 @@ package main
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"go.opentelemetry.io/plugin/httptrace"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
|
||||
@ -25,7 +26,6 @@ import (
|
||||
"go.opentelemetry.io/api/key"
|
||||
"go.opentelemetry.io/api/tag"
|
||||
"go.opentelemetry.io/api/trace"
|
||||
"go.opentelemetry.io/plugin/httptrace"
|
||||
)
|
||||
|
||||
var (
|
||||
@ -50,9 +50,8 @@ func main() {
|
||||
func(ctx context.Context) error {
|
||||
req, _ := http.NewRequest("GET", "http://localhost:7777/hello", nil)
|
||||
|
||||
ctx, req, inj := httptrace.W3C(ctx, req)
|
||||
|
||||
trace.Inject(ctx, inj)
|
||||
ctx, req = httptrace.W3C(ctx, req)
|
||||
httptrace.Inject(ctx, req)
|
||||
|
||||
res, err := client.Do(req)
|
||||
if err != nil {
|
||||
|
@ -81,8 +81,6 @@ github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORN
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/lightstep/tracecontext.go v0.0.0-20181129014701-1757c391b1ac h1:+2b6iGRJe3hvV/yVXrd41yVEjxuFHxasJqDhkIjS4gk=
|
||||
github.com/lightstep/tracecontext.go v0.0.0-20181129014701-1757c391b1ac/go.mod h1:Frd2bnT3w5FB5q49ENTfVlztJES+1k/7lyWX2+9gq/M=
|
||||
github.com/logrusorgru/aurora v0.0.0-20181002194514-a7b3b318ed4e/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4=
|
||||
github.com/magiconair/properties v1.7.6/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||
|
@ -35,7 +35,7 @@ var (
|
||||
|
||||
func main() {
|
||||
helloHandler := func(w http.ResponseWriter, req *http.Request) {
|
||||
attrs, tags, spanCtx := httptrace.Extract(req)
|
||||
attrs, tags, spanCtx := httptrace.Extract(req.Context(), req)
|
||||
|
||||
req = req.WithContext(tag.WithMap(req.Context(), tag.NewMap(tag.MapUpdate{
|
||||
MultiKV: tags,
|
||||
|
@ -20,7 +20,6 @@ import (
|
||||
|
||||
"go.opentelemetry.io/api/core"
|
||||
"go.opentelemetry.io/api/key"
|
||||
"go.opentelemetry.io/api/tag"
|
||||
"go.opentelemetry.io/api/trace"
|
||||
apitrace "go.opentelemetry.io/api/trace"
|
||||
"go.opentelemetry.io/experimental/streaming/exporter/observer"
|
||||
@ -125,7 +124,3 @@ func (t *tracer) Start(ctx context.Context, name string, opts ...apitrace.SpanOp
|
||||
}
|
||||
return trace.SetCurrentSpan(ctx, span), span
|
||||
}
|
||||
|
||||
func (t *tracer) Inject(ctx context.Context, span apitrace.Span, injector apitrace.Injector) {
|
||||
injector.Inject(span.SpanContext(), tag.FromContext(ctx))
|
||||
}
|
||||
|
1
go.mod
1
go.mod
@ -8,7 +8,6 @@ require (
|
||||
github.com/golangci/golangci-lint v1.17.1
|
||||
github.com/google/go-cmp v0.3.0
|
||||
github.com/hashicorp/golang-lru v0.5.3
|
||||
github.com/lightstep/tracecontext.go v0.0.0-20181129014701-1757c391b1ac
|
||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135
|
||||
google.golang.org/api v0.9.0
|
||||
google.golang.org/grpc v1.22.1
|
||||
|
2
go.sum
2
go.sum
@ -123,8 +123,6 @@ github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORN
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/lightstep/tracecontext.go v0.0.0-20181129014701-1757c391b1ac h1:+2b6iGRJe3hvV/yVXrd41yVEjxuFHxasJqDhkIjS4gk=
|
||||
github.com/lightstep/tracecontext.go v0.0.0-20181129014701-1757c391b1ac/go.mod h1:Frd2bnT3w5FB5q49ENTfVlztJES+1k/7lyWX2+9gq/M=
|
||||
github.com/logrusorgru/aurora v0.0.0-20181002194514-a7b3b318ed4e/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4=
|
||||
github.com/magiconair/properties v1.7.6 h1:U+1DqNen04MdEPgFiIwdOUiqZ8qPa37xgogX/sd3+54=
|
||||
github.com/magiconair/properties v1.7.6/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||
|
96
internal/trace/mock_span.go
Normal file
96
internal/trace/mock_span.go
Normal file
@ -0,0 +1,96 @@
|
||||
// Copyright 2019, 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 trace
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"google.golang.org/grpc/codes"
|
||||
|
||||
"go.opentelemetry.io/api/core"
|
||||
"go.opentelemetry.io/api/tag"
|
||||
apitrace "go.opentelemetry.io/api/trace"
|
||||
)
|
||||
|
||||
// MockSpan is a mock span used in association with MockTracer for testing purpose only.
|
||||
type MockSpan struct {
|
||||
sc core.SpanContext
|
||||
tracer apitrace.Tracer
|
||||
}
|
||||
|
||||
var _ apitrace.Span = (*MockSpan)(nil)
|
||||
|
||||
// SpanContext returns associated core.SpanContext. If the receiver is nil it returns
|
||||
// an empty core.SpanContext
|
||||
func (ms *MockSpan) SpanContext() core.SpanContext {
|
||||
if ms == nil {
|
||||
core.EmptySpanContext()
|
||||
}
|
||||
return ms.sc
|
||||
}
|
||||
|
||||
// IsRecordingEvents always returns false for MockSpan.
|
||||
func (ms *MockSpan) IsRecordingEvents() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// SetStatus does nothing.
|
||||
func (ms *MockSpan) SetStatus(status codes.Code) {
|
||||
}
|
||||
|
||||
// SetError does nothing.
|
||||
func (ms *MockSpan) SetError(v bool) {
|
||||
}
|
||||
|
||||
// SetAttribute does nothing.
|
||||
func (ms *MockSpan) SetAttribute(attribute core.KeyValue) {
|
||||
}
|
||||
|
||||
// SetAttributes does nothing.
|
||||
func (ms *MockSpan) SetAttributes(attributes ...core.KeyValue) {
|
||||
}
|
||||
|
||||
// ModifyAttribute does nothing.
|
||||
func (ms *MockSpan) ModifyAttribute(mutator tag.Mutator) {
|
||||
}
|
||||
|
||||
// ModifyAttributes does nothing.
|
||||
func (ms *MockSpan) ModifyAttributes(mutators ...tag.Mutator) {
|
||||
}
|
||||
|
||||
// Finish does nothing.
|
||||
func (ms *MockSpan) Finish(options ...apitrace.FinishOption) {
|
||||
}
|
||||
|
||||
// SetName does nothing.
|
||||
func (ms *MockSpan) SetName(name string) {
|
||||
}
|
||||
|
||||
// Tracer returns MockTracer implementation of Tracer.
|
||||
func (ms *MockSpan) Tracer() apitrace.Tracer {
|
||||
return ms.tracer
|
||||
}
|
||||
|
||||
// AddEvent does nothing.
|
||||
func (ms *MockSpan) AddEvent(ctx context.Context, msg string, attrs ...core.KeyValue) {
|
||||
}
|
||||
|
||||
// AddLink does nothing.
|
||||
func (ms *MockSpan) AddLink(link apitrace.Link) {
|
||||
}
|
||||
|
||||
// Link does nothing.
|
||||
func (ms *MockSpan) Link(sc core.SpanContext, attrs ...core.KeyValue) {
|
||||
}
|
91
internal/trace/mock_tracer.go
Normal file
91
internal/trace/mock_tracer.go
Normal file
@ -0,0 +1,91 @@
|
||||
// Copyright 2019, 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 trace
|
||||
|
||||
import (
|
||||
"context"
|
||||
"math/rand"
|
||||
"sync/atomic"
|
||||
|
||||
"go.opentelemetry.io/api/core"
|
||||
apitrace "go.opentelemetry.io/api/trace"
|
||||
)
|
||||
|
||||
// MockTracer is a simple tracer used for testing purpose only.
|
||||
// It only supports ChildOf option. SpanId is atomically increased every time a
|
||||
// new span is created.
|
||||
type MockTracer struct {
|
||||
// Sampled specifies if the new span should be sampled or not.
|
||||
Sampled bool
|
||||
|
||||
// StartSpanId is used to initialize spanId. It is incremented by one
|
||||
// every time a new span is created.
|
||||
StartSpanId *uint64
|
||||
}
|
||||
|
||||
var _ apitrace.Tracer = (*MockTracer)(nil)
|
||||
|
||||
// WithResources does nothing and returns MockTracer implementation of Tracer.
|
||||
func (mt *MockTracer) WithResources(attributes ...core.KeyValue) apitrace.Tracer {
|
||||
return mt
|
||||
}
|
||||
|
||||
// WithComponent does nothing and returns MockTracer implementation of Tracer.
|
||||
func (mt *MockTracer) WithComponent(name string) apitrace.Tracer {
|
||||
return mt
|
||||
}
|
||||
|
||||
// WithService does nothing and returns MockTracer implementation of Tracer.
|
||||
func (mt *MockTracer) WithService(name string) apitrace.Tracer {
|
||||
return mt
|
||||
}
|
||||
|
||||
// WithSpan does nothing except executing the body.
|
||||
func (mt *MockTracer) WithSpan(ctx context.Context, name string, body func(context.Context) error) error {
|
||||
return body(ctx)
|
||||
}
|
||||
|
||||
// Start starts a MockSpan. It creates a new Span based on Reference SpanContext option.
|
||||
// TracdID is used from Reference Span Context and SpanID is assigned.
|
||||
// If Reference SpanContext option is not specified then random TraceID is used.
|
||||
// No other options are supported.
|
||||
func (mt *MockTracer) Start(ctx context.Context, name string, o ...apitrace.SpanOption) (context.Context, apitrace.Span) {
|
||||
var opts apitrace.SpanOptions
|
||||
for _, op := range o {
|
||||
op(&opts)
|
||||
}
|
||||
var span *MockSpan
|
||||
var sc core.SpanContext
|
||||
if !opts.Reference.SpanContext.IsValid() {
|
||||
sc = core.SpanContext{
|
||||
TraceID: core.TraceID{
|
||||
High: rand.Uint64(),
|
||||
Low: rand.Uint64(),
|
||||
},
|
||||
}
|
||||
if mt.Sampled {
|
||||
sc.TraceOptions = core.TraceOptionSampled
|
||||
}
|
||||
} else {
|
||||
sc = opts.Reference.SpanContext
|
||||
}
|
||||
sc.SpanID = atomic.AddUint64(mt.StartSpanId, 1)
|
||||
span = &MockSpan{
|
||||
sc: sc,
|
||||
tracer: mt,
|
||||
}
|
||||
|
||||
return apitrace.SetCurrentSpan(ctx, span), span
|
||||
}
|
@ -18,12 +18,10 @@ import (
|
||||
"context"
|
||||
"net/http"
|
||||
"net/http/httptrace"
|
||||
|
||||
"go.opentelemetry.io/api/trace"
|
||||
)
|
||||
|
||||
// Client
|
||||
func W3C(ctx context.Context, req *http.Request) (context.Context, *http.Request, trace.Injector) {
|
||||
func W3C(ctx context.Context, req *http.Request) (context.Context, *http.Request) {
|
||||
t := newClientTracer(ctx)
|
||||
|
||||
t.GetConn = t.getConn
|
||||
@ -45,5 +43,5 @@ func W3C(ctx context.Context, req *http.Request) (context.Context, *http.Request
|
||||
|
||||
ctx = httptrace.WithClientTrace(ctx, &t.ClientTrace)
|
||||
req = req.WithContext(ctx)
|
||||
return ctx, req, hinjector{req}
|
||||
return ctx, req
|
||||
}
|
||||
|
@ -15,15 +15,12 @@
|
||||
package httptrace
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"context"
|
||||
"net/http"
|
||||
|
||||
"github.com/lightstep/tracecontext.go"
|
||||
"github.com/lightstep/tracecontext.go/tracestate"
|
||||
|
||||
"go.opentelemetry.io/api/core"
|
||||
"go.opentelemetry.io/api/key"
|
||||
"go.opentelemetry.io/api/tag"
|
||||
"go.opentelemetry.io/propagation"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -34,68 +31,21 @@ var (
|
||||
HostKey = key.New("http.host")
|
||||
URLKey = key.New("http.url")
|
||||
|
||||
encoding = binary.BigEndian
|
||||
propagator = propagation.HttpTraceContextPropagator()
|
||||
)
|
||||
|
||||
// Returns the Attributes, Context Tags, and SpanContext that were encoded by Inject.
|
||||
func Extract(req *http.Request) ([]core.KeyValue, []core.KeyValue, core.SpanContext) {
|
||||
tc, err := tracecontext.FromHeaders(req.Header)
|
||||
|
||||
if err != nil {
|
||||
return nil, nil, core.SpanContext{}
|
||||
}
|
||||
|
||||
var sc core.SpanContext
|
||||
sc.SpanID = encoding.Uint64(tc.TraceParent.SpanID[0:8])
|
||||
sc.TraceID.High = encoding.Uint64(tc.TraceParent.TraceID[0:8])
|
||||
sc.TraceID.Low = encoding.Uint64(tc.TraceParent.TraceID[8:16])
|
||||
func Extract(ctx context.Context, req *http.Request) ([]core.KeyValue, []core.KeyValue, core.SpanContext) {
|
||||
sc := propagator.Extract(ctx, req.Header)
|
||||
|
||||
attrs := []core.KeyValue{
|
||||
URLKey.String(req.URL.String()),
|
||||
// Etc.
|
||||
}
|
||||
|
||||
var tags []core.KeyValue
|
||||
|
||||
for _, ts := range tc.TraceState {
|
||||
if ts.Vendor != Vendor {
|
||||
continue
|
||||
}
|
||||
// TODO: max-hops, type conversion questions answered,
|
||||
// case-conversion questions.
|
||||
tags = append(tags, key.New(ts.Tenant).String(ts.Value))
|
||||
return attrs, nil, sc
|
||||
}
|
||||
|
||||
return attrs, tags, sc
|
||||
}
|
||||
|
||||
type hinjector struct {
|
||||
*http.Request
|
||||
}
|
||||
|
||||
func (h hinjector) Inject(sc core.SpanContext, tags tag.Map) {
|
||||
var tc tracecontext.TraceContext
|
||||
var sid [8]byte
|
||||
var tid [16]byte
|
||||
|
||||
encoding.PutUint64(sid[0:8], sc.SpanID)
|
||||
encoding.PutUint64(tid[0:8], sc.TraceID.High)
|
||||
encoding.PutUint64(tid[8:16], sc.TraceID.Low)
|
||||
|
||||
tc.TraceParent.Version = tracecontext.Version
|
||||
tc.TraceParent.TraceID = tid
|
||||
tc.TraceParent.SpanID = sid
|
||||
tc.TraceParent.Flags.Recorded = true // Note: not implemented.
|
||||
|
||||
tags.Foreach(func(kv core.KeyValue) bool {
|
||||
// TODO: implement MaxHops
|
||||
tc.TraceState = append(tc.TraceState, tracestate.Member{
|
||||
Vendor: Vendor,
|
||||
Tenant: kv.Key.Name,
|
||||
Value: kv.Value.Emit(),
|
||||
})
|
||||
return true
|
||||
})
|
||||
|
||||
tc.SetHeaders(h.Header)
|
||||
func Inject(ctx context.Context, req *http.Request) {
|
||||
propagator.Inject(ctx, req.Header)
|
||||
}
|
||||
|
16
propagation/doc.go
Normal file
16
propagation/doc.go
Normal file
@ -0,0 +1,16 @@
|
||||
// Copyright 2019, 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 propagation contains propagators for different format and carriers.
|
||||
package propagation // import "go.opentelemetry.io/propagation"
|
138
propagation/http_trace_context_propagator.go
Normal file
138
propagation/http_trace_context_propagator.go
Normal file
@ -0,0 +1,138 @@
|
||||
// Copyright 2019, 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 propagation
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"go.opentelemetry.io/api/trace"
|
||||
|
||||
"go.opentelemetry.io/api/core"
|
||||
apipropagation "go.opentelemetry.io/api/propagation"
|
||||
)
|
||||
|
||||
const (
|
||||
supportedVersion = 0
|
||||
maxVersion = 254
|
||||
traceparentHeader = "traceparent"
|
||||
)
|
||||
|
||||
type httpTraceContextPropagator struct{}
|
||||
|
||||
var _ apipropagation.TextFormatPropagator = httpTraceContextPropagator{}
|
||||
var traceCtxRegExp = regexp.MustCompile("^[0-9a-f]{2}-[a-f0-9]{32}-[a-f0-9]{16}-[a-f0-9]{2}-?")
|
||||
|
||||
func (hp httpTraceContextPropagator) Inject(ctx context.Context, supplier apipropagation.Supplier) {
|
||||
sc := trace.CurrentSpan(ctx).SpanContext()
|
||||
if sc.IsValid() {
|
||||
h := fmt.Sprintf("%.2x-%.16x%.16x-%.16x-%.2x",
|
||||
supportedVersion,
|
||||
sc.TraceID.High,
|
||||
sc.TraceID.Low,
|
||||
sc.SpanID,
|
||||
sc.TraceOptions&core.TraceOptionSampled)
|
||||
supplier.Set(traceparentHeader, h)
|
||||
}
|
||||
}
|
||||
|
||||
func (hp httpTraceContextPropagator) Extract(ctx context.Context, supplier apipropagation.Supplier) core.SpanContext {
|
||||
h := supplier.Get(traceparentHeader)
|
||||
if h == "" {
|
||||
return core.EmptySpanContext()
|
||||
}
|
||||
|
||||
h = strings.Trim(h, "-")
|
||||
if !traceCtxRegExp.MatchString(h) {
|
||||
return core.EmptySpanContext()
|
||||
}
|
||||
|
||||
sections := strings.Split(h, "-")
|
||||
if len(sections) < 4 {
|
||||
return core.EmptySpanContext()
|
||||
}
|
||||
|
||||
if len(sections[0]) != 2 {
|
||||
return core.EmptySpanContext()
|
||||
}
|
||||
ver, err := hex.DecodeString(sections[0])
|
||||
if err != nil {
|
||||
return core.EmptySpanContext()
|
||||
}
|
||||
version := int(ver[0])
|
||||
if version > maxVersion {
|
||||
return core.EmptySpanContext()
|
||||
}
|
||||
|
||||
if version == 0 && len(sections) != 4 {
|
||||
return core.EmptySpanContext()
|
||||
}
|
||||
|
||||
if len(sections[1]) != 32 {
|
||||
return core.EmptySpanContext()
|
||||
}
|
||||
|
||||
result, err := strconv.ParseUint(sections[1][0:16], 16, 64)
|
||||
if err != nil {
|
||||
return core.EmptySpanContext()
|
||||
}
|
||||
var sc core.SpanContext
|
||||
|
||||
sc.TraceID.High = result
|
||||
|
||||
result, err = strconv.ParseUint(sections[1][16:32], 16, 64)
|
||||
if err != nil {
|
||||
return core.EmptySpanContext()
|
||||
}
|
||||
sc.TraceID.Low = result
|
||||
|
||||
if len(sections[2]) != 16 {
|
||||
return core.EmptySpanContext()
|
||||
}
|
||||
result, err = strconv.ParseUint(sections[2][0:], 16, 64)
|
||||
if err != nil {
|
||||
return core.EmptySpanContext()
|
||||
}
|
||||
sc.SpanID = result
|
||||
|
||||
if len(sections[3]) != 2 {
|
||||
return core.EmptySpanContext()
|
||||
}
|
||||
opts, err := hex.DecodeString(sections[3])
|
||||
if err != nil || len(opts) < 1 || (version == 0 && opts[0] > 2) {
|
||||
return core.EmptySpanContext()
|
||||
}
|
||||
sc.TraceOptions = opts[0] &^ core.TraceOptionUnused
|
||||
|
||||
if !sc.IsValid() {
|
||||
return core.EmptySpanContext()
|
||||
}
|
||||
|
||||
return sc
|
||||
}
|
||||
|
||||
func (hp httpTraceContextPropagator) GetAllKeys() []string {
|
||||
return []string{traceparentHeader}
|
||||
}
|
||||
|
||||
// HttpTraceContextPropagator creates a new text format propagator that propagates SpanContext
|
||||
// in W3C TraceContext format.
|
||||
func HttpTraceContextPropagator() apipropagation.TextFormatPropagator {
|
||||
return httpTraceContextPropagator{}
|
||||
}
|
286
propagation/http_trace_context_propagator_test.go
Normal file
286
propagation/http_trace_context_propagator_test.go
Normal file
@ -0,0 +1,286 @@
|
||||
// Copyright 2019, 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 propagation_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
"go.opentelemetry.io/api/trace"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
|
||||
"go.opentelemetry.io/api/core"
|
||||
mocktrace "go.opentelemetry.io/internal/trace"
|
||||
"go.opentelemetry.io/propagation"
|
||||
)
|
||||
|
||||
var (
|
||||
traceID = core.TraceID{High: 0x4bf92f3577b34da6, Low: 0xa3ce929d0e0e4736}
|
||||
spanID = uint64(0x00f067aa0ba902b7)
|
||||
)
|
||||
|
||||
func TestExtractValidTraceContextFromHTTPReq(t *testing.T) {
|
||||
trace.SetGlobalTracer(&mocktrace.MockTracer{})
|
||||
propagator := propagation.HttpTraceContextPropagator()
|
||||
tests := []struct {
|
||||
name string
|
||||
header string
|
||||
wantSc core.SpanContext
|
||||
}{
|
||||
{
|
||||
name: "valid header",
|
||||
header: "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-00",
|
||||
wantSc: core.SpanContext{
|
||||
TraceID: traceID,
|
||||
SpanID: spanID,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "valid header and sampled",
|
||||
header: "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01",
|
||||
wantSc: core.SpanContext{
|
||||
TraceID: traceID,
|
||||
SpanID: spanID,
|
||||
TraceOptions: core.TraceOptionSampled,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "future version",
|
||||
header: "02-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01",
|
||||
wantSc: core.SpanContext{
|
||||
TraceID: traceID,
|
||||
SpanID: spanID,
|
||||
TraceOptions: core.TraceOptionSampled,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "future options with sampled bit set",
|
||||
header: "02-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-09",
|
||||
wantSc: core.SpanContext{
|
||||
TraceID: traceID,
|
||||
SpanID: spanID,
|
||||
TraceOptions: core.TraceOptionSampled,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "future options with sampled bit cleared",
|
||||
header: "02-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-08",
|
||||
wantSc: core.SpanContext{
|
||||
TraceID: traceID,
|
||||
SpanID: spanID,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "future additional data",
|
||||
header: "02-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-09-XYZxsf09",
|
||||
wantSc: core.SpanContext{
|
||||
TraceID: traceID,
|
||||
SpanID: spanID,
|
||||
TraceOptions: core.TraceOptionSampled,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "valid header ending in dash",
|
||||
header: "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01-",
|
||||
wantSc: core.SpanContext{
|
||||
TraceID: traceID,
|
||||
SpanID: spanID,
|
||||
TraceOptions: core.TraceOptionSampled,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "future valid header ending in dash",
|
||||
header: "01-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-09-",
|
||||
wantSc: core.SpanContext{
|
||||
TraceID: traceID,
|
||||
SpanID: spanID,
|
||||
TraceOptions: core.TraceOptionSampled,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
req, _ := http.NewRequest("GET", "http://example.com", nil)
|
||||
req.Header.Set("traceparent", tt.header)
|
||||
|
||||
ctx := context.Background()
|
||||
gotSc := propagator.Extract(ctx, req.Header)
|
||||
if diff := cmp.Diff(gotSc, tt.wantSc); diff != "" {
|
||||
t.Errorf("Extract Tracecontext: %s: -got +want %s", tt.name, diff)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestExtractInvalidTraceContextFromHTTPReq(t *testing.T) {
|
||||
trace.SetGlobalTracer(&mocktrace.MockTracer{})
|
||||
propagator := propagation.HttpTraceContextPropagator()
|
||||
wantSc := core.EmptySpanContext()
|
||||
tests := []struct {
|
||||
name string
|
||||
header string
|
||||
}{
|
||||
{
|
||||
name: "wrong version length",
|
||||
header: "0000-00000000000000000000000000000000-0000000000000000-01",
|
||||
},
|
||||
{
|
||||
name: "wrong trace ID length",
|
||||
header: "00-ab00000000000000000000000000000000-cd00000000000000-01",
|
||||
},
|
||||
{
|
||||
name: "wrong span ID length",
|
||||
header: "00-ab000000000000000000000000000000-cd0000000000000000-01",
|
||||
},
|
||||
{
|
||||
name: "wrong trace flag length",
|
||||
header: "00-ab000000000000000000000000000000-cd00000000000000-0100",
|
||||
},
|
||||
{
|
||||
name: "bogus version",
|
||||
header: "qw-00000000000000000000000000000000-0000000000000000-01",
|
||||
},
|
||||
{
|
||||
name: "bogus trace ID",
|
||||
header: "00-qw000000000000000000000000000000-cd00000000000000-01",
|
||||
},
|
||||
{
|
||||
name: "bogus span ID",
|
||||
header: "00-ab000000000000000000000000000000-qw00000000000000-01",
|
||||
},
|
||||
{
|
||||
name: "bogus trace flag",
|
||||
header: "00-ab000000000000000000000000000000-cd00000000000000-qw",
|
||||
},
|
||||
{
|
||||
name: "upper case version",
|
||||
header: "A0-00000000000000000000000000000000-0000000000000000-01",
|
||||
},
|
||||
{
|
||||
name: "upper case trace ID",
|
||||
header: "00-AB000000000000000000000000000000-cd00000000000000-01",
|
||||
},
|
||||
{
|
||||
name: "upper case span ID",
|
||||
header: "00-ab000000000000000000000000000000-CD00000000000000-01",
|
||||
},
|
||||
{
|
||||
name: "upper case trace flag",
|
||||
header: "00-ab000000000000000000000000000000-cd00000000000000-A1",
|
||||
},
|
||||
{
|
||||
name: "zero trace ID and span ID",
|
||||
header: "00-00000000000000000000000000000000-0000000000000000-01",
|
||||
},
|
||||
{
|
||||
name: "trace-flag unused bits set",
|
||||
header: "00-ab000000000000000000000000000000-cd00000000000000-09",
|
||||
},
|
||||
{
|
||||
name: "missing options",
|
||||
header: "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7",
|
||||
},
|
||||
{
|
||||
name: "empty options",
|
||||
header: "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
req, _ := http.NewRequest("GET", "http://example.com", nil)
|
||||
req.Header.Set("traceparent", tt.header)
|
||||
|
||||
ctx := context.Background()
|
||||
gotSc := propagator.Extract(ctx, req.Header)
|
||||
if diff := cmp.Diff(gotSc, wantSc); diff != "" {
|
||||
t.Errorf("Extract Tracecontext: %s: -got +want %s", tt.name, diff)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestInjectTraceContextToHTTPReq(t *testing.T) {
|
||||
var id uint64
|
||||
mockTracer := &mocktrace.MockTracer{
|
||||
Sampled: false,
|
||||
StartSpanId: &id,
|
||||
}
|
||||
propagator := propagation.HttpTraceContextPropagator()
|
||||
tests := []struct {
|
||||
name string
|
||||
sc core.SpanContext
|
||||
wantHeader string
|
||||
}{
|
||||
{
|
||||
name: "valid spancontext, sampled",
|
||||
sc: core.SpanContext{
|
||||
TraceID: traceID,
|
||||
SpanID: spanID,
|
||||
TraceOptions: core.TraceOptionSampled,
|
||||
},
|
||||
wantHeader: "00-4bf92f3577b34da6a3ce929d0e0e4736-0000000000000001-01",
|
||||
},
|
||||
{
|
||||
name: "valid spancontext, not sampled",
|
||||
sc: core.SpanContext{
|
||||
TraceID: traceID,
|
||||
SpanID: spanID,
|
||||
},
|
||||
wantHeader: "00-4bf92f3577b34da6a3ce929d0e0e4736-0000000000000002-00",
|
||||
},
|
||||
{
|
||||
name: "valid spancontext, with unsupported bit set in traceoption",
|
||||
sc: core.SpanContext{
|
||||
TraceID: traceID,
|
||||
SpanID: spanID,
|
||||
TraceOptions: 0xff,
|
||||
},
|
||||
wantHeader: "00-4bf92f3577b34da6a3ce929d0e0e4736-0000000000000003-01",
|
||||
},
|
||||
{
|
||||
name: "invalid spancontext",
|
||||
sc: core.EmptySpanContext(),
|
||||
wantHeader: "",
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
req, _ := http.NewRequest("GET", "http://example.com", nil)
|
||||
ctx := context.Background()
|
||||
if tt.sc.IsValid() {
|
||||
ctx, _ = mockTracer.Start(ctx, "inject", trace.ChildOf(tt.sc))
|
||||
}
|
||||
propagator.Inject(ctx, req.Header)
|
||||
|
||||
gotHeader := req.Header.Get("traceparent")
|
||||
if diff := cmp.Diff(gotHeader, tt.wantHeader); diff != "" {
|
||||
t.Errorf("Extract Tracecontext: %s: -got +want %s", tt.name, diff)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestHttpTraceContextPropagator_GetAllKeys(t *testing.T) {
|
||||
propagator := propagation.HttpTraceContextPropagator()
|
||||
want := []string{"traceparent"}
|
||||
got := propagator.GetAllKeys()
|
||||
if diff := cmp.Diff(got, want); diff != "" {
|
||||
t.Errorf("GetAllKeys: -got +want %s", diff)
|
||||
}
|
||||
}
|
@ -18,7 +18,6 @@ import (
|
||||
"context"
|
||||
|
||||
"go.opentelemetry.io/api/core"
|
||||
"go.opentelemetry.io/api/tag"
|
||||
apitrace "go.opentelemetry.io/api/trace"
|
||||
)
|
||||
|
||||
@ -102,7 +101,3 @@ func (tr *tracer) WithComponent(component string) apitrace.Tracer {
|
||||
tr.component = component
|
||||
return tr
|
||||
}
|
||||
|
||||
func (tr *tracer) Inject(ctx context.Context, span apitrace.Span, injector apitrace.Injector) {
|
||||
injector.Inject(span.SpanContext(), tag.NewEmptyMap())
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user