| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896 |
- // Unless explicitly stated otherwise all files in this repository are licensed
- // under the Apache License Version 2.0.
- // This product includes software developed at Datadog (https://www.datadoghq.com/).
- // Copyright 2016 Datadog, Inc.
- package tracer
- import (
- "fmt"
- "net/http"
- "os"
- "regexp"
- "strconv"
- "strings"
- "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/log"
- "gopkg.in/DataDog/dd-trace-go.v1/internal/samplernames"
- )
- // HTTPHeadersCarrier wraps an http.Header as a TextMapWriter and TextMapReader, allowing
- // it to be used using the provided Propagator implementation.
- type HTTPHeadersCarrier http.Header
- var _ TextMapWriter = (*HTTPHeadersCarrier)(nil)
- var _ TextMapReader = (*HTTPHeadersCarrier)(nil)
- // Set implements TextMapWriter.
- func (c HTTPHeadersCarrier) Set(key, val string) {
- http.Header(c).Set(key, val)
- }
- // ForeachKey implements TextMapReader.
- func (c HTTPHeadersCarrier) ForeachKey(handler func(key, val string) error) error {
- for k, vals := range c {
- for _, v := range vals {
- if err := handler(k, v); err != nil {
- return err
- }
- }
- }
- return nil
- }
- // TextMapCarrier allows the use of a regular map[string]string as both TextMapWriter
- // and TextMapReader, making it compatible with the provided Propagator.
- type TextMapCarrier map[string]string
- var _ TextMapWriter = (*TextMapCarrier)(nil)
- var _ TextMapReader = (*TextMapCarrier)(nil)
- // Set implements TextMapWriter.
- func (c TextMapCarrier) Set(key, val string) {
- c[key] = val
- }
- // ForeachKey conforms to the TextMapReader interface.
- func (c TextMapCarrier) ForeachKey(handler func(key, val string) error) error {
- for k, v := range c {
- if err := handler(k, v); err != nil {
- return err
- }
- }
- return nil
- }
- const (
- headerPropagationStyleInject = "DD_TRACE_PROPAGATION_STYLE_INJECT"
- headerPropagationStyleExtract = "DD_TRACE_PROPAGATION_STYLE_EXTRACT"
- headerPropagationStyle = "DD_TRACE_PROPAGATION_STYLE"
- headerPropagationStyleInjectDeprecated = "DD_PROPAGATION_STYLE_INJECT" // deprecated
- headerPropagationStyleExtractDeprecated = "DD_PROPAGATION_STYLE_EXTRACT" // deprecated
- )
- const (
- // DefaultBaggageHeaderPrefix specifies the prefix that will be used in
- // HTTP headers or text maps to prefix baggage keys.
- DefaultBaggageHeaderPrefix = "ot-baggage-"
- // DefaultTraceIDHeader specifies the key that will be used in HTTP headers
- // or text maps to store the trace ID.
- DefaultTraceIDHeader = "x-datadog-trace-id"
- // DefaultParentIDHeader specifies the key that will be used in HTTP headers
- // or text maps to store the parent ID.
- DefaultParentIDHeader = "x-datadog-parent-id"
- // DefaultPriorityHeader specifies the key that will be used in HTTP headers
- // or text maps to store the sampling priority value.
- 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"
- // traceTagsHeader holds the propagated trace tags
- const traceTagsHeader = "x-datadog-tags"
- // propagationExtractMaxSize limits the total size of incoming propagated tags to parse
- const propagationExtractMaxSize = 512
- // PropagatorConfig defines the configuration for initializing a propagator.
- type PropagatorConfig struct {
- // BaggagePrefix specifies the prefix that will be used to store baggage
- // items in a map. It defaults to DefaultBaggageHeaderPrefix.
- BaggagePrefix string
- // TraceHeader specifies the map key that will be used to store the trace ID.
- // It defaults to DefaultTraceIDHeader.
- TraceHeader string
- // ParentHeader specifies the map key that will be used to store the parent ID.
- // It defaults to DefaultParentIDHeader.
- ParentHeader string
- // PriorityHeader specifies the map key that will be used to store the sampling priority.
- // It defaults to DefaultPriorityHeader.
- PriorityHeader string
- // MaxTagsHeaderLen specifies the maximum length of trace tags header value.
- // It defaults to defaultMaxTagsHeaderLen, a value of 0 disables propagation of tags.
- MaxTagsHeaderLen int
- // B3 specifies if B3 headers should be added for trace propagation.
- // See https://github.com/openzipkin/b3-propagation
- B3 bool
- }
- // NewPropagator returns a new propagator which uses TextMap to inject
- // and extract values. It propagates trace and span IDs and baggage.
- // To use the defaults, nil may be provided in place of the config.
- //
- // The inject and extract propagators are determined using environment variables
- // with the following order of precedence:
- // 1. DD_TRACE_PROPAGATION_STYLE_INJECT
- // 2. DD_PROPAGATION_STYLE_INJECT (deprecated)
- // 3. DD_TRACE_PROPAGATION_STYLE (applies to both inject and extract)
- // 4. If none of the above, use default values
- func NewPropagator(cfg *PropagatorConfig, propagators ...Propagator) Propagator {
- if cfg == nil {
- cfg = new(PropagatorConfig)
- }
- if cfg.BaggagePrefix == "" {
- cfg.BaggagePrefix = DefaultBaggageHeaderPrefix
- }
- if cfg.TraceHeader == "" {
- cfg.TraceHeader = DefaultTraceIDHeader
- }
- if cfg.ParentHeader == "" {
- cfg.ParentHeader = DefaultParentIDHeader
- }
- if cfg.PriorityHeader == "" {
- cfg.PriorityHeader = DefaultPriorityHeader
- }
- if len(propagators) > 0 {
- return &chainedPropagator{
- injectors: propagators,
- extractors: propagators,
- }
- }
- injectorsPs := os.Getenv(headerPropagationStyleInject)
- if injectorsPs == "" {
- if injectorsPs = os.Getenv(headerPropagationStyleInjectDeprecated); injectorsPs != "" {
- log.Warn("%v is deprecated. Please use %v or %v instead.\n", headerPropagationStyleInjectDeprecated, headerPropagationStyleInject, headerPropagationStyle)
- }
- }
- extractorsPs := os.Getenv(headerPropagationStyleExtract)
- if extractorsPs == "" {
- if extractorsPs = os.Getenv(headerPropagationStyleExtractDeprecated); extractorsPs != "" {
- log.Warn("%v is deprecated. Please use %v or %v instead.\n", headerPropagationStyleExtractDeprecated, headerPropagationStyleExtract, headerPropagationStyle)
- }
- }
- return &chainedPropagator{
- injectors: getPropagators(cfg, injectorsPs),
- extractors: getPropagators(cfg, extractorsPs),
- }
- }
- // 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 ps, which is a comma seperated
- // list of propagators. If the list doesn't contain any valid values, the
- // default propagator will be returned. Any invalid values in the list will log
- // a warning and be ignored.
- func getPropagators(cfg *PropagatorConfig, ps string) []Propagator {
- dd := &propagator{cfg}
- defaultPs := []Propagator{&propagatorW3c{}, dd}
- if cfg.B3 {
- defaultPs = append(defaultPs, &propagatorB3{})
- }
- if ps == "" {
- if prop := os.Getenv(headerPropagationStyle); prop != "" {
- ps = prop // use the generic DD_TRACE_PROPAGATION_STYLE if set
- } else {
- return defaultPs // no env set, so use default from configuration
- }
- }
- ps = strings.ToLower(ps)
- if ps == "none" {
- return nil
- }
- var list []Propagator
- if cfg.B3 {
- list = append(list, &propagatorB3{})
- }
- for _, v := range strings.Split(ps, ",") {
- switch strings.ToLower(v) {
- case "datadog":
- list = append(list, dd)
- case "tracecontext":
- list = append([]Propagator{&propagatorW3c{}}, list...)
- case "b3", "b3multi":
- if !cfg.B3 {
- // propagatorB3 hasn't already been added, add a new one.
- list = append(list, &propagatorB3{})
- }
- case "b3 single header":
- list = append(list, &propagatorB3SingleHeader{})
- case "none":
- log.Warn("Propagator \"none\" has no effect when combined with other propagators. " +
- "To disable the propagator, set to `none`")
- default:
- log.Warn("unrecognized propagator: %s\n", v)
- }
- }
- if len(list) == 0 {
- return defaultPs // no valid propagators, so return default
- }
- 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
- log.Debug("Extracted span context: %#v", ctx)
- 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 c := carrier.(type) {
- case TextMapWriter:
- return p.injectTextMap(spanCtx, c)
- default:
- return ErrInvalidCarrier
- }
- }
- func (p *propagator) injectTextMap(spanCtx ddtrace.SpanContext, writer TextMapWriter) error {
- ctx, ok := spanCtx.(*spanContext)
- if !ok || ctx.traceID == 0 || ctx.spanID == 0 {
- return ErrInvalidSpanContext
- }
- // propagate the TraceID and the current active SpanID
- writer.Set(p.cfg.TraceHeader, strconv.FormatUint(ctx.traceID, 10))
- writer.Set(p.cfg.ParentHeader, strconv.FormatUint(ctx.spanID, 10))
- if sp, ok := ctx.samplingPriority(); ok {
- writer.Set(p.cfg.PriorityHeader, strconv.Itoa(sp))
- }
- if ctx.origin != "" {
- writer.Set(originHeader, ctx.origin)
- }
- // propagate OpenTracing baggage
- for k, v := range ctx.baggage {
- writer.Set(p.cfg.BaggagePrefix+k, v)
- }
- if p.cfg.MaxTagsHeaderLen <= 0 {
- return nil
- }
- if s := p.marshalPropagatingTags(ctx); len(s) > 0 {
- writer.Set(traceTagsHeader, s)
- }
- return nil
- }
- // marshalPropagatingTags marshals all propagating tags included in ctx to a comma separated string
- func (p *propagator) marshalPropagatingTags(ctx *spanContext) string {
- var sb strings.Builder
- if ctx.trace == nil {
- return ""
- }
- ctx.trace.mu.Lock()
- defer ctx.trace.mu.Unlock()
- for k, v := range ctx.trace.propagatingTags {
- if err := isValidPropagatableTag(k, v); err != nil {
- log.Warn("Won't propagate tag '%s': %v", k, err.Error())
- ctx.trace.setTag(keyPropagationError, "encoding_error")
- continue
- }
- if sb.Len()+len(k)+len(v) > p.cfg.MaxTagsHeaderLen {
- sb.Reset()
- log.Warn("Won't propagate tag: maximum trace tags header len (%d) reached.", p.cfg.MaxTagsHeaderLen)
- ctx.trace.setTag(keyPropagationError, "inject_max_size")
- break
- }
- if sb.Len() > 0 {
- sb.WriteByte(',')
- }
- sb.WriteString(k)
- sb.WriteByte('=')
- sb.WriteString(v)
- }
- return sb.String()
- }
- func (p *propagator) Extract(carrier interface{}) (ddtrace.SpanContext, error) {
- switch c := carrier.(type) {
- case TextMapReader:
- return p.extractTextMap(c)
- default:
- return nil, ErrInvalidCarrier
- }
- }
- func (p *propagator) 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 p.cfg.TraceHeader:
- ctx.traceID, err = parseUint64(v)
- if err != nil {
- return ErrSpanContextCorrupted
- }
- case p.cfg.ParentHeader:
- ctx.spanID, err = parseUint64(v)
- if err != nil {
- return ErrSpanContextCorrupted
- }
- case p.cfg.PriorityHeader:
- priority, err := strconv.Atoi(v)
- if err != nil {
- return ErrSpanContextCorrupted
- }
- ctx.setSamplingPriority(priority, samplernames.Unknown)
- case originHeader:
- ctx.origin = v
- case traceTagsHeader:
- unmarshalPropagatingTags(&ctx, v)
- default:
- if strings.HasPrefix(key, p.cfg.BaggagePrefix) {
- ctx.setBaggageItem(strings.TrimPrefix(key, p.cfg.BaggagePrefix), v)
- }
- }
- return nil
- })
- if err != nil {
- return nil, err
- }
- if ctx.traceID == 0 || (ctx.spanID == 0 && ctx.origin != "synthetics") {
- return nil, ErrSpanContextNotFound
- }
- return &ctx, nil
- }
- // unmarshalPropagatingTags unmarshals tags from v into ctx
- func unmarshalPropagatingTags(ctx *spanContext, v string) {
- if ctx.trace == nil {
- ctx.trace = newTrace()
- }
- ctx.trace.mu.Lock()
- defer ctx.trace.mu.Unlock()
- if len(v) > propagationExtractMaxSize {
- log.Warn("Did not extract %s, size limit exceeded: %d. Incoming tags will not be propagated further.", traceTagsHeader, propagationExtractMaxSize)
- ctx.trace.setTag(keyPropagationError, "extract_max_size")
- return
- }
- var err error
- ctx.trace.propagatingTags, err = parsePropagatableTraceTags(v)
- if err != nil {
- log.Warn("Did not extract %s: %v. Incoming tags will not be propagated further.", traceTagsHeader, err.Error())
- ctx.trace.setTag(keyPropagationError, "decoding_error")
- }
- }
- // setPropagatingTag adds the key value pair to the map of propagating tags on the trace,
- // creating the map if one is not initialized.
- func setPropagatingTag(ctx *spanContext, k, v string) {
- if ctx.trace == nil {
- // extractors initialize a new spanContext, so the trace might be nil
- ctx.trace = newTrace()
- }
- ctx.trace.setPropagatingTag(k, v)
- }
- const (
- b3TraceIDHeader = "x-b3-traceid"
- b3SpanIDHeader = "x-b3-spanid"
- b3SampledHeader = "x-b3-sampled"
- b3SingleHeader = "b3"
- )
- // 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, fmt.Sprintf("%016x", ctx.traceID))
- writer.Set(b3SpanIDHeader, fmt.Sprintf("%016x", ctx.spanID))
- if p, ok := ctx.samplingPriority(); ok {
- if p >= 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:
- if len(v) > 16 {
- v = v[len(v)-16:]
- }
- 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, samplernames.Unknown)
- default:
- }
- return nil
- })
- if err != nil {
- return nil, err
- }
- if ctx.traceID == 0 || ctx.spanID == 0 {
- return nil, ErrSpanContextNotFound
- }
- return &ctx, nil
- }
- // propagatorB3 implements Propagator and injects/extracts span contexts
- // using B3 headers. Only TextMap carriers are supported.
- type propagatorB3SingleHeader struct{}
- func (p *propagatorB3SingleHeader) Inject(spanCtx ddtrace.SpanContext, carrier interface{}) error {
- switch c := carrier.(type) {
- case TextMapWriter:
- return p.injectTextMap(spanCtx, c)
- default:
- return ErrInvalidCarrier
- }
- }
- func (*propagatorB3SingleHeader) injectTextMap(spanCtx ddtrace.SpanContext, writer TextMapWriter) error {
- ctx, ok := spanCtx.(*spanContext)
- if !ok || ctx.traceID == 0 || ctx.spanID == 0 {
- return ErrInvalidSpanContext
- }
- sb := strings.Builder{}
- sb.WriteString(fmt.Sprintf("%016x-%016x", ctx.traceID, ctx.spanID))
- if p, ok := ctx.samplingPriority(); ok {
- if p >= ext.PriorityAutoKeep {
- sb.WriteString("-1")
- } else {
- sb.WriteString("-0")
- }
- }
- writer.Set(b3SingleHeader, sb.String())
- return nil
- }
- func (p *propagatorB3SingleHeader) Extract(carrier interface{}) (ddtrace.SpanContext, error) {
- switch c := carrier.(type) {
- case TextMapReader:
- return p.extractTextMap(c)
- default:
- return nil, ErrInvalidCarrier
- }
- }
- func (*propagatorB3SingleHeader) 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 b3SingleHeader:
- b3Parts := strings.Split(v, "-")
- if len(b3Parts) >= 2 {
- if len(b3Parts[0]) > 16 {
- b3Parts[0] = b3Parts[0][len(b3Parts[0])-16:]
- }
- ctx.traceID, err = strconv.ParseUint(b3Parts[0], 16, 64)
- if err != nil {
- return ErrSpanContextCorrupted
- }
- ctx.spanID, err = strconv.ParseUint(b3Parts[1], 16, 64)
- if err != nil {
- return ErrSpanContextCorrupted
- }
- if len(b3Parts) >= 3 {
- switch b3Parts[2] {
- case "":
- break
- case "1", "d": // Treat 'debug' traces as priority 1
- ctx.setSamplingPriority(1, samplernames.Unknown)
- case "0":
- ctx.setSamplingPriority(0, samplernames.Unknown)
- default:
- return ErrSpanContextCorrupted
- }
- }
- } else {
- return ErrSpanContextCorrupted
- }
- default:
- }
- return nil
- })
- if err != nil {
- return nil, err
- }
- if ctx.traceID == 0 || ctx.spanID == 0 {
- return nil, ErrSpanContextNotFound
- }
- return &ctx, nil
- }
- const (
- traceparentHeader = "traceparent"
- tracestateHeader = "tracestate"
- w3cTraceIDTag = "w3cTraceID"
- )
- // propagatorW3c implements Propagator and injects/extracts span contexts
- // using W3C tracecontext/traceparent headers. Only TextMap carriers are supported.
- type propagatorW3c struct{}
- func (p *propagatorW3c) Inject(spanCtx ddtrace.SpanContext, carrier interface{}) error {
- switch c := carrier.(type) {
- case TextMapWriter:
- return p.injectTextMap(spanCtx, c)
- default:
- return ErrInvalidCarrier
- }
- }
- // injectTextMap propagates span context attributes into the writer,
- // in the format of the traceparentHeader and tracestateHeader.
- // traceparentHeader encodes W3C Trace Propagation version, 128-bit traceID,
- // spanID, and a flags field, which supports 8 unique flags.
- // The current specification only supports a single flag called sampled,
- // which is equal to 00000001 when no other flag is present.
- // tracestateHeader is a comma-separated list of list-members with a <key>=<value> format,
- // where each list-member is managed by a vendor or instrumentation library.
- func (*propagatorW3c) injectTextMap(spanCtx ddtrace.SpanContext, writer TextMapWriter) error {
- ctx, ok := spanCtx.(*spanContext)
- if !ok || ctx.traceID == 0 || ctx.spanID == 0 {
- return ErrInvalidSpanContext
- }
- flags := ""
- p, ok := ctx.samplingPriority()
- if ok && p >= ext.PriorityAutoKeep {
- flags = "01"
- } else {
- flags = "00"
- }
- var traceID string
- // if previous traceparent is valid, do NOT update the trace ID
- if ctx.trace != nil && ctx.trace.propagatingTags != nil {
- tag := ctx.trace.propagatingTags[w3cTraceIDTag]
- if len(tag) == 32 {
- id, err := strconv.ParseUint(tag[16:], 16, 64)
- if err == nil && id != 0 {
- traceID = tag
- }
- }
- }
- if len(traceID) == 0 {
- traceID = fmt.Sprintf("%032x", ctx.traceID)
- }
- writer.Set(traceparentHeader, fmt.Sprintf("00-%s-%016x-%v", traceID, ctx.spanID, flags))
- // if context priority / origin / tags were updated after extraction,
- // or the tracestateHeader doesn't start with `dd=`
- // we need to recreate tracestate
- if ctx.updated ||
- (ctx.trace != nil && ctx.trace.propagatingTags != nil && !strings.HasPrefix(ctx.trace.propagatingTags[tracestateHeader], "dd=")) ||
- len(ctx.trace.propagatingTags[tracestateHeader]) == 0 {
- writer.Set(tracestateHeader, composeTracestate(ctx, p, ctx.trace.propagatingTags[tracestateHeader]))
- } else {
- writer.Set(tracestateHeader, ctx.trace.propagatingTags[tracestateHeader])
- }
- return nil
- }
- var (
- // keyRgx is used to sanitize the keys of the datadog propagating tags.
- // Disallowed characters are comma (reserved as a list-member separator),
- // equals (reserved for list-member key-value separator),
- // space and characters outside the ASCII range 0x20 to 0x7E.
- // Disallowed characters must be replaced with the underscore.
- keyRgx = regexp.MustCompile(",|=|[^\\x20-\\x7E]+")
- // valueRgx is used to sanitize the values of the datadog propagating tags.
- // Disallowed characters are comma (reserved as a list-member separator),
- // semi-colon (reserved for separator between entries in the dd list-member),
- // tilde (reserved, will represent 0x3D (equals) in the encoded tag value,
- // and characters outside the ASCII range 0x20 to 0x7E.
- // Equals character must be encoded with a tilde.
- // Other disallowed characters must be replaced with the underscore.
- valueRgx = regexp.MustCompile(",|;|~|[^\\x20-\\x7E]+")
- // originRgx is used to sanitize the value of the datadog origin tag.
- // Disallowed characters are comma (reserved as a list-member separator),
- // semi-colon (reserved for separator between entries in the dd list-member),
- // equals (reserved for list-member key-value separator),
- // and characters outside the ASCII range 0x21 to 0x7E.
- // Disallowed characters must be replaced with the underscore.
- originRgx = regexp.MustCompile(",|=|;|[^\\x21-\\x7E]+")
- )
- // composeTracestate creates a tracestateHeader from the spancontext.
- // The Datadog tracing library is only responsible for managing the list member with key dd,
- // which holds the values of the sampling decision(`s:<value>`), origin(`o:<origin>`),
- // and propagated tags prefixed with `t.`(e.g. _dd.p.usr.id:usr_id tag will become `t.usr.id:usr_id`).
- func composeTracestate(ctx *spanContext, priority int, oldState string) string {
- var b strings.Builder
- b.WriteString(fmt.Sprintf("dd=s:%d", priority))
- listLength := 1
- if ctx.origin != "" {
- b.WriteString(fmt.Sprintf(";o:%s",
- originRgx.ReplaceAllString(ctx.origin, "_")))
- }
- for k, v := range ctx.trace.propagatingTags {
- if !strings.HasPrefix(k, "_dd.p.") {
- continue
- }
- // Datadog propagating tags must be appended to the tracestateHeader
- // with the `t.` prefix. Tag value must have all `=` signs replaced with a tilde (`~`).
- tag := fmt.Sprintf("t.%s:%s",
- keyRgx.ReplaceAllString(k[len("_dd.p."):], "_"),
- strings.ReplaceAll(valueRgx.ReplaceAllString(v, "_"), "=", "~"))
- if b.Len()+len(tag) > 256 {
- break
- }
- b.WriteString(";")
- b.WriteString(tag)
- }
- // the old state is split by vendors, must be concatenated with a `,`
- if len(oldState) == 0 {
- return b.String()
- }
- for _, s := range strings.Split(strings.Trim(oldState, " \t"), ",") {
- if strings.HasPrefix(s, "dd=") {
- continue
- }
- listLength++
- // if the resulting tracestateHeader exceeds 32 list-members,
- // remove the rightmost list-member(s)
- if listLength > 32 {
- break
- }
- b.WriteString("," + strings.Trim(s, " \t"))
- }
- return b.String()
- }
- func (p *propagatorW3c) Extract(carrier interface{}) (ddtrace.SpanContext, error) {
- switch c := carrier.(type) {
- case TextMapReader:
- return p.extractTextMap(c)
- default:
- return nil, ErrInvalidCarrier
- }
- }
- func (*propagatorW3c) extractTextMap(reader TextMapReader) (ddtrace.SpanContext, error) {
- var parentHeader string
- var stateHeader string
- // to avoid parsing tracestate header(s) if traceparent is invalid
- if err := reader.ForeachKey(func(k, v string) error {
- key := strings.ToLower(k)
- switch key {
- case traceparentHeader:
- if parentHeader != "" {
- return ErrSpanContextCorrupted
- }
- parentHeader = v
- case tracestateHeader:
- stateHeader = v
- }
- return nil
- }); err != nil {
- return nil, err
- }
- var ctx spanContext
- if err := parseTraceparent(&ctx, parentHeader); err != nil {
- return nil, err
- }
- if err := parseTracestate(&ctx, stateHeader); err != nil {
- return nil, err
- }
- return &ctx, nil
- }
- // parseTraceparent attempts to parse traceparentHeader which describes the position
- // of the incoming request in its trace graph in a portable, fixed-length format.
- // The format of the traceparentHeader is `-` separated string with in the
- // following format: `version-traceId-spanID-flags`,
- // where:
- // - version - represents the version of the W3C Tracecontext Propagation format in hex format.
- // - traceId - represents the propagated traceID in the format of 32 hex-encoded digits.
- // - spanID - represents the propagated spanID (parentID) in the format of 16 hex-encoded digits.
- // - flags - represents the propagated flags in the format of 2 hex-encoded digits, and supports 8 unique flags.
- // Example value of HTTP `traceparent` header: `00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01`,
- // Currently, Go tracer doesn't support 128-bit traceIDs, so the full traceID (32 hex-encoded digits) must be
- // stored into a field that is accessible from the span’s context. TraceId will be parsed from the least significant 16
- // hex-encoded digits into a 64-bit number.
- func parseTraceparent(ctx *spanContext, header string) error {
- nonWordCutset := "_-\t \n"
- header = strings.ToLower(strings.Trim(header, "\t -"))
- if len(header) == 0 {
- return ErrSpanContextNotFound
- }
- if len(header) != 55 {
- return ErrSpanContextCorrupted
- }
- parts := strings.Split(header, "-")
- if len(parts) != 4 {
- return ErrSpanContextCorrupted
- }
- version := strings.Trim(parts[0], nonWordCutset)
- if len(version) != 2 {
- return ErrSpanContextCorrupted
- }
- if v, err := strconv.ParseUint(version, 16, 64); err != nil || v == 255 {
- return ErrSpanContextCorrupted
- }
- // parsing traceID
- fullTraceID := strings.Trim(parts[1], nonWordCutset)
- if len(fullTraceID) != 32 {
- return ErrSpanContextCorrupted
- }
- // checking that the entire TraceID is a valid hex string
- if ok, err := regexp.MatchString("^[a-f0-9]+$", fullTraceID); !ok || err != nil {
- return ErrSpanContextCorrupted
- }
- var err error
- if ctx.traceID, err = strconv.ParseUint(fullTraceID[16:], 16, 64); err != nil {
- return ErrSpanContextCorrupted
- }
- if ctx.traceID == 0 {
- if strings.Trim(fullTraceID[:16], "0") == "" {
- return ErrSpanContextNotFound
- }
- }
- // setting trace-id to be used for span context propagation
- setPropagatingTag(ctx, w3cTraceIDTag, fullTraceID)
- // parsing spanID
- spanID := strings.Trim(parts[2], nonWordCutset)
- if len(spanID) != 16 {
- return ErrSpanContextCorrupted
- }
- if ok, err := regexp.MatchString("[a-f0-9]+", spanID); !ok || err != nil {
- return ErrSpanContextCorrupted
- }
- if ctx.spanID, err = strconv.ParseUint(spanID, 16, 64); err != nil {
- return ErrSpanContextCorrupted
- }
- if ctx.spanID == 0 {
- return ErrSpanContextNotFound
- }
- // parsing flags
- flags := parts[3]
- f, err := strconv.ParseInt(flags, 16, 8)
- if err != nil {
- return ErrSpanContextCorrupted
- }
- ctx.setSamplingPriority(int(f)&0x1, samplernames.Unknown)
- return nil
- }
- // parseTracestate attempts to parse tracestateHeader which is a list
- // with up to 32 comma-separated (,) list-members.
- // An example value would be: `vendorname1=opaqueValue1,vendorname2=opaqueValue2,dd=s:1;o:synthetics`,
- // Where `dd` list contains values that would be in x-datadog-tags as well as those needed for propagation information.
- // The keys to the “dd“ values have been shortened as follows to save space:
- // `sampling_priority` = `s`
- // `origin` = `o`
- // `_dd.p.` prefix = `t.`
- func parseTracestate(ctx *spanContext, header string) error {
- // if multiple headers are present, they must be combined and stored
- setPropagatingTag(ctx, tracestateHeader, header)
- list := strings.Split(strings.Trim(header, "\t "), ",")
- for _, s := range list {
- if !strings.HasPrefix(s, "dd=") {
- continue
- }
- dd := strings.Split(s[len("dd="):], ";")
- for _, val := range dd {
- x := strings.SplitN(val, ":", 2)
- if len(x) != 2 {
- continue
- }
- k, v := x[0], x[1]
- if k == "o" {
- ctx.origin = v
- } else if k == "s" {
- p, err := strconv.Atoi(v)
- if err != nil {
- // if the tracestate priority is absent, relying on traceparent value
- continue
- }
- flagPriority, _ := ctx.samplingPriority()
- if (flagPriority == 1 && p > 0) || (flagPriority == 0 && p <= 0) {
- ctx.setSamplingPriority(p, samplernames.Unknown)
- }
- } else if strings.HasPrefix(k, "t.") {
- k = k[len("t."):]
- v = strings.ReplaceAll(v, "~", "=")
- setPropagatingTag(ctx, "_dd.p."+k, v)
- }
- }
- }
- return nil
- }
|