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

Feat/bridge support text map #2911

Merged
merged 20 commits into from Jul 6, 2022
Merged
Show file tree
Hide file tree
Changes from 16 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -27,6 +27,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
- Add the `go.opentelemetry.io/otel/semconv/v1.10.0` package.
The package contains semantic conventions from the `v1.10.0` version of the OpenTelemetry specification. (#2842)
- Added an in-memory exporter to metrictest to aid testing with a full SDK. (#2776)
- The bridge package supported extract and injection of opentracing.TextMap type. (#2911)
tttoad marked this conversation as resolved.
Show resolved Hide resolved

### Fixed

Expand Down
167 changes: 153 additions & 14 deletions bridge/opentracing/bridge.go
Expand Up @@ -634,7 +634,7 @@ func (s fakeSpan) SpanContext() trace.SpanContext {
// Inject is a part of the implementation of the OpenTracing Tracer
// interface.
//
// Currently only the HTTPHeaders format is supported.
// Currently only the HTTPHeaders and TextMap format are supported.
func (t *BridgeTracer) Inject(sm ot.SpanContext, format interface{}, carrier interface{}) error {
bridgeSC, ok := sm.(*bridgeSpanContext)
if !ok {
Expand All @@ -643,38 +643,75 @@ func (t *BridgeTracer) Inject(sm ot.SpanContext, format interface{}, carrier int
if !bridgeSC.otelSpanContext.IsValid() {
return ot.ErrInvalidSpanContext
}
if builtinFormat, ok := format.(ot.BuiltinFormat); !ok || builtinFormat != ot.HTTPHeaders {

builtinFormat, ok := format.(ot.BuiltinFormat)
if !ok {
return ot.ErrUnsupportedFormat
}
hhcarrier, ok := carrier.(ot.HTTPHeadersCarrier)
if !ok {
return ot.ErrInvalidCarrier

var textCarrier propagation.TextMapCarrier

switch builtinFormat {
case ot.HTTPHeaders:
hhcarrier, ok := carrier.(ot.HTTPHeadersCarrier)
if !ok {
return ot.ErrInvalidCarrier
}

textCarrier = propagation.HeaderCarrier(hhcarrier)
case ot.TextMap:
if textCarrier, ok = carrier.(propagation.TextMapCarrier); !ok {
var err error
if textCarrier, err = newTextMapWrapperForInject(carrier); err != nil {
return err
Aneurysm9 marked this conversation as resolved.
Show resolved Hide resolved
}
}
default:
return ot.ErrUnsupportedFormat
}
header := http.Header(hhcarrier)

fs := fakeSpan{
Span: noopSpan,
sc: bridgeSC.otelSpanContext,
}
ctx := trace.ContextWithSpan(context.Background(), fs)
ctx = baggage.ContextWithBaggage(ctx, bridgeSC.bag)
t.getPropagator().Inject(ctx, propagation.HeaderCarrier(header))
t.getPropagator().Inject(ctx, textCarrier)
tttoad marked this conversation as resolved.
Show resolved Hide resolved
return nil
}

// Extract is a part of the implementation of the OpenTracing Tracer
// interface.
//
// Currently only the HTTPHeaders format is supported.
// Currently only the HTTPHeaders and TextMap format are supported.
tttoad marked this conversation as resolved.
Show resolved Hide resolved
func (t *BridgeTracer) Extract(format interface{}, carrier interface{}) (ot.SpanContext, error) {
if builtinFormat, ok := format.(ot.BuiltinFormat); !ok || builtinFormat != ot.HTTPHeaders {
builtinFormat, ok := format.(ot.BuiltinFormat)
if !ok {
return nil, ot.ErrUnsupportedFormat
}
hhcarrier, ok := carrier.(ot.HTTPHeadersCarrier)
if !ok {
return nil, ot.ErrInvalidCarrier

var textCarrier propagation.TextMapCarrier

switch builtinFormat {
case ot.HTTPHeaders:
hhcarrier, ok := carrier.(ot.HTTPHeadersCarrier)
if !ok {
return nil, ot.ErrInvalidCarrier
}

textCarrier = propagation.HeaderCarrier(hhcarrier)
case ot.TextMap:
if textCarrier, ok = carrier.(propagation.TextMapCarrier); !ok {
var err error
if textCarrier, err = newTextMapWrapperForExtract(carrier); err != nil {
return nil, err
Aneurysm9 marked this conversation as resolved.
Show resolved Hide resolved
}
}
default:
return nil, ot.ErrUnsupportedFormat
}
header := http.Header(hhcarrier)
ctx := t.getPropagator().Extract(context.Background(), propagation.HeaderCarrier(header))

ctx := t.getPropagator().Extract(context.Background(), textCarrier)
bag := baggage.FromContext(ctx)
bridgeSC := &bridgeSpanContext{
bag: bag,
Expand All @@ -692,3 +729,105 @@ func (t *BridgeTracer) getPropagator() propagation.TextMapPropagator {
}
return otel.GetTextMapPropagator()
}

// textMapWrapper Provides operating.TextMapWriter and operating.TextMapReader to
// propagation.TextMapCarrier compatibility.
// Usually, Inject method will only use the write-related interface.
// Extract method will only use the reade-related interface.
// To avoid panic,
// when the carrier implements only one of the interfaces,
// it provides a default implementation of the other interface (textMapWriter and textMapReader).
type textMapWrapper struct {
ot.TextMapWriter
ot.TextMapReader
readerMap map[string]string
}

func (t *textMapWrapper) Get(key string) string {
if t.readerMap == nil {
t.loadMap()
}

Aneurysm9 marked this conversation as resolved.
Show resolved Hide resolved
return t.readerMap[key]
}

func (t *textMapWrapper) Set(key string, value string) {
t.TextMapWriter.Set(key, value)
}

func (t *textMapWrapper) Keys() []string {
if t.readerMap == nil {
t.loadMap()
}

str := make([]string, 0, len(t.readerMap))
for key := range t.readerMap {
str = append(str, key)
}

Aneurysm9 marked this conversation as resolved.
Show resolved Hide resolved
return str
}

func (t *textMapWrapper) loadMap() {
t.readerMap = make(map[string]string)

_ = t.ForeachKey(func(key, val string) error {
t.readerMap[key] = val

return nil
})
}

func newTextMapWrapperForExtract(carrier interface{}) (*textMapWrapper, error) {
t := &textMapWrapper{}

reader, ok := carrier.(ot.TextMapReader)
if !ok {
return nil, ot.ErrInvalidCarrier
}

t.TextMapReader = reader

writer, ok := carrier.(ot.TextMapWriter)
if ok {
t.TextMapWriter = writer
} else {
t.TextMapWriter = &textMapWriter{}
}

return t, nil
}

func newTextMapWrapperForInject(carrier interface{}) (*textMapWrapper, error) {
t := &textMapWrapper{}

writer, ok := carrier.(ot.TextMapWriter)
if !ok {
return nil, ot.ErrInvalidCarrier
}

t.TextMapWriter = writer

reader, ok := carrier.(ot.TextMapReader)
if ok {
t.TextMapReader = reader
} else {
t.TextMapReader = &textMapReader{}
}

return t, nil
}

type textMapWriter struct {
}

func (t *textMapWriter) Set(key string, value string) {
// maybe print a warning log.
}

type textMapReader struct {
}

func (t *textMapReader) ForeachKey(handler func(key, val string) error) error {
return nil // maybe print a warning log.
}