You've already forked opentelemetry-go
mirror of
https://github.com/open-telemetry/opentelemetry-go.git
synced 2025-10-08 23:21:56 +02:00
Update to new stdlib apis. The new Float64 is uniform, which resolves a long comment. https://cs.opensource.google/go/go/+/refs/tags/go1.24.2:src/math/rand/v2/rand.go;l=209 > // There are exactly 1<<53 float64s in [0,1). Use Intn(1<<53) / (1<<53). return float64(r.Uint64()<<11>>11) / (1 << 53) ``` goos: linux goarch: amd64 pkg: go.opentelemetry.io/otel/sdk/trace cpu: 12th Gen Intel(R) Core(TM) i7-1260P │ old.txt │ new.txt │ │ sec/op │ sec/op vs base │ TraceStart/with_a_simple_span-16 387.1n ± 5% 306.8n ± 15% -20.73% (p=0.000 n=10) TraceStart/with_several_links-16 542.2n ± 5% 501.0n ± 2% -7.61% (p=0.000 n=10) TraceStart/with_attributes-16 521.4n ± 14% 571.6n ± 6% +9.64% (p=0.009 n=10) geomean 478.3n 444.6n -7.05% │ old.txt │ new.txt │ │ B/op │ B/op vs base │ TraceStart/with_a_simple_span-16 528.0 ± 0% 528.0 ± 0% ~ (p=1.000 n=10) ¹ TraceStart/with_several_links-16 704.0 ± 0% 704.0 ± 0% ~ (p=1.000 n=10) ¹ TraceStart/with_attributes-16 784.0 ± 0% 784.0 ± 0% ~ (p=1.000 n=10) ¹ geomean 663.0 663.0 +0.00% ¹ all samples are equal │ old.txt │ new.txt │ │ allocs/op │ allocs/op vs base │ TraceStart/with_a_simple_span-16 2.000 ± 0% 2.000 ± 0% ~ (p=1.000 n=10) ¹ TraceStart/with_several_links-16 3.000 ± 0% 3.000 ± 0% ~ (p=1.000 n=10) ¹ TraceStart/with_attributes-16 4.000 ± 0% 4.000 ± 0% ~ (p=1.000 n=10) ¹ geomean 2.884 2.884 +0.00% ¹ all samples are equal ``` --------- Co-authored-by: Robert Pająk <pellared@hotmail.com> Co-authored-by: Damien Mathieu <42@dmathieu.com>
313 lines
7.5 KiB
Go
313 lines
7.5 KiB
Go
// Copyright The OpenTelemetry Authors
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
package internal // import "go.opentelemetry.io/otel/bridge/opentracing/internal"
|
|
|
|
import (
|
|
"context"
|
|
"math/rand/v2"
|
|
"reflect"
|
|
"sync"
|
|
"time"
|
|
|
|
"go.opentelemetry.io/otel/attribute"
|
|
"go.opentelemetry.io/otel/bridge/opentracing/migration"
|
|
"go.opentelemetry.io/otel/codes"
|
|
semconv "go.opentelemetry.io/otel/semconv/v1.26.0"
|
|
"go.opentelemetry.io/otel/trace"
|
|
"go.opentelemetry.io/otel/trace/embedded"
|
|
"go.opentelemetry.io/otel/trace/noop"
|
|
)
|
|
|
|
//nolint:revive // ignoring missing comments for unexported global variables in an internal package.
|
|
var (
|
|
ComponentKey = attribute.Key("component")
|
|
ServiceKey = attribute.Key("service")
|
|
StatusCodeKey = attribute.Key("status.code")
|
|
StatusMessageKey = attribute.Key("status.message")
|
|
ErrorKey = attribute.Key("error")
|
|
NameKey = attribute.Key("name")
|
|
)
|
|
|
|
type MockContextKeyValue struct {
|
|
Key interface{}
|
|
Value interface{}
|
|
}
|
|
|
|
type MockTracer struct {
|
|
embedded.Tracer
|
|
|
|
FinishedSpans []*MockSpan
|
|
SpareTraceIDs []trace.TraceID
|
|
SpareSpanIDs []trace.SpanID
|
|
SpareContextKeyValues []MockContextKeyValue
|
|
TraceFlags trace.TraceFlags
|
|
|
|
randLock sync.Mutex
|
|
rand *rand.ChaCha8
|
|
}
|
|
|
|
var (
|
|
_ trace.Tracer = &MockTracer{}
|
|
_ migration.DeferredContextSetupTracerExtension = &MockTracer{}
|
|
)
|
|
|
|
func NewMockTracer() *MockTracer {
|
|
u := rand.Uint32()
|
|
seed := [32]byte{byte(u), byte(u >> 8), byte(u >> 16), byte(u >> 24)}
|
|
return &MockTracer{
|
|
FinishedSpans: nil,
|
|
SpareTraceIDs: nil,
|
|
SpareSpanIDs: nil,
|
|
SpareContextKeyValues: nil,
|
|
|
|
rand: rand.NewChaCha8(seed),
|
|
}
|
|
}
|
|
|
|
func (t *MockTracer) Start(
|
|
ctx context.Context,
|
|
name string,
|
|
opts ...trace.SpanStartOption,
|
|
) (context.Context, trace.Span) {
|
|
config := trace.NewSpanStartConfig(opts...)
|
|
startTime := config.Timestamp()
|
|
if startTime.IsZero() {
|
|
startTime = time.Now()
|
|
}
|
|
spanContext := trace.NewSpanContext(trace.SpanContextConfig{
|
|
TraceID: t.getTraceID(ctx, &config),
|
|
SpanID: t.getSpanID(),
|
|
TraceFlags: t.TraceFlags,
|
|
})
|
|
span := &MockSpan{
|
|
mockTracer: t,
|
|
officialTracer: t,
|
|
spanContext: spanContext,
|
|
Attributes: config.Attributes(),
|
|
StartTime: startTime,
|
|
EndTime: time.Time{},
|
|
ParentSpanID: t.getParentSpanID(ctx, &config),
|
|
Events: nil,
|
|
SpanKind: trace.ValidateSpanKind(config.SpanKind()),
|
|
}
|
|
if !migration.SkipContextSetup(ctx) {
|
|
ctx = trace.ContextWithSpan(ctx, span)
|
|
ctx = t.addSpareContextValue(ctx)
|
|
}
|
|
return ctx, span
|
|
}
|
|
|
|
func (t *MockTracer) addSpareContextValue(ctx context.Context) context.Context {
|
|
if len(t.SpareContextKeyValues) > 0 {
|
|
pair := t.SpareContextKeyValues[0]
|
|
t.SpareContextKeyValues[0] = MockContextKeyValue{}
|
|
t.SpareContextKeyValues = t.SpareContextKeyValues[1:]
|
|
if len(t.SpareContextKeyValues) == 0 {
|
|
t.SpareContextKeyValues = nil
|
|
}
|
|
ctx = context.WithValue(ctx, pair.Key, pair.Value)
|
|
}
|
|
return ctx
|
|
}
|
|
|
|
func (t *MockTracer) getTraceID(ctx context.Context, config *trace.SpanConfig) trace.TraceID {
|
|
if parent := t.getParentSpanContext(ctx, config); parent.IsValid() {
|
|
return parent.TraceID()
|
|
}
|
|
if len(t.SpareTraceIDs) > 0 {
|
|
traceID := t.SpareTraceIDs[0]
|
|
t.SpareTraceIDs = t.SpareTraceIDs[1:]
|
|
if len(t.SpareTraceIDs) == 0 {
|
|
t.SpareTraceIDs = nil
|
|
}
|
|
return traceID
|
|
}
|
|
return t.getRandTraceID()
|
|
}
|
|
|
|
func (t *MockTracer) getParentSpanID(ctx context.Context, config *trace.SpanConfig) trace.SpanID {
|
|
if parent := t.getParentSpanContext(ctx, config); parent.IsValid() {
|
|
return parent.SpanID()
|
|
}
|
|
return trace.SpanID{}
|
|
}
|
|
|
|
func (t *MockTracer) getParentSpanContext(ctx context.Context, config *trace.SpanConfig) trace.SpanContext {
|
|
if !config.NewRoot() {
|
|
return trace.SpanContextFromContext(ctx)
|
|
}
|
|
return trace.SpanContext{}
|
|
}
|
|
|
|
func (t *MockTracer) getSpanID() trace.SpanID {
|
|
if len(t.SpareSpanIDs) > 0 {
|
|
spanID := t.SpareSpanIDs[0]
|
|
t.SpareSpanIDs = t.SpareSpanIDs[1:]
|
|
if len(t.SpareSpanIDs) == 0 {
|
|
t.SpareSpanIDs = nil
|
|
}
|
|
return spanID
|
|
}
|
|
return t.getRandSpanID()
|
|
}
|
|
|
|
func (t *MockTracer) getRandSpanID() trace.SpanID {
|
|
t.randLock.Lock()
|
|
defer t.randLock.Unlock()
|
|
|
|
sid := trace.SpanID{}
|
|
_, _ = t.rand.Read(sid[:])
|
|
|
|
return sid
|
|
}
|
|
|
|
func (t *MockTracer) getRandTraceID() trace.TraceID {
|
|
t.randLock.Lock()
|
|
defer t.randLock.Unlock()
|
|
|
|
tid := trace.TraceID{}
|
|
_, _ = t.rand.Read(tid[:])
|
|
|
|
return tid
|
|
}
|
|
|
|
func (t *MockTracer) DeferredContextSetupHook(ctx context.Context, span trace.Span) context.Context {
|
|
return t.addSpareContextValue(ctx)
|
|
}
|
|
|
|
type MockEvent struct {
|
|
Timestamp time.Time
|
|
Name string
|
|
Attributes []attribute.KeyValue
|
|
}
|
|
|
|
type MockLink struct {
|
|
SpanContext trace.SpanContext
|
|
Attributes []attribute.KeyValue
|
|
}
|
|
|
|
type MockSpan struct {
|
|
embedded.Span
|
|
|
|
mockTracer *MockTracer
|
|
officialTracer trace.Tracer
|
|
spanContext trace.SpanContext
|
|
SpanKind trace.SpanKind
|
|
recording bool
|
|
|
|
Attributes []attribute.KeyValue
|
|
StartTime time.Time
|
|
EndTime time.Time
|
|
ParentSpanID trace.SpanID
|
|
Events []MockEvent
|
|
Links []MockLink
|
|
}
|
|
|
|
var (
|
|
_ trace.Span = &MockSpan{}
|
|
_ migration.OverrideTracerSpanExtension = &MockSpan{}
|
|
)
|
|
|
|
func (s *MockSpan) SpanContext() trace.SpanContext {
|
|
return s.spanContext
|
|
}
|
|
|
|
func (s *MockSpan) IsRecording() bool {
|
|
return s.recording
|
|
}
|
|
|
|
func (s *MockSpan) SetStatus(code codes.Code, msg string) {
|
|
s.SetAttributes(StatusCodeKey.Int(int(code)), StatusMessageKey.String(msg))
|
|
}
|
|
|
|
func (s *MockSpan) SetName(name string) {
|
|
s.SetAttributes(NameKey.String(name))
|
|
}
|
|
|
|
func (s *MockSpan) SetError(v bool) {
|
|
s.SetAttributes(ErrorKey.Bool(v))
|
|
}
|
|
|
|
func (s *MockSpan) SetAttributes(attributes ...attribute.KeyValue) {
|
|
s.applyUpdate(attributes)
|
|
}
|
|
|
|
func (s *MockSpan) applyUpdate(update []attribute.KeyValue) {
|
|
updateM := make(map[attribute.Key]attribute.Value, len(update))
|
|
for _, kv := range update {
|
|
updateM[kv.Key] = kv.Value
|
|
}
|
|
|
|
seen := make(map[attribute.Key]struct{})
|
|
for i, kv := range s.Attributes {
|
|
if v, ok := updateM[kv.Key]; ok {
|
|
s.Attributes[i].Value = v
|
|
seen[kv.Key] = struct{}{}
|
|
}
|
|
}
|
|
|
|
for k, v := range updateM {
|
|
if _, ok := seen[k]; ok {
|
|
continue
|
|
}
|
|
s.Attributes = append(s.Attributes, attribute.KeyValue{Key: k, Value: v})
|
|
}
|
|
}
|
|
|
|
func (s *MockSpan) End(options ...trace.SpanEndOption) {
|
|
if !s.EndTime.IsZero() {
|
|
return // already finished
|
|
}
|
|
config := trace.NewSpanEndConfig(options...)
|
|
endTime := config.Timestamp()
|
|
if endTime.IsZero() {
|
|
endTime = time.Now()
|
|
}
|
|
s.EndTime = endTime
|
|
s.mockTracer.FinishedSpans = append(s.mockTracer.FinishedSpans, s)
|
|
}
|
|
|
|
func (s *MockSpan) RecordError(err error, opts ...trace.EventOption) {
|
|
if err == nil {
|
|
return // no-op on nil error
|
|
}
|
|
|
|
if !s.EndTime.IsZero() {
|
|
return // already finished
|
|
}
|
|
|
|
s.SetStatus(codes.Error, "")
|
|
opts = append(opts, trace.WithAttributes(
|
|
semconv.ExceptionType(reflect.TypeOf(err).String()),
|
|
semconv.ExceptionMessage(err.Error()),
|
|
))
|
|
s.AddEvent(semconv.ExceptionEventName, opts...)
|
|
}
|
|
|
|
func (s *MockSpan) Tracer() trace.Tracer {
|
|
return s.officialTracer
|
|
}
|
|
|
|
func (s *MockSpan) AddEvent(name string, o ...trace.EventOption) {
|
|
c := trace.NewEventConfig(o...)
|
|
s.Events = append(s.Events, MockEvent{
|
|
Timestamp: c.Timestamp(),
|
|
Name: name,
|
|
Attributes: c.Attributes(),
|
|
})
|
|
}
|
|
|
|
func (s *MockSpan) AddLink(link trace.Link) {
|
|
s.Links = append(s.Links, MockLink{
|
|
SpanContext: link.SpanContext,
|
|
Attributes: link.Attributes,
|
|
})
|
|
}
|
|
|
|
func (s *MockSpan) OverrideTracer(tracer trace.Tracer) {
|
|
s.officialTracer = tracer
|
|
}
|
|
|
|
func (s *MockSpan) TracerProvider() trace.TracerProvider { return noop.NewTracerProvider() }
|