mirror of
https://github.com/open-telemetry/opentelemetry-go.git
synced 2024-12-14 10:13:10 +02:00
339ca2d974
* Allow specifying custom timestamps for events Adding event with timestamp is not yet a part of the OpenTelemetry specification, but this function will come in handy when implementing the OpenTracing bridge. * Add opentracing bridge, wrapper tracer and migration interfaces There are some features missing - setting up links and span kind; context propagation will only work between two OpenTracing bridges. * Add some tests for the opentracing bridge The tests mostly check various aspects of the cooperation between OpenTracing and OpenTelemetry APIs.
627 lines
17 KiB
Go
627 lines
17 KiB
Go
// 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 opentracing
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"net/http"
|
|
"strconv"
|
|
"strings"
|
|
"sync"
|
|
|
|
"google.golang.org/grpc/codes"
|
|
|
|
ot "github.com/opentracing/opentracing-go"
|
|
otext "github.com/opentracing/opentracing-go/ext"
|
|
otlog "github.com/opentracing/opentracing-go/log"
|
|
|
|
otelcore "go.opentelemetry.io/api/core"
|
|
oteltrace "go.opentelemetry.io/api/trace"
|
|
|
|
migration "go.opentelemetry.io/experimental/bridge/opentracing/migration"
|
|
)
|
|
|
|
type bridgeSpanContext struct {
|
|
// TODO: have a look at the java implementation of the shim to
|
|
// see what do they do with the baggage items
|
|
baggageItems map[string]string
|
|
otelSpanContext otelcore.SpanContext
|
|
}
|
|
|
|
var _ ot.SpanContext = &bridgeSpanContext{}
|
|
|
|
func newBridgeSpanContext(otelSpanContext otelcore.SpanContext, parentOtSpanContext ot.SpanContext) *bridgeSpanContext {
|
|
bCtx := &bridgeSpanContext{
|
|
baggageItems: nil,
|
|
otelSpanContext: otelSpanContext,
|
|
}
|
|
if parentOtSpanContext != nil {
|
|
parentOtSpanContext.ForeachBaggageItem(func(key, value string) bool {
|
|
bCtx.setBaggageItem(key, value)
|
|
return true
|
|
})
|
|
}
|
|
return bCtx
|
|
}
|
|
|
|
func (c *bridgeSpanContext) ForeachBaggageItem(handler func(k, v string) bool) {
|
|
for k, v := range c.baggageItems {
|
|
if !handler(k, v) {
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
func (c *bridgeSpanContext) setBaggageItem(restrictedKey, value string) {
|
|
if c.baggageItems == nil {
|
|
c.baggageItems = make(map[string]string)
|
|
}
|
|
crk := http.CanonicalHeaderKey(restrictedKey)
|
|
c.baggageItems[crk] = value
|
|
}
|
|
|
|
func (c *bridgeSpanContext) baggageItem(restrictedKey string) string {
|
|
crk := http.CanonicalHeaderKey(restrictedKey)
|
|
return c.baggageItems[crk]
|
|
}
|
|
|
|
type bridgeSpan struct {
|
|
otelSpan oteltrace.Span
|
|
ctx *bridgeSpanContext
|
|
tracer *BridgeTracer
|
|
skipDeferHook bool
|
|
}
|
|
|
|
var _ ot.Span = &bridgeSpan{}
|
|
|
|
func (s *bridgeSpan) Finish() {
|
|
s.otelSpan.Finish()
|
|
}
|
|
|
|
func (s *bridgeSpan) FinishWithOptions(opts ot.FinishOptions) {
|
|
var otelOpts []oteltrace.FinishOption
|
|
|
|
if !opts.FinishTime.IsZero() {
|
|
otelOpts = append(otelOpts, oteltrace.WithFinishTime(opts.FinishTime))
|
|
}
|
|
for _, record := range opts.LogRecords {
|
|
s.logRecord(record)
|
|
}
|
|
for _, data := range opts.BulkLogData {
|
|
s.logRecord(data.ToLogRecord())
|
|
}
|
|
s.otelSpan.Finish(otelOpts...)
|
|
}
|
|
|
|
func (s *bridgeSpan) logRecord(record ot.LogRecord) {
|
|
s.otelSpan.AddEventWithTimestamp(context.Background(), record.Timestamp, "", otLogFieldsToOtelCoreKeyValues(record.Fields)...)
|
|
}
|
|
|
|
func (s *bridgeSpan) Context() ot.SpanContext {
|
|
return s.ctx
|
|
}
|
|
|
|
func (s *bridgeSpan) SetOperationName(operationName string) ot.Span {
|
|
s.otelSpan.SetName(operationName)
|
|
return s
|
|
}
|
|
|
|
func (s *bridgeSpan) SetTag(key string, value interface{}) ot.Span {
|
|
switch key {
|
|
case string(otext.SpanKind):
|
|
// TODO: Should we ignore it?
|
|
case string(otext.Error):
|
|
if b, ok := value.(bool); ok {
|
|
status := codes.OK
|
|
if b {
|
|
status = codes.Unknown
|
|
}
|
|
s.otelSpan.SetStatus(status)
|
|
}
|
|
default:
|
|
s.otelSpan.SetAttribute(otTagToOtelCoreKeyValue(key, value))
|
|
}
|
|
return s
|
|
}
|
|
|
|
func (s *bridgeSpan) LogFields(fields ...otlog.Field) {
|
|
s.otelSpan.AddEvent(context.Background(), "", otLogFieldsToOtelCoreKeyValues(fields)...)
|
|
}
|
|
|
|
type bridgeFieldEncoder struct {
|
|
pairs []otelcore.KeyValue
|
|
}
|
|
|
|
var _ otlog.Encoder = &bridgeFieldEncoder{}
|
|
|
|
func (e *bridgeFieldEncoder) EmitString(key, value string) {
|
|
e.emitCommon(key, value)
|
|
}
|
|
|
|
func (e *bridgeFieldEncoder) EmitBool(key string, value bool) {
|
|
e.emitCommon(key, value)
|
|
}
|
|
|
|
func (e *bridgeFieldEncoder) EmitInt(key string, value int) {
|
|
e.emitCommon(key, value)
|
|
}
|
|
|
|
func (e *bridgeFieldEncoder) EmitInt32(key string, value int32) {
|
|
e.emitCommon(key, value)
|
|
}
|
|
|
|
func (e *bridgeFieldEncoder) EmitInt64(key string, value int64) {
|
|
e.emitCommon(key, value)
|
|
}
|
|
|
|
func (e *bridgeFieldEncoder) EmitUint32(key string, value uint32) {
|
|
e.emitCommon(key, value)
|
|
}
|
|
|
|
func (e *bridgeFieldEncoder) EmitUint64(key string, value uint64) {
|
|
e.emitCommon(key, value)
|
|
}
|
|
|
|
func (e *bridgeFieldEncoder) EmitFloat32(key string, value float32) {
|
|
e.emitCommon(key, value)
|
|
}
|
|
|
|
func (e *bridgeFieldEncoder) EmitFloat64(key string, value float64) {
|
|
e.emitCommon(key, value)
|
|
}
|
|
|
|
func (e *bridgeFieldEncoder) EmitObject(key string, value interface{}) {
|
|
e.emitCommon(key, value)
|
|
}
|
|
|
|
func (e *bridgeFieldEncoder) EmitLazyLogger(value otlog.LazyLogger) {
|
|
value(e)
|
|
}
|
|
|
|
func (e *bridgeFieldEncoder) emitCommon(key string, value interface{}) {
|
|
e.pairs = append(e.pairs, otTagToOtelCoreKeyValue(key, value))
|
|
}
|
|
|
|
func otLogFieldsToOtelCoreKeyValues(fields []otlog.Field) []otelcore.KeyValue {
|
|
encoder := &bridgeFieldEncoder{}
|
|
for _, field := range fields {
|
|
field.Marshal(encoder)
|
|
}
|
|
return encoder.pairs
|
|
}
|
|
|
|
func (s *bridgeSpan) LogKV(alternatingKeyValues ...interface{}) {
|
|
fields, err := otlog.InterleavedKVToFields(alternatingKeyValues...)
|
|
if err != nil {
|
|
return
|
|
}
|
|
s.LogFields(fields...)
|
|
}
|
|
|
|
func (s *bridgeSpan) SetBaggageItem(restrictedKey, value string) ot.Span {
|
|
s.ctx.setBaggageItem(restrictedKey, value)
|
|
return s
|
|
}
|
|
|
|
func (s *bridgeSpan) BaggageItem(restrictedKey string) string {
|
|
return s.ctx.baggageItem(restrictedKey)
|
|
}
|
|
|
|
func (s *bridgeSpan) Tracer() ot.Tracer {
|
|
return s.tracer
|
|
}
|
|
|
|
func (s *bridgeSpan) LogEvent(event string) {
|
|
s.LogEventWithPayload(event, nil)
|
|
}
|
|
|
|
func (s *bridgeSpan) LogEventWithPayload(event string, payload interface{}) {
|
|
data := ot.LogData{
|
|
Event: event,
|
|
Payload: payload,
|
|
}
|
|
s.Log(data)
|
|
}
|
|
|
|
func (s *bridgeSpan) Log(data ot.LogData) {
|
|
record := data.ToLogRecord()
|
|
s.LogFields(record.Fields...)
|
|
}
|
|
|
|
type bridgeSetTracer struct {
|
|
isSet bool
|
|
otelTracer oteltrace.Tracer
|
|
|
|
warningHandler BridgeWarningHandler
|
|
warnOnce sync.Once
|
|
}
|
|
|
|
func (s *bridgeSetTracer) tracer() oteltrace.Tracer {
|
|
if !s.isSet {
|
|
s.warnOnce.Do(func() {
|
|
s.warningHandler("The OpenTelemetry tracer is not set, default no-op tracer is used! Call SetOpenTelemetryTracer to set it up.\n")
|
|
})
|
|
}
|
|
return s.otelTracer
|
|
}
|
|
|
|
// BridgeWarningHandler is a type of handler that receives warnings
|
|
// from the BridgeTracer.
|
|
type BridgeWarningHandler func(msg string)
|
|
|
|
// BridgeTracer is an implementation of the OpenTracing tracer, which
|
|
// translates the calls to the OpenTracing API into OpenTelemetry
|
|
// counterparts and calls the underlying OpenTelemetry tracer.
|
|
type BridgeTracer struct {
|
|
setTracer bridgeSetTracer
|
|
|
|
warningHandler BridgeWarningHandler
|
|
warnOnce sync.Once
|
|
}
|
|
|
|
var _ ot.Tracer = &BridgeTracer{}
|
|
var _ ot.TracerContextWithSpanExtension = &BridgeTracer{}
|
|
|
|
// NewBridgeTracer creates a new BridgeTracer. The new tracer forwards
|
|
// the calls to the OpenTelemetry Noop tracer, so it should be
|
|
// overridden with the SetOpenTelemetryTracer function. The warnings
|
|
// handler does nothing by default, so to override it use the
|
|
// SetWarningHandler function.
|
|
func NewBridgeTracer() *BridgeTracer {
|
|
return &BridgeTracer{
|
|
setTracer: bridgeSetTracer{
|
|
otelTracer: oteltrace.NoopTracer{},
|
|
},
|
|
warningHandler: func(msg string) {},
|
|
}
|
|
}
|
|
|
|
// SetWarningHandler overrides the warning handler.
|
|
func (t *BridgeTracer) SetWarningHandler(handler BridgeWarningHandler) {
|
|
t.setTracer.warningHandler = handler
|
|
t.warningHandler = handler
|
|
}
|
|
|
|
// SetWarningHandler overrides the underlying OpenTelemetry
|
|
// tracer. The passed tracer should know how to operate in the
|
|
// environment that uses OpenTracing API.
|
|
func (t *BridgeTracer) SetOpenTelemetryTracer(tracer oteltrace.Tracer) {
|
|
t.setTracer.otelTracer = tracer
|
|
t.setTracer.isSet = true
|
|
}
|
|
|
|
// StartSpan is a part of the implementation of the OpenTracing Tracer
|
|
// interface.
|
|
func (t *BridgeTracer) StartSpan(operationName string, opts ...ot.StartSpanOption) ot.Span {
|
|
sso := ot.StartSpanOptions{}
|
|
for _, opt := range opts {
|
|
opt.Apply(&sso)
|
|
}
|
|
// TODO: handle links, needs SpanData to be in the API first?
|
|
bReference, _ := otSpanReferencesToBridgeReferenceAndLinks(sso.References)
|
|
// TODO: handle span kind, needs SpanData to be in the API first?
|
|
attributes, _, hadTrueErrorTag := otTagsToOtelAttributesKindAndError(sso.Tags)
|
|
checkCtx := migration.WithDeferredSetup(context.Background())
|
|
checkCtx2, otelSpan := t.setTracer.tracer().Start(checkCtx, operationName, func(opts *oteltrace.SpanOptions) {
|
|
opts.Attributes = attributes
|
|
opts.StartTime = sso.StartTime
|
|
opts.Reference = bReference.ToOtelReference()
|
|
opts.RecordEvent = true
|
|
})
|
|
if checkCtx != checkCtx2 {
|
|
t.warnOnce.Do(func() {
|
|
t.warningHandler("SDK should have deferred the context setup, see the documentation of go.opentelemetry.io/experimental/bridge/opentracing/migration\n")
|
|
})
|
|
}
|
|
if hadTrueErrorTag {
|
|
otelSpan.SetStatus(codes.Unknown)
|
|
}
|
|
var otSpanContext ot.SpanContext
|
|
if bReference.spanContext != nil {
|
|
otSpanContext = bReference.spanContext
|
|
}
|
|
sctx := newBridgeSpanContext(otelSpan.SpanContext(), otSpanContext)
|
|
span := &bridgeSpan{
|
|
otelSpan: otelSpan,
|
|
ctx: sctx,
|
|
tracer: t,
|
|
}
|
|
|
|
return span
|
|
}
|
|
|
|
// ContextWithBridgeSpan sets up the context with the passed
|
|
// OpenTelemetry span as the active OpenTracing span.
|
|
//
|
|
// This function should be used by the OpenTelemetry tracers that want
|
|
// to be aware how to operate in the environment using OpenTracing
|
|
// API.
|
|
func (t *BridgeTracer) ContextWithBridgeSpan(ctx context.Context, span oteltrace.Span) context.Context {
|
|
var otSpanContext ot.SpanContext
|
|
if parentSpan := ot.SpanFromContext(ctx); parentSpan != nil {
|
|
otSpanContext = parentSpan.Context()
|
|
}
|
|
bCtx := newBridgeSpanContext(span.SpanContext(), otSpanContext)
|
|
bSpan := &bridgeSpan{
|
|
otelSpan: span,
|
|
ctx: bCtx,
|
|
tracer: t,
|
|
skipDeferHook: true,
|
|
}
|
|
return ot.ContextWithSpan(ctx, bSpan)
|
|
}
|
|
|
|
// ContextWithSpanHook is an implementation of the OpenTracing tracer
|
|
// extension interface. It will call the DeferredContextSetupHook
|
|
// function on the tracer if it implements the
|
|
// DeferredContextSetupTracerExtension interface.
|
|
func (t *BridgeTracer) ContextWithSpanHook(ctx context.Context, span ot.Span) context.Context {
|
|
bSpan, ok := span.(*bridgeSpan)
|
|
if !ok || bSpan.skipDeferHook {
|
|
return ctx
|
|
}
|
|
if tracerWithExtension, ok := bSpan.tracer.setTracer.tracer().(migration.DeferredContextSetupTracerExtension); ok {
|
|
ctx = tracerWithExtension.DeferredContextSetupHook(ctx, bSpan.otelSpan)
|
|
}
|
|
return ctx
|
|
}
|
|
|
|
type spanKindTODO struct{}
|
|
|
|
func otTagsToOtelAttributesKindAndError(tags map[string]interface{}) ([]otelcore.KeyValue, spanKindTODO, bool) {
|
|
kind := spanKindTODO{}
|
|
error := false
|
|
var pairs []otelcore.KeyValue
|
|
for k, v := range tags {
|
|
switch k {
|
|
case string(otext.SpanKind):
|
|
// TODO: java has some notion of span kind, it
|
|
// probably is related to some proto stuff
|
|
case string(otext.Error):
|
|
if b, ok := v.(bool); ok && b {
|
|
error = true
|
|
}
|
|
default:
|
|
pairs = append(pairs, otTagToOtelCoreKeyValue(k, v))
|
|
}
|
|
}
|
|
return pairs, kind, error
|
|
}
|
|
|
|
func otTagToOtelCoreKeyValue(k string, v interface{}) otelcore.KeyValue {
|
|
key := otTagToOtelCoreKey(k)
|
|
switch v.(type) {
|
|
case bool:
|
|
return key.Bool(v.(bool))
|
|
case int64:
|
|
return key.Int64(v.(int64))
|
|
case uint64:
|
|
return key.Uint64(v.(uint64))
|
|
case float64:
|
|
return key.Float64(v.(float64))
|
|
case int32:
|
|
return key.Int32(v.(int32))
|
|
case uint32:
|
|
return key.Uint32(v.(uint32))
|
|
case float32:
|
|
return key.Float32(v.(float32))
|
|
case int:
|
|
return key.Int(v.(int))
|
|
case uint:
|
|
return key.Uint(v.(uint))
|
|
case string:
|
|
return key.String(v.(string))
|
|
case []byte:
|
|
return key.Bytes(v.([]byte))
|
|
default:
|
|
return key.String(fmt.Sprint(v))
|
|
}
|
|
}
|
|
|
|
func otTagToOtelCoreKey(k string) otelcore.Key {
|
|
return otelcore.Key{
|
|
Name: k,
|
|
}
|
|
}
|
|
|
|
type bridgeReference struct {
|
|
spanContext *bridgeSpanContext
|
|
relationshipType oteltrace.RelationshipType
|
|
}
|
|
|
|
func (r bridgeReference) ToOtelReference() oteltrace.Reference {
|
|
if r.spanContext == nil {
|
|
return oteltrace.Reference{}
|
|
}
|
|
return oteltrace.Reference{
|
|
SpanContext: r.spanContext.otelSpanContext,
|
|
RelationshipType: r.relationshipType,
|
|
}
|
|
}
|
|
|
|
func otSpanReferencesToBridgeReferenceAndLinks(references []ot.SpanReference) (bridgeReference, []*bridgeSpanContext) {
|
|
if len(references) == 0 {
|
|
return bridgeReference{}, nil
|
|
}
|
|
first := references[0]
|
|
bReference := bridgeReference{
|
|
spanContext: mustGetBridgeSpanContext(first.ReferencedContext),
|
|
relationshipType: otSpanReferenceTypeToOtelRelationshipType(first.Type),
|
|
}
|
|
var links []*bridgeSpanContext
|
|
for _, reference := range references[1:] {
|
|
links = append(links, mustGetBridgeSpanContext(reference.ReferencedContext))
|
|
}
|
|
return bReference, links
|
|
}
|
|
|
|
func mustGetBridgeSpanContext(ctx ot.SpanContext) *bridgeSpanContext {
|
|
ourCtx, ok := ctx.(*bridgeSpanContext)
|
|
if !ok {
|
|
panic("oops, some foreign span context here")
|
|
}
|
|
return ourCtx
|
|
}
|
|
|
|
func otSpanReferenceTypeToOtelRelationshipType(srt ot.SpanReferenceType) oteltrace.RelationshipType {
|
|
switch srt {
|
|
case ot.ChildOfRef:
|
|
return oteltrace.ChildOfRelationship
|
|
case ot.FollowsFromRef:
|
|
return oteltrace.FollowsFromRelationship
|
|
default:
|
|
panic("fix yer code, it uses bogus opentracing reference type")
|
|
}
|
|
}
|
|
|
|
// TODO: these headers are most likely bogus
|
|
var (
|
|
traceIDHeader = http.CanonicalHeaderKey("x-otelbridge-trace-id")
|
|
spanIDHeader = http.CanonicalHeaderKey("x-otelbridge-span-id")
|
|
traceOptionsHeader = http.CanonicalHeaderKey("x-otelbridge-trace-options")
|
|
baggageHeaderPrefix = http.CanonicalHeaderKey("x-otelbridge-baggage-")
|
|
)
|
|
|
|
// Inject is a part of the implementation of the OpenTracing Tracer
|
|
// interface.
|
|
//
|
|
// Currently only the HTTPHeaders format is kinda sorta supported.
|
|
func (t *BridgeTracer) Inject(sm ot.SpanContext, format interface{}, carrier interface{}) error {
|
|
bridgeSC, ok := sm.(*bridgeSpanContext)
|
|
if !ok {
|
|
return ot.ErrInvalidSpanContext
|
|
}
|
|
if !bridgeSC.otelSpanContext.IsValid() {
|
|
return ot.ErrInvalidSpanContext
|
|
}
|
|
if builtinFormat, ok := format.(ot.BuiltinFormat); !ok || builtinFormat != ot.HTTPHeaders {
|
|
return ot.ErrUnsupportedFormat
|
|
}
|
|
hhcarrier, ok := carrier.(ot.HTTPHeadersCarrier)
|
|
if !ok {
|
|
return ot.ErrInvalidCarrier
|
|
}
|
|
hhcarrier.Set(traceIDHeader, traceIDString(bridgeSC.otelSpanContext.TraceID))
|
|
hhcarrier.Set(spanIDHeader, spanIDToString(bridgeSC.otelSpanContext.SpanID))
|
|
hhcarrier.Set(traceOptionsHeader, traceOptionsToString(bridgeSC.otelSpanContext.TraceOptions))
|
|
bridgeSC.ForeachBaggageItem(func(k, v string) bool {
|
|
// we assume that keys are already canonicalized
|
|
hhcarrier.Set(baggageHeaderPrefix+k, v)
|
|
return true
|
|
})
|
|
return nil
|
|
}
|
|
|
|
// mostly copied from core/span_context.go, but I prefer not to rely
|
|
// on some impl details
|
|
func traceIDString(traceID otelcore.TraceID) string {
|
|
return fmt.Sprintf("%.16x%.16x", traceID.High, traceID.Low)
|
|
}
|
|
|
|
func spanIDToString(spanID uint64) string {
|
|
return fmt.Sprintf("%.16x", spanID)
|
|
}
|
|
|
|
func traceOptionsToString(opts byte) string {
|
|
var parts []string
|
|
if opts&otelcore.TraceOptionSampled == otelcore.TraceOptionSampled {
|
|
parts = append(parts, "sampled")
|
|
}
|
|
return strings.Join(parts, ",")
|
|
}
|
|
|
|
// Extract is a part of the implementation of the OpenTracing Tracer
|
|
// interface.
|
|
//
|
|
// Currently only the HTTPHeaders format is kinda sorta supported.
|
|
func (t *BridgeTracer) Extract(format interface{}, carrier interface{}) (ot.SpanContext, error) {
|
|
if builtinFormat, ok := format.(ot.BuiltinFormat); !ok || builtinFormat != ot.HTTPHeaders {
|
|
return nil, ot.ErrUnsupportedFormat
|
|
}
|
|
hhcarrier, ok := carrier.(ot.HTTPHeadersCarrier)
|
|
if !ok {
|
|
return nil, ot.ErrInvalidCarrier
|
|
}
|
|
bridgeSC := &bridgeSpanContext{}
|
|
err := hhcarrier.ForeachKey(func(k, v string) error {
|
|
ck := http.CanonicalHeaderKey(k)
|
|
switch ck {
|
|
case traceIDHeader:
|
|
traceID, err := traceIDFromString(v)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
bridgeSC.otelSpanContext.TraceID = traceID
|
|
case spanIDHeader:
|
|
spanID, err := spanIDFromString(v)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
bridgeSC.otelSpanContext.SpanID = spanID
|
|
case traceOptionsHeader:
|
|
bridgeSC.otelSpanContext.TraceOptions = stringToTraceOptions(v)
|
|
default:
|
|
if strings.HasPrefix(ck, baggageHeaderPrefix) {
|
|
bk := strings.TrimPrefix(ck, baggageHeaderPrefix)
|
|
bridgeSC.setBaggageItem(bk, v)
|
|
}
|
|
}
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if !bridgeSC.otelSpanContext.IsValid() {
|
|
return nil, ot.ErrSpanContextNotFound
|
|
}
|
|
return bridgeSC, nil
|
|
}
|
|
|
|
func traceIDFromString(s string) (otelcore.TraceID, error) {
|
|
traceID := otelcore.TraceID{}
|
|
if len(s) != 32 {
|
|
return traceID, fmt.Errorf("invalid trace ID")
|
|
}
|
|
high, err := strconv.ParseUint(s[0:16], 16, 64)
|
|
if err != nil {
|
|
return traceID, err
|
|
}
|
|
low, err := strconv.ParseUint(s[16:32], 16, 64)
|
|
if err != nil {
|
|
return traceID, err
|
|
}
|
|
traceID.High, traceID.Low = high, low
|
|
return traceID, nil
|
|
}
|
|
|
|
func spanIDFromString(s string) (uint64, error) {
|
|
if len(s) != 16 {
|
|
return 0, fmt.Errorf("invalid span ID")
|
|
}
|
|
return strconv.ParseUint(s, 16, 64)
|
|
}
|
|
|
|
func stringToTraceOptions(s string) byte {
|
|
var opts byte
|
|
for _, part := range strings.Split(s, ",") {
|
|
switch part {
|
|
case "sampled":
|
|
opts |= otelcore.TraceOptionSampled
|
|
}
|
|
}
|
|
return opts
|
|
}
|