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 18 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
5 changes: 5 additions & 0 deletions CHANGELOG.md
Expand Up @@ -8,6 +8,11 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm

## [Unreleased]

### Added

- Add support for `opentracing.TextMap` format in the `Inject` and `Extract` methods
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
- Add support for `opentracing.TextMap` format in the `Inject` and `Extract` methods
- Add support for `opentracing.TextMap` format in the `Inject` and `Extract` methods

markdown err, pls commit to fix it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done.

of the `"go.opentelemetry.io/otel/bridge/opentracing".BridgeTracer` type. (#2911)

### Changed

- The `crosslink` make target has been updated to use the `go.opentelemetry.io/build-tools/crosslink` package. (#2886)
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 formats 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 formats are supported.
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.
}