tracing/smithyoteltracing/adapt.go (106 lines of code) (raw):
package smithyoteltracing
import (
"context"
"github.com/aws/smithy-go/tracing"
otelcodes "go.opentelemetry.io/otel/codes"
oteltrace "go.opentelemetry.io/otel/trace"
)
// Adapt wraps a concrete OpenTelemetry SDK TraceProvider for use with Smithy
// SDK clients.
//
// Adapt can be called multiple times on a single TracerProvider.
func Adapt(tp oteltrace.TracerProvider) tracing.TracerProvider {
return &tracerProvider{tp}
}
type tracerProvider struct {
otel oteltrace.TracerProvider
}
var _ tracing.TracerProvider = (*tracerProvider)(nil)
func (p *tracerProvider) Tracer(scope string, opts ...tracing.TracerOption) tracing.Tracer {
var options tracing.TracerOptions
for _, opt := range opts {
opt(&options)
}
t := p.otel.Tracer(scope, oteltrace.WithInstrumentationAttributes(
toOTELKeyValues(options.Properties)...,
))
return &tracer{t}
}
type tracer struct {
otel oteltrace.Tracer
}
var _ tracing.Tracer = (*tracer)(nil)
func (t *tracer) StartSpan(ctx context.Context, name string, opts ...tracing.SpanOption) (context.Context, tracing.Span) {
// We do some context value juggling with our adapted Span to ensure the
// following:
// (1) Our adapted Span is what actually persists on the context and
// is what callers are getting and recording to.
// (2) OTEL itself sees any pre-existing Span such that the parent-child
// relationship of concrete OTEL spans is maintained.
ours, ok := tracing.GetSpan(ctx)
if ok {
ctx = oteltrace.ContextWithSpan(ctx, ours.(*span).otel) // (2)
}
var options tracing.SpanOptions
for _, opt := range opts {
opt(&options)
}
kind := toOTELSpanKind(options.Kind)
ctx, theirs := t.otel.Start(ctx, name, oteltrace.WithSpanKind(kind))
ours = &span{
otel: theirs,
name: name,
}
for k, v := range options.Properties.Values() {
ours.SetProperty(k, v)
}
return tracing.WithSpan(ctx, ours) /* (1) */, ours
}
type span struct {
otel oteltrace.Span
name string
}
var _ tracing.Span = (*span)(nil)
func (s *span) Name() string {
return s.name
}
func (s *span) Context() tracing.SpanContext {
ctx := s.otel.SpanContext()
return tracing.SpanContext{
TraceID: ctx.TraceID().String(),
SpanID: ctx.SpanID().String(),
IsRemote: ctx.IsRemote(),
}
}
func (s *span) AddEvent(name string, opts ...tracing.EventOption) {
var options tracing.EventOptions
for _, opt := range opts {
opt(&options)
}
s.otel.AddEvent(name, oteltrace.WithAttributes(
toOTELKeyValues(options.Properties)...,
))
}
func (s *span) SetProperty(k, v any) {
s.otel.SetAttributes(toOTELKeyValue(k, v))
}
func (s *span) SetStatus(status tracing.SpanStatus) {
s.otel.SetStatus(toOTELSpanStatus(status), "")
}
func (s *span) End() {
s.otel.End()
}
func toOTELSpanKind(v tracing.SpanKind) oteltrace.SpanKind {
switch v {
case tracing.SpanKindClient:
return oteltrace.SpanKindClient
case tracing.SpanKindServer:
return oteltrace.SpanKindServer
case tracing.SpanKindProducer:
return oteltrace.SpanKindProducer
case tracing.SpanKindConsumer:
return oteltrace.SpanKindConsumer
default:
return oteltrace.SpanKindInternal
}
}
func toOTELSpanStatus(v tracing.SpanStatus) otelcodes.Code {
switch v {
case tracing.SpanStatusOK:
return otelcodes.Ok
case tracing.SpanStatusError:
return otelcodes.Error
default:
return otelcodes.Unset
}
}