215 lines
5.0 KiB
Go
215 lines
5.0 KiB
Go
package mocktracer // import "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/mocktracer"
|
|
|
|
import (
|
|
"fmt"
|
|
"sync"
|
|
"time"
|
|
|
|
"gopkg.in/DataDog/dd-trace-go.v1/ddtrace"
|
|
"gopkg.in/DataDog/dd-trace-go.v1/ddtrace/ext"
|
|
)
|
|
|
|
var _ ddtrace.Span = (*mockspan)(nil)
|
|
var _ Span = (*mockspan)(nil)
|
|
|
|
// Span is an interface that allows querying a span returned by the mock tracer.
|
|
type Span interface {
|
|
// SpanID returns the span's ID.
|
|
SpanID() uint64
|
|
|
|
// TraceID returns the span's trace ID.
|
|
TraceID() uint64
|
|
|
|
// ParentID returns the span's parent ID.
|
|
ParentID() uint64
|
|
|
|
// StartTime returns the time when the span has started.
|
|
StartTime() time.Time
|
|
|
|
// FinishTime returns the time when the span has finished.
|
|
FinishTime() time.Time
|
|
|
|
// OperationName returns the operation name held by this span.
|
|
OperationName() string
|
|
|
|
// Tag returns the value of the tag at key k.
|
|
Tag(k string) interface{}
|
|
|
|
// Tags returns a copy of all the tags in this span.
|
|
Tags() map[string]interface{}
|
|
|
|
// Context returns the span's SpanContext.
|
|
Context() ddtrace.SpanContext
|
|
|
|
// Stringer allows pretty-printing the span's fields for debugging.
|
|
fmt.Stringer
|
|
}
|
|
|
|
func newSpan(t *mocktracer, operationName string, cfg *ddtrace.StartSpanConfig) *mockspan {
|
|
if cfg.Tags == nil {
|
|
cfg.Tags = make(map[string]interface{})
|
|
}
|
|
if cfg.Tags[ext.ResourceName] == nil {
|
|
cfg.Tags[ext.ResourceName] = operationName
|
|
}
|
|
s := &mockspan{
|
|
name: operationName,
|
|
tracer: t,
|
|
}
|
|
if cfg.StartTime.IsZero() {
|
|
s.startTime = time.Now()
|
|
} else {
|
|
s.startTime = cfg.StartTime
|
|
}
|
|
id := nextID()
|
|
s.context = &spanContext{spanID: id, traceID: id, span: s}
|
|
if ctx, ok := cfg.Parent.(*spanContext); ok {
|
|
if ctx.span != nil && s.tags[ext.ServiceName] == nil {
|
|
// if we have a local parent and no service, inherit the parent's
|
|
s.SetTag(ext.ServiceName, ctx.span.Tag(ext.ServiceName))
|
|
}
|
|
if ctx.hasSamplingPriority() {
|
|
s.SetTag(ext.SamplingPriority, ctx.samplingPriority())
|
|
}
|
|
s.parentID = ctx.spanID
|
|
s.context.priority = ctx.samplingPriority()
|
|
s.context.hasPriority = ctx.hasSamplingPriority()
|
|
s.context.traceID = ctx.traceID
|
|
s.context.baggage = make(map[string]string, len(ctx.baggage))
|
|
ctx.ForeachBaggageItem(func(k, v string) bool {
|
|
s.context.baggage[k] = v
|
|
return true
|
|
})
|
|
}
|
|
for k, v := range cfg.Tags {
|
|
s.SetTag(k, v)
|
|
}
|
|
return s
|
|
}
|
|
|
|
type mockspan struct {
|
|
sync.RWMutex // guards below fields
|
|
name string
|
|
tags map[string]interface{}
|
|
finishTime time.Time
|
|
|
|
startTime time.Time
|
|
parentID uint64
|
|
context *spanContext
|
|
tracer *mocktracer
|
|
}
|
|
|
|
// SetTag sets a given tag on the span.
|
|
func (s *mockspan) SetTag(key string, value interface{}) {
|
|
s.Lock()
|
|
defer s.Unlock()
|
|
if s.tags == nil {
|
|
s.tags = make(map[string]interface{}, 1)
|
|
}
|
|
if key == ext.SamplingPriority {
|
|
switch p := value.(type) {
|
|
case int:
|
|
s.context.setSamplingPriority(p)
|
|
case float64:
|
|
s.context.setSamplingPriority(int(p))
|
|
}
|
|
}
|
|
s.tags[key] = value
|
|
}
|
|
|
|
func (s *mockspan) FinishTime() time.Time {
|
|
s.RLock()
|
|
defer s.RUnlock()
|
|
return s.finishTime
|
|
}
|
|
|
|
func (s *mockspan) StartTime() time.Time { return s.startTime }
|
|
|
|
func (s *mockspan) Tag(k string) interface{} {
|
|
s.RLock()
|
|
defer s.RUnlock()
|
|
return s.tags[k]
|
|
}
|
|
|
|
func (s *mockspan) Tags() map[string]interface{} {
|
|
s.RLock()
|
|
defer s.RUnlock()
|
|
// copy
|
|
cp := make(map[string]interface{}, len(s.tags))
|
|
for k, v := range s.tags {
|
|
cp[k] = v
|
|
}
|
|
return cp
|
|
}
|
|
|
|
func (s *mockspan) TraceID() uint64 { return s.context.traceID }
|
|
|
|
func (s *mockspan) SpanID() uint64 { return s.context.spanID }
|
|
|
|
func (s *mockspan) ParentID() uint64 { return s.parentID }
|
|
|
|
func (s *mockspan) OperationName() string {
|
|
s.RLock()
|
|
defer s.RUnlock()
|
|
return s.name
|
|
}
|
|
|
|
// SetOperationName resets the original operation name to the given one.
|
|
func (s *mockspan) SetOperationName(operationName string) {
|
|
s.Lock()
|
|
defer s.Unlock()
|
|
s.name = operationName
|
|
return
|
|
}
|
|
|
|
// BaggageItem returns the baggage item with the given key.
|
|
func (s *mockspan) BaggageItem(key string) string {
|
|
return s.context.baggageItem(key)
|
|
}
|
|
|
|
// SetBaggageItem sets a new baggage item at the given key. The baggage
|
|
// item should propagate to all descendant spans, both in- and cross-process.
|
|
func (s *mockspan) SetBaggageItem(key, val string) {
|
|
s.context.setBaggageItem(key, val)
|
|
return
|
|
}
|
|
|
|
// Finish finishes the current span with the given options.
|
|
func (s *mockspan) Finish(opts ...ddtrace.FinishOption) {
|
|
var cfg ddtrace.FinishConfig
|
|
for _, fn := range opts {
|
|
fn(&cfg)
|
|
}
|
|
var t time.Time
|
|
if cfg.FinishTime.IsZero() {
|
|
t = time.Now()
|
|
} else {
|
|
t = cfg.FinishTime
|
|
}
|
|
if cfg.Error != nil {
|
|
s.SetTag(ext.Error, cfg.Error)
|
|
}
|
|
s.Lock()
|
|
s.finishTime = t
|
|
s.Unlock()
|
|
s.tracer.addFinishedSpan(s)
|
|
}
|
|
|
|
// String implements fmt.Stringer.
|
|
func (s *mockspan) String() string {
|
|
sc := s.context
|
|
return fmt.Sprintf(`
|
|
name: %s
|
|
tags: %#v
|
|
start: %s
|
|
finish: %s
|
|
id: %d
|
|
parent: %d
|
|
trace: %d
|
|
baggage: %#v
|
|
`, s.name, s.tags, s.startTime, s.finishTime, sc.spanID, s.parentID, sc.traceID, sc.baggage)
|
|
}
|
|
|
|
// Context returns the SpanContext of this Span.
|
|
func (s *mockspan) Context() ddtrace.SpanContext { return s.context }
|