From adc2b62c22fae5c4ec03e8c808d094eaf08a5a2a Mon Sep 17 00:00:00 2001 From: Alex Antonov Date: Wed, 15 May 2019 11:04:06 -0500 Subject: [PATCH] Upgraded DataDog tracing library to 1.14.0 --- Gopkg.lock | 8 +- Gopkg.toml | 2 +- .../DataDog/dd-trace-go.v1/ddtrace/ddtrace.go | 10 + .../dd-trace-go.v1/ddtrace/ext/tags.go | 25 ++- .../ddtrace/opentracer/option.go | 5 + .../dd-trace-go.v1/ddtrace/tracer/option.go | 55 +++++- .../dd-trace-go.v1/ddtrace/tracer/sampler.go | 2 +- .../dd-trace-go.v1/ddtrace/tracer/span.go | 99 +++++++++- .../ddtrace/tracer/spancontext.go | 104 +++++++--- .../dd-trace-go.v1/ddtrace/tracer/textmap.go | 180 +++++++++++++++++- .../dd-trace-go.v1/ddtrace/tracer/tracer.go | 25 ++- .../ddtrace/tracer/transport.go | 2 +- .../internal/globalconfig/globalconfig.go | 28 +++ 13 files changed, 485 insertions(+), 60 deletions(-) create mode 100644 vendor/gopkg.in/DataDog/dd-trace-go.v1/internal/globalconfig/globalconfig.go diff --git a/Gopkg.lock b/Gopkg.lock index 137690a59..261d6446d 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -1864,7 +1864,7 @@ version = "v1.20.1" [[projects]] - digest = "1:b886012746f19e2a7c6c3901ea9f86e8a5e32ff2b4407086f4f3181269976957" + digest = "1:b49eceff862a3048ec28dad1fce40bcbdc1703119dbad35d7e5f1beb4f9a4527" name = "gopkg.in/DataDog/dd-trace-go.v1" packages = [ "ddtrace", @@ -1872,10 +1872,11 @@ "ddtrace/internal", "ddtrace/opentracer", "ddtrace/tracer", + "internal/globalconfig", ] pruneopts = "NUT" - revision = "7fb2bce4b1ed6ab61f7a9e1be30dea56de19db7c" - version = "v1.8.0" + revision = "c19e9e56d5b5b71b6507ce1b0ec06d85aa3705a1" + version = "v1.14.0" [[projects]] digest = "1:c970218a20933dd0a2eb2006de922217fa9276f57d25009b2a934eb1c50031cc" @@ -2287,6 +2288,7 @@ "github.com/opentracing/opentracing-go/log", "github.com/openzipkin-contrib/zipkin-go-opentracing", "github.com/patrickmn/go-cache", + "github.com/pmezard/go-difflib/difflib", "github.com/prometheus/client_golang/prometheus", "github.com/prometheus/client_golang/prometheus/promhttp", "github.com/prometheus/client_model/go", diff --git a/Gopkg.toml b/Gopkg.toml index 28922748d..d69d4f533 100644 --- a/Gopkg.toml +++ b/Gopkg.toml @@ -285,7 +285,7 @@ required = [ [[constraint]] name = "gopkg.in/DataDog/dd-trace-go.v1" - version = "1.7.0" + version = "1.13.0" [[constraint]] name = "github.com/instana/go-sensor" diff --git a/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/ddtrace.go b/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/ddtrace.go index bfd40561e..2a49bf6d0 100644 --- a/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/ddtrace.go +++ b/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/ddtrace.go @@ -91,6 +91,12 @@ type FinishConfig struct { // NoDebugStack will prevent any set errors from generating an attached stack trace tag. NoDebugStack bool + + // StackFrames specifies the number of stack frames to be attached in spans that finish with errors. + StackFrames uint + + // SkipStackFrames specifies the offset at which to start reporting stack frames from the stack. + SkipStackFrames uint } // StartSpanConfig holds the configuration for starting a new span. It is usually passed @@ -108,4 +114,8 @@ type StartSpanConfig struct { // Tags holds a set of key/value pairs that should be set as metadata on the // new span. Tags map[string]interface{} + + // Force-set the SpanID, rather than use a random number. If no Parent SpanContext is present, + // then this will also set the TraceID to the same value. + SpanID uint64 } diff --git a/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/ext/tags.go b/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/ext/tags.go index 6775e2aac..c21f84825 100644 --- a/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/ext/tags.go +++ b/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/ext/tags.go @@ -27,10 +27,15 @@ const ( // HTTPURL sets the HTTP URL for a span. HTTPURL = "http.url" - // TODO: In the next major version, suffix these constants (SpanType, etc) - // with "*Key" (SpanTypeKey, etc) to more easily differentiate between + // TODO: In the next major version, prefix these constants (SpanType, etc) + // with "Key*" (KeySpanType, etc) to more easily differentiate between // constants representing tag values and constants representing keys. + // SpanName is a pseudo-key for setting a span's operation name by means of + // a tag. It is mostly here to facilitate vendor-agnostic frameworks like Opentracing + // and OpenCensus. + SpanName = "span.name" + // SpanType defines the Span type (web, db, cache). SpanType = "span.type" @@ -54,4 +59,20 @@ const ( // Environment specifies the environment to use with a trace. Environment = "env" + + // EventSampleRate specifies the rate at which this span will be sampled + // as an APM event. + EventSampleRate = "_dd1.sr.eausr" + + // AnalyticsEvent specifies whether the span should be recorded as a Trace + // Search & Analytics event. + AnalyticsEvent = "analytics.event" + + // ManualKeep is a tag which specifies that the trace to which this span + // belongs to should be kept when set to true. + ManualKeep = "manual.keep" + + // ManualDrop is a tag which specifies that the trace to which this span + // belongs to should be dropped when set to true. + ManualDrop = "manual.drop" ) diff --git a/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/opentracer/option.go b/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/opentracer/option.go index cbae45184..6ee859a4a 100644 --- a/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/opentracer/option.go +++ b/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/opentracer/option.go @@ -18,6 +18,11 @@ func ResourceName(name string) opentracing.StartSpanOption { return opentracing.Tag{Key: ext.ResourceName, Value: name} } +// SpanName sets the Datadog operation name for the span. +func SpanName(name string) opentracing.StartSpanOption { + return opentracing.Tag{Key: ext.SpanName, Value: name} +} + // SpanType can be used with opentracing.StartSpan to set the type of a span. func SpanType(name string) opentracing.StartSpanOption { return opentracing.Tag{Key: ext.SpanType, Value: name} diff --git a/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer/option.go b/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer/option.go index 789c9aa38..5e3031f21 100644 --- a/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer/option.go +++ b/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer/option.go @@ -1,6 +1,7 @@ package tracer import ( + "log" "net/http" "os" "path/filepath" @@ -8,6 +9,7 @@ import ( "gopkg.in/DataDog/dd-trace-go.v1/ddtrace" "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/ext" + "gopkg.in/DataDog/dd-trace-go.v1/internal/globalconfig" ) // config holds the tracer configuration. @@ -21,7 +23,7 @@ type config struct { // sampler specifies the sampler that will be used for sampling traces. sampler Sampler - // agentAddr specifies the hostname and of the agent where the traces + // agentAddr specifies the hostname and port of the agent where the traces // are sent to. agentAddr string @@ -37,6 +39,10 @@ type config struct { // httpRoundTripper defines the http.RoundTripper used by the agent transport. httpRoundTripper http.RoundTripper + + // hostname is automatically assigned when the DD_TRACE_REPORT_HOSTNAME is set to true, + // and is added as a special tag to the root span of traces. + hostname string } // StartOption represents a function that can be provided as a parameter to Start. @@ -47,6 +53,14 @@ func defaults(c *config) { c.serviceName = filepath.Base(os.Args[0]) c.sampler = NewAllSampler() c.agentAddr = defaultAddress + + if os.Getenv("DD_TRACE_REPORT_HOSTNAME") == "true" { + var err error + c.hostname, err = os.Hostname() + if err != nil { + log.Printf("%sunable to look up hostname: %v\n", errorPrefix, err) + } + } } // WithPrioritySampling is deprecated, and priority sampling is enabled by default. @@ -117,6 +131,22 @@ func WithHTTPRoundTripper(r http.RoundTripper) StartOption { } } +// WithAnalytics allows specifying whether Trace Search & Analytics should be enabled +// for integrations. +func WithAnalytics(on bool) StartOption { + if on { + return WithAnalyticsRate(1.0) + } + return WithAnalyticsRate(0.0) +} + +// WithAnalyticsRate sets the global sampling rate for sampling APM events. +func WithAnalyticsRate(rate float64) StartOption { + return func(_ *config) { + globalconfig.SetAnalyticsRate(rate) + } +} + // StartSpanOption is a configuration option for StartSpan. It is aliased in order // to help godoc group all the functions returning it together. It is considered // more correct to refer to it as the type as the origin, ddtrace.StartSpanOption. @@ -149,6 +179,15 @@ func SpanType(name string) StartSpanOption { return Tag(ext.SpanType, name) } +// WithSpanID sets the SpanID on the started span, instead of using a random number. +// If there is no parent Span (eg from ChildOf), then the TraceID will also be set to the +// value given here. +func WithSpanID(id uint64) StartSpanOption { + return func(cfg *ddtrace.StartSpanConfig) { + cfg.SpanID = id + } +} + // ChildOf tells StartSpan to use the given span context as a parent for the // created span. func ChildOf(ctx ddtrace.SpanContext) StartSpanOption { @@ -179,7 +218,8 @@ func FinishTime(t time.Time) FinishOption { } // WithError marks the span as having had an error. It uses the information from -// err to set tags such as the error message, error type and stack trace. +// err to set tags such as the error message, error type and stack trace. It has +// no effect if the error is nil. func WithError(err error) FinishOption { return func(cfg *ddtrace.FinishConfig) { cfg.Error = err @@ -194,3 +234,14 @@ func NoDebugStack() FinishOption { cfg.NoDebugStack = true } } + +// StackFrames limits the number of stack frames included into erroneous spans to n, starting from skip. +func StackFrames(n, skip uint) FinishOption { + if n == 0 { + return NoDebugStack() + } + return func(cfg *ddtrace.FinishConfig) { + cfg.StackFrames = n + cfg.SkipStackFrames = skip + } +} diff --git a/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer/sampler.go b/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer/sampler.go index 26d814cef..773354caa 100644 --- a/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer/sampler.go +++ b/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer/sampler.go @@ -140,5 +140,5 @@ func (ps *prioritySampler) apply(spn *span) { } else { spn.SetTag(ext.SamplingPriority, ext.PriorityAutoReject) } - spn.SetTag(samplingPriorityRateKey, rate) + spn.SetTag(keySamplingPriorityRate, rate) } diff --git a/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer/span.go b/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer/span.go index f8113710f..75d963f6a 100644 --- a/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer/span.go +++ b/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer/span.go @@ -5,7 +5,9 @@ package tracer import ( "fmt" "reflect" + "runtime" "runtime/debug" + "strconv" "strings" "sync" "time" @@ -30,6 +32,13 @@ var ( _ msgp.Decodable = (*spanLists)(nil) ) +// errorConfig holds customization options for setting error tags. +type errorConfig struct { + noDebugStack bool + stackFrames uint + stackSkip uint +} + // span represents a computation. Callers must call Finish when a span is // complete to ensure it's submitted. type span struct { @@ -80,8 +89,13 @@ func (s *span) SetTag(key string, value interface{}) { if s.finished { return } - if key == ext.Error { - s.setTagError(value, true) + switch key { + case ext.Error: + s.setTagError(value, &errorConfig{}) + return + } + if v, ok := value.(bool); ok { + s.setTagBool(key, v) return } if v, ok := value.(string); ok { @@ -99,7 +113,7 @@ func (s *span) SetTag(key string, value interface{}) { // setTagError sets the error tag. It accounts for various valid scenarios. // This method is not safe for concurrent use. -func (s *span) setTagError(value interface{}, debugStack bool) { +func (s *span) setTagError(value interface{}, cfg *errorConfig) { if s.finished { return } @@ -117,8 +131,12 @@ func (s *span) setTagError(value interface{}, debugStack bool) { s.Error = 1 s.Meta[ext.ErrorMsg] = v.Error() s.Meta[ext.ErrorType] = reflect.TypeOf(v).String() - if debugStack { - s.Meta[ext.ErrorStack] = string(debug.Stack()) + if !cfg.noDebugStack { + if cfg.stackFrames == 0 { + s.Meta[ext.ErrorStack] = string(debug.Stack()) + } else { + s.Meta[ext.ErrorStack] = takeStacktrace(cfg.stackFrames, cfg.stackSkip) + } } case nil: // no error @@ -130,9 +148,40 @@ func (s *span) setTagError(value interface{}, debugStack bool) { } } +// takeStacktrace takes stacktrace +func takeStacktrace(n, skip uint) string { + var builder strings.Builder + pcs := make([]uintptr, n) + + // +2 to exclude runtime.Callers and takeStacktrace + numFrames := runtime.Callers(2+int(skip), pcs) + if numFrames == 0 { + return "" + } + frames := runtime.CallersFrames(pcs[:numFrames]) + for i := 0; ; i++ { + frame, more := frames.Next() + if i != 0 { + builder.WriteByte('\n') + } + builder.WriteString(frame.Function) + builder.WriteByte('\n') + builder.WriteByte('\t') + builder.WriteString(frame.File) + builder.WriteByte(':') + builder.WriteString(strconv.Itoa(frame.Line)) + if !more { + break + } + } + return builder.String() +} + // setTagString sets a string tag. This method is not safe for concurrent use. func (s *span) setTagString(key, v string) { switch key { + case ext.SpanName: + s.Name = v case ext.ServiceName: s.Service = v case ext.ResourceName: @@ -144,13 +193,39 @@ func (s *span) setTagString(key, v string) { } } +// setTagBool sets a boolean tag on the span. +func (s *span) setTagBool(key string, v bool) { + switch key { + case ext.AnalyticsEvent: + if v { + s.setTagNumeric(ext.EventSampleRate, 1.0) + } else { + s.setTagNumeric(ext.EventSampleRate, 0.0) + } + case ext.ManualDrop: + if v { + s.setTagNumeric(ext.SamplingPriority, ext.PriorityUserReject) + } + case ext.ManualKeep: + if v { + s.setTagNumeric(ext.SamplingPriority, ext.PriorityUserKeep) + } + default: + if v { + s.setTagString(key, "true") + } else { + s.setTagString(key, "false") + } + } +} + // setTagNumeric sets a numeric tag, in our case called a metric. This method // is not safe for concurrent use. func (s *span) setTagNumeric(key string, v float64) { switch key { case ext.SamplingPriority: // setting sampling priority per spec - s.Metrics[samplingPriorityKey] = v + s.Metrics[keySamplingPriority] = v s.context.setSamplingPriority(int(v)) default: s.Metrics[key] = v @@ -172,7 +247,11 @@ func (s *span) Finish(opts ...ddtrace.FinishOption) { } if cfg.Error != nil { s.Lock() - s.setTagError(cfg.Error, !cfg.NoDebugStack) + s.setTagError(cfg.Error, &errorConfig{ + noDebugStack: cfg.NoDebugStack, + stackFrames: cfg.StackFrames, + stackSkip: cfg.SkipStackFrames, + }) s.Unlock() } s.finish(t) @@ -236,6 +315,8 @@ func (s *span) String() string { } const ( - samplingPriorityKey = "_sampling_priority_v1" - samplingPriorityRateKey = "_sampling_priority_rate_v1" + keySamplingPriority = "_sampling_priority_v1" + keySamplingPriorityRate = "_sampling_priority_rate_v1" + keyOrigin = "_dd.origin" + keyHostname = "_dd.hostname" ) diff --git a/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer/spancontext.go b/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer/spancontext.go index 2def04015..29c81633a 100644 --- a/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer/spancontext.go +++ b/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer/spancontext.go @@ -25,10 +25,9 @@ type spanContext struct { traceID uint64 spanID uint64 - mu sync.RWMutex // guards below fields - baggage map[string]string - priority int - hasPriority bool + mu sync.RWMutex // guards below fields + baggage map[string]string + origin string // e.g. "synthetics" } // newSpanContext creates a new SpanContext to serve as context for the given @@ -42,15 +41,10 @@ func newSpanContext(span *span, parent *spanContext) *spanContext { spanID: span.SpanID, span: span, } - if v, ok := span.Metrics[samplingPriorityKey]; ok { - context.hasPriority = true - context.priority = int(v) - } if parent != nil { context.trace = parent.trace context.drop = parent.drop - context.hasPriority = parent.hasSamplingPriority() - context.priority = parent.samplingPriority() + context.origin = parent.origin parent.ForeachBaggageItem(func(k, v string) bool { context.setBaggageItem(k, v) return true @@ -59,6 +53,10 @@ func newSpanContext(span *span, parent *spanContext) *spanContext { if context.trace == nil { context.trace = newTrace() } + if context.trace.root == nil { + // first span in the trace can safely be assumed to be the root + context.trace.root = span + } // put span in context's trace context.trace.push(span) return context @@ -82,22 +80,21 @@ func (c *spanContext) ForeachBaggageItem(handler func(k, v string) bool) { } func (c *spanContext) setSamplingPriority(p int) { - c.mu.Lock() - defer c.mu.Unlock() - c.priority = p - c.hasPriority = true + if c.trace == nil { + c.trace = newTrace() + } + c.trace.setSamplingPriority(float64(p)) } func (c *spanContext) samplingPriority() int { - c.mu.RLock() - defer c.mu.RUnlock() - return c.priority + if c.trace == nil { + return 0 + } + return c.trace.samplingPriority() } func (c *spanContext) hasSamplingPriority() bool { - c.mu.RLock() - defer c.mu.RUnlock() - return c.hasPriority + return c.trace != nil && c.trace.hasSamplingPriority() } func (c *spanContext) setBaggageItem(key, val string) { @@ -116,15 +113,23 @@ func (c *spanContext) baggageItem(key string) string { } // finish marks this span as finished in the trace. -func (c *spanContext) finish() { c.trace.ackFinish() } +func (c *spanContext) finish() { c.trace.finishedOne(c.span) } -// trace holds information about a specific trace. This structure is shared -// between all spans in a trace. +// trace contains shared context information about a trace, such as sampling +// priority, the root reference and a buffer of the spans which are part of the +// trace, if these exist. type trace struct { mu sync.RWMutex // guards below fields spans []*span // all the spans that are part of this trace finished int // the number of finished spans full bool // signifies that the span buffer is full + priority *float64 // sampling priority + locked bool // specifies if the sampling priority can be altered + + // root specifies the root of the trace, if known; it is nil when a span + // context is extracted from a carrier, at which point there are no spans in + // the trace yet. + root *span } var ( @@ -146,6 +151,42 @@ func newTrace() *trace { return &trace{spans: make([]*span, 0, traceStartSize)} } +func (t *trace) hasSamplingPriority() bool { + t.mu.RLock() + defer t.mu.RUnlock() + return t.priority != nil +} + +func (t *trace) samplingPriority() int { + t.mu.RLock() + defer t.mu.RUnlock() + if t.priority == nil { + return 0 + } + return int(*t.priority) +} + +func (t *trace) setSamplingPriority(p float64) { + t.mu.Lock() + defer t.mu.Unlock() + t.setSamplingPriorityLocked(p) +} + +func (t *trace) setSamplingPriorityLocked(p float64) { + if t.locked { + return + } + if t.root == nil { + // this trace is distributed (no local root); modifications + // to the sampling priority are not allowed. + t.locked = true + } + if t.priority == nil { + t.priority = new(float64) + } + *t.priority = p +} + // push pushes a new span into the trace. If the buffer is full, it returns // a errBufferFull error. func (t *trace) push(sp *span) { @@ -164,12 +205,16 @@ func (t *trace) push(sp *span) { } return } + if v, ok := sp.Metrics[keySamplingPriority]; ok { + t.setSamplingPriorityLocked(v) + } t.spans = append(t.spans, sp) } -// ackFinish aknowledges that another span in the trace has finished, and checks -// if the trace is complete, in which case it calls the onFinish function. -func (t *trace) ackFinish() { +// finishedOne aknowledges that another span in the trace has finished, and checks +// if the trace is complete, in which case it calls the onFinish function. It uses +// the given priority, if non-nil, to mark the root span. +func (t *trace) finishedOne(s *span) { t.mu.Lock() defer t.mu.Unlock() if t.full { @@ -180,6 +225,13 @@ func (t *trace) ackFinish() { return } t.finished++ + if s == t.root && t.priority != nil { + // after the root has finished we lock down the priority; + // we won't be able to make changes to a span after finishing + // without causing a race condition. + t.root.Metrics[keySamplingPriority] = *t.priority + t.locked = true + } if len(t.spans) != t.finished { return } diff --git a/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer/textmap.go b/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer/textmap.go index fe0ef9933..d8053ba00 100644 --- a/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer/textmap.go +++ b/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer/textmap.go @@ -2,10 +2,12 @@ package tracer import ( "net/http" + "os" "strconv" "strings" "gopkg.in/DataDog/dd-trace-go.v1/ddtrace" + "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/ext" ) // HTTPHeadersCarrier wraps an http.Header as a TextMapWriter and TextMapReader, allowing @@ -54,6 +56,11 @@ func (c TextMapCarrier) ForeachKey(handler func(key, val string) error) error { return nil } +const ( + headerPropagationStyleInject = "DD_PROPAGATION_STYLE_INJECT" + headerPropagationStyleExtract = "DD_PROPAGATION_STYLE_EXTRACT" +) + const ( // DefaultBaggageHeaderPrefix specifies the prefix that will be used in // HTTP headers or text maps to prefix baggage keys. @@ -72,6 +79,10 @@ const ( DefaultPriorityHeader = "x-datadog-sampling-priority" ) +// originHeader specifies the name of the header indicating the origin of the trace. +// It is used with the Synthetics product and usually has the value "synthetics". +const originHeader = "x-datadog-origin" + // PropagatorConfig defines the configuration for initializing a propagator. type PropagatorConfig struct { // BaggagePrefix specifies the prefix that will be used to store baggage @@ -110,21 +121,86 @@ func NewPropagator(cfg *PropagatorConfig) Propagator { if cfg.PriorityHeader == "" { cfg.PriorityHeader = DefaultPriorityHeader } - return &propagator{cfg} + return &chainedPropagator{ + injectors: getPropagators(cfg, headerPropagationStyleInject), + extractors: getPropagators(cfg, headerPropagationStyleExtract), + } } -// propagator implements a propagator which uses TextMap internally. -// It propagates the trace and span IDs, as well as the baggage from the -// context. -type propagator struct{ cfg *PropagatorConfig } +// chainedPropagator implements Propagator and applies a list of injectors and extractors. +// When injecting, all injectors are called to propagate the span context. +// When extracting, it tries each extractor, selecting the first successful one. +type chainedPropagator struct { + injectors []Propagator + extractors []Propagator +} + +// getPropagators returns a list of propagators based on the list found in the +// given environment variable. If the list doesn't contain a value or has invalid +// values, the default propagator will be returned. +func getPropagators(cfg *PropagatorConfig, env string) []Propagator { + dd := &propagator{cfg} + ps := os.Getenv(env) + if ps == "" { + return []Propagator{dd} + } + var list []Propagator + for _, v := range strings.Split(ps, ",") { + switch strings.ToLower(v) { + case "datadog": + list = append(list, dd) + case "b3": + list = append(list, &propagatorB3{}) + default: + // TODO(cgilmour): consider logging something for invalid/unknown styles. + } + } + if len(list) == 0 { + // return the default + return []Propagator{dd} + } + return list +} // Inject defines the Propagator to propagate SpanContext data // out of the current process. The implementation propagates the // TraceID and the current active SpanID, as well as the Span baggage. +func (p *chainedPropagator) Inject(spanCtx ddtrace.SpanContext, carrier interface{}) error { + for _, v := range p.injectors { + err := v.Inject(spanCtx, carrier) + if err != nil { + return err + } + } + return nil +} + +// Extract implements Propagator. +func (p *chainedPropagator) Extract(carrier interface{}) (ddtrace.SpanContext, error) { + for _, v := range p.extractors { + ctx, err := v.Extract(carrier) + if ctx != nil { + // first extractor returns + return ctx, nil + } + if err == ErrSpanContextNotFound { + continue + } + return nil, err + } + return nil, ErrSpanContextNotFound +} + +// propagator implements Propagator and injects/extracts span contexts +// using datadog headers. Only TextMap carriers are supported. +type propagator struct { + cfg *PropagatorConfig +} + func (p *propagator) Inject(spanCtx ddtrace.SpanContext, carrier interface{}) error { - switch v := carrier.(type) { + switch c := carrier.(type) { case TextMapWriter: - return p.injectTextMap(spanCtx, v) + return p.injectTextMap(spanCtx, c) default: return ErrInvalidCarrier } @@ -141,6 +217,9 @@ func (p *propagator) injectTextMap(spanCtx ddtrace.SpanContext, writer TextMapWr if ctx.hasSamplingPriority() { writer.Set(p.cfg.PriorityHeader, strconv.Itoa(ctx.samplingPriority())) } + if ctx.origin != "" { + writer.Set(originHeader, ctx.origin) + } // propagate OpenTracing baggage for k, v := range ctx.baggage { writer.Set(p.cfg.BaggagePrefix+k, v) @@ -148,11 +227,10 @@ func (p *propagator) injectTextMap(spanCtx ddtrace.SpanContext, writer TextMapWr return nil } -// Extract implements Propagator. func (p *propagator) Extract(carrier interface{}) (ddtrace.SpanContext, error) { - switch v := carrier.(type) { + switch c := carrier.(type) { case TextMapReader: - return p.extractTextMap(v) + return p.extractTextMap(c) default: return nil, ErrInvalidCarrier } @@ -180,6 +258,8 @@ func (p *propagator) extractTextMap(reader TextMapReader) (ddtrace.SpanContext, return ErrSpanContextCorrupted } ctx.setSamplingPriority(priority) + case originHeader: + ctx.origin = v default: if strings.HasPrefix(key, p.cfg.BaggagePrefix) { ctx.setBaggageItem(strings.TrimPrefix(key, p.cfg.BaggagePrefix), v) @@ -195,3 +275,83 @@ func (p *propagator) extractTextMap(reader TextMapReader) (ddtrace.SpanContext, } return &ctx, nil } + +const ( + b3TraceIDHeader = "x-b3-traceid" + b3SpanIDHeader = "x-b3-spanid" + b3SampledHeader = "x-b3-sampled" +) + +// propagatorB3 implements Propagator and injects/extracts span contexts +// using B3 headers. Only TextMap carriers are supported. +type propagatorB3 struct{} + +func (p *propagatorB3) Inject(spanCtx ddtrace.SpanContext, carrier interface{}) error { + switch c := carrier.(type) { + case TextMapWriter: + return p.injectTextMap(spanCtx, c) + default: + return ErrInvalidCarrier + } +} + +func (*propagatorB3) injectTextMap(spanCtx ddtrace.SpanContext, writer TextMapWriter) error { + ctx, ok := spanCtx.(*spanContext) + if !ok || ctx.traceID == 0 || ctx.spanID == 0 { + return ErrInvalidSpanContext + } + writer.Set(b3TraceIDHeader, strconv.FormatUint(ctx.traceID, 16)) + writer.Set(b3SpanIDHeader, strconv.FormatUint(ctx.spanID, 16)) + if ctx.hasSamplingPriority() { + if ctx.samplingPriority() >= ext.PriorityAutoKeep { + writer.Set(b3SampledHeader, "1") + } else { + writer.Set(b3SampledHeader, "0") + } + } + return nil +} + +func (p *propagatorB3) Extract(carrier interface{}) (ddtrace.SpanContext, error) { + switch c := carrier.(type) { + case TextMapReader: + return p.extractTextMap(c) + default: + return nil, ErrInvalidCarrier + } +} + +func (*propagatorB3) extractTextMap(reader TextMapReader) (ddtrace.SpanContext, error) { + var ctx spanContext + err := reader.ForeachKey(func(k, v string) error { + var err error + key := strings.ToLower(k) + switch key { + case b3TraceIDHeader: + ctx.traceID, err = strconv.ParseUint(v, 16, 64) + if err != nil { + return ErrSpanContextCorrupted + } + case b3SpanIDHeader: + ctx.spanID, err = strconv.ParseUint(v, 16, 64) + if err != nil { + return ErrSpanContextCorrupted + } + case b3SampledHeader: + priority, err := strconv.Atoi(v) + if err != nil { + return ErrSpanContextCorrupted + } + ctx.setSamplingPriority(priority) + default: + } + return nil + }) + if err != nil { + return nil, err + } + if ctx.traceID == 0 || ctx.spanID == 0 { + return nil, ErrSpanContextNotFound + } + return &ctx, nil +} diff --git a/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer/tracer.go b/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer/tracer.go index 42620786b..91812ac6c 100644 --- a/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer/tracer.go +++ b/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer/tracer.go @@ -44,6 +44,8 @@ type tracer struct { // prioritySampling holds an instance of the priority sampler. prioritySampling *prioritySampler + // pid of the process + pid string } const ( @@ -131,6 +133,7 @@ func newTracer(opts ...StartOption) *tracer { errorBuffer: make(chan error, errorBufferSize), stopped: make(chan struct{}), prioritySampling: newPrioritySampler(), + pid: strconv.Itoa(os.Getpid()), } go t.worker() @@ -230,7 +233,10 @@ func (t *tracer) StartSpan(operationName string, options ...ddtrace.StartSpanOpt context = ctx } } - id := random.Uint64() + id := opts.SpanID + if id == 0 { + id = random.Uint64() + } // span defaults span := &span{ Name: operationName, @@ -248,19 +254,28 @@ func (t *tracer) StartSpan(operationName string, options ...ddtrace.StartSpanOpt span.TraceID = context.traceID span.ParentID = context.spanID if context.hasSamplingPriority() { - span.Metrics[samplingPriorityKey] = float64(context.samplingPriority()) + span.Metrics[keySamplingPriority] = float64(context.samplingPriority()) } if context.span != nil { - // it has a local parent, inherit the service + // local parent, inherit service context.span.RLock() span.Service = context.span.Service context.span.RUnlock() + } else { + // remote parent + if context.origin != "" { + // mark origin + span.Meta[keyOrigin] = context.origin + } } } span.context = newSpanContext(span, context) if context == nil || context.span == nil { // this is either a root span or it has a remote parent, we should add the PID. - span.SetTag(ext.Pid, strconv.Itoa(os.Getpid())) + span.SetTag(ext.Pid, t.pid) + if t.hostname != "" { + span.SetTag(keyHostname, t.hostname) + } } // add tags from options for k, v := range opts.Tags { @@ -361,7 +376,7 @@ const sampleRateMetricKey = "_sample_rate" // Sample samples a span with the internal sampler. func (t *tracer) sample(span *span) { - if span.context.hasPriority { + if span.context.hasSamplingPriority() { // sampling decision was already made return } diff --git a/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer/transport.go b/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer/transport.go index 9a8e70cfd..8821d3de8 100644 --- a/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer/transport.go +++ b/vendor/gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer/transport.go @@ -15,7 +15,7 @@ import ( var ( // TODO(gbbr): find a more effective way to keep this up to date, // e.g. via `go generate` - tracerVersion = "v1.7.0" + tracerVersion = "v1.13.1" // We copy the transport to avoid using the default one, as it might be // augmented with tracing and we don't want these calls to be recorded. diff --git a/vendor/gopkg.in/DataDog/dd-trace-go.v1/internal/globalconfig/globalconfig.go b/vendor/gopkg.in/DataDog/dd-trace-go.v1/internal/globalconfig/globalconfig.go new file mode 100644 index 000000000..115f472ac --- /dev/null +++ b/vendor/gopkg.in/DataDog/dd-trace-go.v1/internal/globalconfig/globalconfig.go @@ -0,0 +1,28 @@ +// Package globalconfig stores configuration which applies globally to both the tracer +// and integrations. +package globalconfig + +import "sync" + +var cfg = &config{} + +type config struct { + mu sync.RWMutex + analyticsRate float64 +} + +// AnalyticsRate returns the sampling rate at which events should be marked. It uses +// synchronizing mechanisms, meaning that for optimal performance it's best to read it +// once and store it. +func AnalyticsRate() float64 { + cfg.mu.RLock() + defer cfg.mu.RUnlock() + return cfg.analyticsRate +} + +// SetAnalyticsRate sets the given event sampling rate globally. +func SetAnalyticsRate(rate float64) { + cfg.mu.Lock() + cfg.analyticsRate = rate + cfg.mu.Unlock() +}