textmap.go 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896
  1. // Unless explicitly stated otherwise all files in this repository are licensed
  2. // under the Apache License Version 2.0.
  3. // This product includes software developed at Datadog (https://www.datadoghq.com/).
  4. // Copyright 2016 Datadog, Inc.
  5. package tracer
  6. import (
  7. "fmt"
  8. "net/http"
  9. "os"
  10. "regexp"
  11. "strconv"
  12. "strings"
  13. "gopkg.in/DataDog/dd-trace-go.v1/ddtrace"
  14. "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/ext"
  15. "gopkg.in/DataDog/dd-trace-go.v1/internal/log"
  16. "gopkg.in/DataDog/dd-trace-go.v1/internal/samplernames"
  17. )
  18. // HTTPHeadersCarrier wraps an http.Header as a TextMapWriter and TextMapReader, allowing
  19. // it to be used using the provided Propagator implementation.
  20. type HTTPHeadersCarrier http.Header
  21. var _ TextMapWriter = (*HTTPHeadersCarrier)(nil)
  22. var _ TextMapReader = (*HTTPHeadersCarrier)(nil)
  23. // Set implements TextMapWriter.
  24. func (c HTTPHeadersCarrier) Set(key, val string) {
  25. http.Header(c).Set(key, val)
  26. }
  27. // ForeachKey implements TextMapReader.
  28. func (c HTTPHeadersCarrier) ForeachKey(handler func(key, val string) error) error {
  29. for k, vals := range c {
  30. for _, v := range vals {
  31. if err := handler(k, v); err != nil {
  32. return err
  33. }
  34. }
  35. }
  36. return nil
  37. }
  38. // TextMapCarrier allows the use of a regular map[string]string as both TextMapWriter
  39. // and TextMapReader, making it compatible with the provided Propagator.
  40. type TextMapCarrier map[string]string
  41. var _ TextMapWriter = (*TextMapCarrier)(nil)
  42. var _ TextMapReader = (*TextMapCarrier)(nil)
  43. // Set implements TextMapWriter.
  44. func (c TextMapCarrier) Set(key, val string) {
  45. c[key] = val
  46. }
  47. // ForeachKey conforms to the TextMapReader interface.
  48. func (c TextMapCarrier) ForeachKey(handler func(key, val string) error) error {
  49. for k, v := range c {
  50. if err := handler(k, v); err != nil {
  51. return err
  52. }
  53. }
  54. return nil
  55. }
  56. const (
  57. headerPropagationStyleInject = "DD_TRACE_PROPAGATION_STYLE_INJECT"
  58. headerPropagationStyleExtract = "DD_TRACE_PROPAGATION_STYLE_EXTRACT"
  59. headerPropagationStyle = "DD_TRACE_PROPAGATION_STYLE"
  60. headerPropagationStyleInjectDeprecated = "DD_PROPAGATION_STYLE_INJECT" // deprecated
  61. headerPropagationStyleExtractDeprecated = "DD_PROPAGATION_STYLE_EXTRACT" // deprecated
  62. )
  63. const (
  64. // DefaultBaggageHeaderPrefix specifies the prefix that will be used in
  65. // HTTP headers or text maps to prefix baggage keys.
  66. DefaultBaggageHeaderPrefix = "ot-baggage-"
  67. // DefaultTraceIDHeader specifies the key that will be used in HTTP headers
  68. // or text maps to store the trace ID.
  69. DefaultTraceIDHeader = "x-datadog-trace-id"
  70. // DefaultParentIDHeader specifies the key that will be used in HTTP headers
  71. // or text maps to store the parent ID.
  72. DefaultParentIDHeader = "x-datadog-parent-id"
  73. // DefaultPriorityHeader specifies the key that will be used in HTTP headers
  74. // or text maps to store the sampling priority value.
  75. DefaultPriorityHeader = "x-datadog-sampling-priority"
  76. )
  77. // originHeader specifies the name of the header indicating the origin of the trace.
  78. // It is used with the Synthetics product and usually has the value "synthetics".
  79. const originHeader = "x-datadog-origin"
  80. // traceTagsHeader holds the propagated trace tags
  81. const traceTagsHeader = "x-datadog-tags"
  82. // propagationExtractMaxSize limits the total size of incoming propagated tags to parse
  83. const propagationExtractMaxSize = 512
  84. // PropagatorConfig defines the configuration for initializing a propagator.
  85. type PropagatorConfig struct {
  86. // BaggagePrefix specifies the prefix that will be used to store baggage
  87. // items in a map. It defaults to DefaultBaggageHeaderPrefix.
  88. BaggagePrefix string
  89. // TraceHeader specifies the map key that will be used to store the trace ID.
  90. // It defaults to DefaultTraceIDHeader.
  91. TraceHeader string
  92. // ParentHeader specifies the map key that will be used to store the parent ID.
  93. // It defaults to DefaultParentIDHeader.
  94. ParentHeader string
  95. // PriorityHeader specifies the map key that will be used to store the sampling priority.
  96. // It defaults to DefaultPriorityHeader.
  97. PriorityHeader string
  98. // MaxTagsHeaderLen specifies the maximum length of trace tags header value.
  99. // It defaults to defaultMaxTagsHeaderLen, a value of 0 disables propagation of tags.
  100. MaxTagsHeaderLen int
  101. // B3 specifies if B3 headers should be added for trace propagation.
  102. // See https://github.com/openzipkin/b3-propagation
  103. B3 bool
  104. }
  105. // NewPropagator returns a new propagator which uses TextMap to inject
  106. // and extract values. It propagates trace and span IDs and baggage.
  107. // To use the defaults, nil may be provided in place of the config.
  108. //
  109. // The inject and extract propagators are determined using environment variables
  110. // with the following order of precedence:
  111. // 1. DD_TRACE_PROPAGATION_STYLE_INJECT
  112. // 2. DD_PROPAGATION_STYLE_INJECT (deprecated)
  113. // 3. DD_TRACE_PROPAGATION_STYLE (applies to both inject and extract)
  114. // 4. If none of the above, use default values
  115. func NewPropagator(cfg *PropagatorConfig, propagators ...Propagator) Propagator {
  116. if cfg == nil {
  117. cfg = new(PropagatorConfig)
  118. }
  119. if cfg.BaggagePrefix == "" {
  120. cfg.BaggagePrefix = DefaultBaggageHeaderPrefix
  121. }
  122. if cfg.TraceHeader == "" {
  123. cfg.TraceHeader = DefaultTraceIDHeader
  124. }
  125. if cfg.ParentHeader == "" {
  126. cfg.ParentHeader = DefaultParentIDHeader
  127. }
  128. if cfg.PriorityHeader == "" {
  129. cfg.PriorityHeader = DefaultPriorityHeader
  130. }
  131. if len(propagators) > 0 {
  132. return &chainedPropagator{
  133. injectors: propagators,
  134. extractors: propagators,
  135. }
  136. }
  137. injectorsPs := os.Getenv(headerPropagationStyleInject)
  138. if injectorsPs == "" {
  139. if injectorsPs = os.Getenv(headerPropagationStyleInjectDeprecated); injectorsPs != "" {
  140. log.Warn("%v is deprecated. Please use %v or %v instead.\n", headerPropagationStyleInjectDeprecated, headerPropagationStyleInject, headerPropagationStyle)
  141. }
  142. }
  143. extractorsPs := os.Getenv(headerPropagationStyleExtract)
  144. if extractorsPs == "" {
  145. if extractorsPs = os.Getenv(headerPropagationStyleExtractDeprecated); extractorsPs != "" {
  146. log.Warn("%v is deprecated. Please use %v or %v instead.\n", headerPropagationStyleExtractDeprecated, headerPropagationStyleExtract, headerPropagationStyle)
  147. }
  148. }
  149. return &chainedPropagator{
  150. injectors: getPropagators(cfg, injectorsPs),
  151. extractors: getPropagators(cfg, extractorsPs),
  152. }
  153. }
  154. // chainedPropagator implements Propagator and applies a list of injectors and extractors.
  155. // When injecting, all injectors are called to propagate the span context.
  156. // When extracting, it tries each extractor, selecting the first successful one.
  157. type chainedPropagator struct {
  158. injectors []Propagator
  159. extractors []Propagator
  160. }
  161. // getPropagators returns a list of propagators based on ps, which is a comma seperated
  162. // list of propagators. If the list doesn't contain any valid values, the
  163. // default propagator will be returned. Any invalid values in the list will log
  164. // a warning and be ignored.
  165. func getPropagators(cfg *PropagatorConfig, ps string) []Propagator {
  166. dd := &propagator{cfg}
  167. defaultPs := []Propagator{&propagatorW3c{}, dd}
  168. if cfg.B3 {
  169. defaultPs = append(defaultPs, &propagatorB3{})
  170. }
  171. if ps == "" {
  172. if prop := os.Getenv(headerPropagationStyle); prop != "" {
  173. ps = prop // use the generic DD_TRACE_PROPAGATION_STYLE if set
  174. } else {
  175. return defaultPs // no env set, so use default from configuration
  176. }
  177. }
  178. ps = strings.ToLower(ps)
  179. if ps == "none" {
  180. return nil
  181. }
  182. var list []Propagator
  183. if cfg.B3 {
  184. list = append(list, &propagatorB3{})
  185. }
  186. for _, v := range strings.Split(ps, ",") {
  187. switch strings.ToLower(v) {
  188. case "datadog":
  189. list = append(list, dd)
  190. case "tracecontext":
  191. list = append([]Propagator{&propagatorW3c{}}, list...)
  192. case "b3", "b3multi":
  193. if !cfg.B3 {
  194. // propagatorB3 hasn't already been added, add a new one.
  195. list = append(list, &propagatorB3{})
  196. }
  197. case "b3 single header":
  198. list = append(list, &propagatorB3SingleHeader{})
  199. case "none":
  200. log.Warn("Propagator \"none\" has no effect when combined with other propagators. " +
  201. "To disable the propagator, set to `none`")
  202. default:
  203. log.Warn("unrecognized propagator: %s\n", v)
  204. }
  205. }
  206. if len(list) == 0 {
  207. return defaultPs // no valid propagators, so return default
  208. }
  209. return list
  210. }
  211. // Inject defines the Propagator to propagate SpanContext data
  212. // out of the current process. The implementation propagates the
  213. // TraceID and the current active SpanID, as well as the Span baggage.
  214. func (p *chainedPropagator) Inject(spanCtx ddtrace.SpanContext, carrier interface{}) error {
  215. for _, v := range p.injectors {
  216. err := v.Inject(spanCtx, carrier)
  217. if err != nil {
  218. return err
  219. }
  220. }
  221. return nil
  222. }
  223. // Extract implements Propagator.
  224. func (p *chainedPropagator) Extract(carrier interface{}) (ddtrace.SpanContext, error) {
  225. for _, v := range p.extractors {
  226. ctx, err := v.Extract(carrier)
  227. if ctx != nil {
  228. // first extractor returns
  229. log.Debug("Extracted span context: %#v", ctx)
  230. return ctx, nil
  231. }
  232. if err == ErrSpanContextNotFound {
  233. continue
  234. }
  235. return nil, err
  236. }
  237. return nil, ErrSpanContextNotFound
  238. }
  239. // propagator implements Propagator and injects/extracts span contexts
  240. // using datadog headers. Only TextMap carriers are supported.
  241. type propagator struct {
  242. cfg *PropagatorConfig
  243. }
  244. func (p *propagator) Inject(spanCtx ddtrace.SpanContext, carrier interface{}) error {
  245. switch c := carrier.(type) {
  246. case TextMapWriter:
  247. return p.injectTextMap(spanCtx, c)
  248. default:
  249. return ErrInvalidCarrier
  250. }
  251. }
  252. func (p *propagator) injectTextMap(spanCtx ddtrace.SpanContext, writer TextMapWriter) error {
  253. ctx, ok := spanCtx.(*spanContext)
  254. if !ok || ctx.traceID == 0 || ctx.spanID == 0 {
  255. return ErrInvalidSpanContext
  256. }
  257. // propagate the TraceID and the current active SpanID
  258. writer.Set(p.cfg.TraceHeader, strconv.FormatUint(ctx.traceID, 10))
  259. writer.Set(p.cfg.ParentHeader, strconv.FormatUint(ctx.spanID, 10))
  260. if sp, ok := ctx.samplingPriority(); ok {
  261. writer.Set(p.cfg.PriorityHeader, strconv.Itoa(sp))
  262. }
  263. if ctx.origin != "" {
  264. writer.Set(originHeader, ctx.origin)
  265. }
  266. // propagate OpenTracing baggage
  267. for k, v := range ctx.baggage {
  268. writer.Set(p.cfg.BaggagePrefix+k, v)
  269. }
  270. if p.cfg.MaxTagsHeaderLen <= 0 {
  271. return nil
  272. }
  273. if s := p.marshalPropagatingTags(ctx); len(s) > 0 {
  274. writer.Set(traceTagsHeader, s)
  275. }
  276. return nil
  277. }
  278. // marshalPropagatingTags marshals all propagating tags included in ctx to a comma separated string
  279. func (p *propagator) marshalPropagatingTags(ctx *spanContext) string {
  280. var sb strings.Builder
  281. if ctx.trace == nil {
  282. return ""
  283. }
  284. ctx.trace.mu.Lock()
  285. defer ctx.trace.mu.Unlock()
  286. for k, v := range ctx.trace.propagatingTags {
  287. if err := isValidPropagatableTag(k, v); err != nil {
  288. log.Warn("Won't propagate tag '%s': %v", k, err.Error())
  289. ctx.trace.setTag(keyPropagationError, "encoding_error")
  290. continue
  291. }
  292. if sb.Len()+len(k)+len(v) > p.cfg.MaxTagsHeaderLen {
  293. sb.Reset()
  294. log.Warn("Won't propagate tag: maximum trace tags header len (%d) reached.", p.cfg.MaxTagsHeaderLen)
  295. ctx.trace.setTag(keyPropagationError, "inject_max_size")
  296. break
  297. }
  298. if sb.Len() > 0 {
  299. sb.WriteByte(',')
  300. }
  301. sb.WriteString(k)
  302. sb.WriteByte('=')
  303. sb.WriteString(v)
  304. }
  305. return sb.String()
  306. }
  307. func (p *propagator) Extract(carrier interface{}) (ddtrace.SpanContext, error) {
  308. switch c := carrier.(type) {
  309. case TextMapReader:
  310. return p.extractTextMap(c)
  311. default:
  312. return nil, ErrInvalidCarrier
  313. }
  314. }
  315. func (p *propagator) extractTextMap(reader TextMapReader) (ddtrace.SpanContext, error) {
  316. var ctx spanContext
  317. err := reader.ForeachKey(func(k, v string) error {
  318. var err error
  319. key := strings.ToLower(k)
  320. switch key {
  321. case p.cfg.TraceHeader:
  322. ctx.traceID, err = parseUint64(v)
  323. if err != nil {
  324. return ErrSpanContextCorrupted
  325. }
  326. case p.cfg.ParentHeader:
  327. ctx.spanID, err = parseUint64(v)
  328. if err != nil {
  329. return ErrSpanContextCorrupted
  330. }
  331. case p.cfg.PriorityHeader:
  332. priority, err := strconv.Atoi(v)
  333. if err != nil {
  334. return ErrSpanContextCorrupted
  335. }
  336. ctx.setSamplingPriority(priority, samplernames.Unknown)
  337. case originHeader:
  338. ctx.origin = v
  339. case traceTagsHeader:
  340. unmarshalPropagatingTags(&ctx, v)
  341. default:
  342. if strings.HasPrefix(key, p.cfg.BaggagePrefix) {
  343. ctx.setBaggageItem(strings.TrimPrefix(key, p.cfg.BaggagePrefix), v)
  344. }
  345. }
  346. return nil
  347. })
  348. if err != nil {
  349. return nil, err
  350. }
  351. if ctx.traceID == 0 || (ctx.spanID == 0 && ctx.origin != "synthetics") {
  352. return nil, ErrSpanContextNotFound
  353. }
  354. return &ctx, nil
  355. }
  356. // unmarshalPropagatingTags unmarshals tags from v into ctx
  357. func unmarshalPropagatingTags(ctx *spanContext, v string) {
  358. if ctx.trace == nil {
  359. ctx.trace = newTrace()
  360. }
  361. ctx.trace.mu.Lock()
  362. defer ctx.trace.mu.Unlock()
  363. if len(v) > propagationExtractMaxSize {
  364. log.Warn("Did not extract %s, size limit exceeded: %d. Incoming tags will not be propagated further.", traceTagsHeader, propagationExtractMaxSize)
  365. ctx.trace.setTag(keyPropagationError, "extract_max_size")
  366. return
  367. }
  368. var err error
  369. ctx.trace.propagatingTags, err = parsePropagatableTraceTags(v)
  370. if err != nil {
  371. log.Warn("Did not extract %s: %v. Incoming tags will not be propagated further.", traceTagsHeader, err.Error())
  372. ctx.trace.setTag(keyPropagationError, "decoding_error")
  373. }
  374. }
  375. // setPropagatingTag adds the key value pair to the map of propagating tags on the trace,
  376. // creating the map if one is not initialized.
  377. func setPropagatingTag(ctx *spanContext, k, v string) {
  378. if ctx.trace == nil {
  379. // extractors initialize a new spanContext, so the trace might be nil
  380. ctx.trace = newTrace()
  381. }
  382. ctx.trace.setPropagatingTag(k, v)
  383. }
  384. const (
  385. b3TraceIDHeader = "x-b3-traceid"
  386. b3SpanIDHeader = "x-b3-spanid"
  387. b3SampledHeader = "x-b3-sampled"
  388. b3SingleHeader = "b3"
  389. )
  390. // propagatorB3 implements Propagator and injects/extracts span contexts
  391. // using B3 headers. Only TextMap carriers are supported.
  392. type propagatorB3 struct{}
  393. func (p *propagatorB3) Inject(spanCtx ddtrace.SpanContext, carrier interface{}) error {
  394. switch c := carrier.(type) {
  395. case TextMapWriter:
  396. return p.injectTextMap(spanCtx, c)
  397. default:
  398. return ErrInvalidCarrier
  399. }
  400. }
  401. func (*propagatorB3) injectTextMap(spanCtx ddtrace.SpanContext, writer TextMapWriter) error {
  402. ctx, ok := spanCtx.(*spanContext)
  403. if !ok || ctx.traceID == 0 || ctx.spanID == 0 {
  404. return ErrInvalidSpanContext
  405. }
  406. writer.Set(b3TraceIDHeader, fmt.Sprintf("%016x", ctx.traceID))
  407. writer.Set(b3SpanIDHeader, fmt.Sprintf("%016x", ctx.spanID))
  408. if p, ok := ctx.samplingPriority(); ok {
  409. if p >= ext.PriorityAutoKeep {
  410. writer.Set(b3SampledHeader, "1")
  411. } else {
  412. writer.Set(b3SampledHeader, "0")
  413. }
  414. }
  415. return nil
  416. }
  417. func (p *propagatorB3) Extract(carrier interface{}) (ddtrace.SpanContext, error) {
  418. switch c := carrier.(type) {
  419. case TextMapReader:
  420. return p.extractTextMap(c)
  421. default:
  422. return nil, ErrInvalidCarrier
  423. }
  424. }
  425. func (*propagatorB3) extractTextMap(reader TextMapReader) (ddtrace.SpanContext, error) {
  426. var ctx spanContext
  427. err := reader.ForeachKey(func(k, v string) error {
  428. var err error
  429. key := strings.ToLower(k)
  430. switch key {
  431. case b3TraceIDHeader:
  432. if len(v) > 16 {
  433. v = v[len(v)-16:]
  434. }
  435. ctx.traceID, err = strconv.ParseUint(v, 16, 64)
  436. if err != nil {
  437. return ErrSpanContextCorrupted
  438. }
  439. case b3SpanIDHeader:
  440. ctx.spanID, err = strconv.ParseUint(v, 16, 64)
  441. if err != nil {
  442. return ErrSpanContextCorrupted
  443. }
  444. case b3SampledHeader:
  445. priority, err := strconv.Atoi(v)
  446. if err != nil {
  447. return ErrSpanContextCorrupted
  448. }
  449. ctx.setSamplingPriority(priority, samplernames.Unknown)
  450. default:
  451. }
  452. return nil
  453. })
  454. if err != nil {
  455. return nil, err
  456. }
  457. if ctx.traceID == 0 || ctx.spanID == 0 {
  458. return nil, ErrSpanContextNotFound
  459. }
  460. return &ctx, nil
  461. }
  462. // propagatorB3 implements Propagator and injects/extracts span contexts
  463. // using B3 headers. Only TextMap carriers are supported.
  464. type propagatorB3SingleHeader struct{}
  465. func (p *propagatorB3SingleHeader) Inject(spanCtx ddtrace.SpanContext, carrier interface{}) error {
  466. switch c := carrier.(type) {
  467. case TextMapWriter:
  468. return p.injectTextMap(spanCtx, c)
  469. default:
  470. return ErrInvalidCarrier
  471. }
  472. }
  473. func (*propagatorB3SingleHeader) injectTextMap(spanCtx ddtrace.SpanContext, writer TextMapWriter) error {
  474. ctx, ok := spanCtx.(*spanContext)
  475. if !ok || ctx.traceID == 0 || ctx.spanID == 0 {
  476. return ErrInvalidSpanContext
  477. }
  478. sb := strings.Builder{}
  479. sb.WriteString(fmt.Sprintf("%016x-%016x", ctx.traceID, ctx.spanID))
  480. if p, ok := ctx.samplingPriority(); ok {
  481. if p >= ext.PriorityAutoKeep {
  482. sb.WriteString("-1")
  483. } else {
  484. sb.WriteString("-0")
  485. }
  486. }
  487. writer.Set(b3SingleHeader, sb.String())
  488. return nil
  489. }
  490. func (p *propagatorB3SingleHeader) Extract(carrier interface{}) (ddtrace.SpanContext, error) {
  491. switch c := carrier.(type) {
  492. case TextMapReader:
  493. return p.extractTextMap(c)
  494. default:
  495. return nil, ErrInvalidCarrier
  496. }
  497. }
  498. func (*propagatorB3SingleHeader) extractTextMap(reader TextMapReader) (ddtrace.SpanContext, error) {
  499. var ctx spanContext
  500. err := reader.ForeachKey(func(k, v string) error {
  501. var err error
  502. key := strings.ToLower(k)
  503. switch key {
  504. case b3SingleHeader:
  505. b3Parts := strings.Split(v, "-")
  506. if len(b3Parts) >= 2 {
  507. if len(b3Parts[0]) > 16 {
  508. b3Parts[0] = b3Parts[0][len(b3Parts[0])-16:]
  509. }
  510. ctx.traceID, err = strconv.ParseUint(b3Parts[0], 16, 64)
  511. if err != nil {
  512. return ErrSpanContextCorrupted
  513. }
  514. ctx.spanID, err = strconv.ParseUint(b3Parts[1], 16, 64)
  515. if err != nil {
  516. return ErrSpanContextCorrupted
  517. }
  518. if len(b3Parts) >= 3 {
  519. switch b3Parts[2] {
  520. case "":
  521. break
  522. case "1", "d": // Treat 'debug' traces as priority 1
  523. ctx.setSamplingPriority(1, samplernames.Unknown)
  524. case "0":
  525. ctx.setSamplingPriority(0, samplernames.Unknown)
  526. default:
  527. return ErrSpanContextCorrupted
  528. }
  529. }
  530. } else {
  531. return ErrSpanContextCorrupted
  532. }
  533. default:
  534. }
  535. return nil
  536. })
  537. if err != nil {
  538. return nil, err
  539. }
  540. if ctx.traceID == 0 || ctx.spanID == 0 {
  541. return nil, ErrSpanContextNotFound
  542. }
  543. return &ctx, nil
  544. }
  545. const (
  546. traceparentHeader = "traceparent"
  547. tracestateHeader = "tracestate"
  548. w3cTraceIDTag = "w3cTraceID"
  549. )
  550. // propagatorW3c implements Propagator and injects/extracts span contexts
  551. // using W3C tracecontext/traceparent headers. Only TextMap carriers are supported.
  552. type propagatorW3c struct{}
  553. func (p *propagatorW3c) Inject(spanCtx ddtrace.SpanContext, carrier interface{}) error {
  554. switch c := carrier.(type) {
  555. case TextMapWriter:
  556. return p.injectTextMap(spanCtx, c)
  557. default:
  558. return ErrInvalidCarrier
  559. }
  560. }
  561. // injectTextMap propagates span context attributes into the writer,
  562. // in the format of the traceparentHeader and tracestateHeader.
  563. // traceparentHeader encodes W3C Trace Propagation version, 128-bit traceID,
  564. // spanID, and a flags field, which supports 8 unique flags.
  565. // The current specification only supports a single flag called sampled,
  566. // which is equal to 00000001 when no other flag is present.
  567. // tracestateHeader is a comma-separated list of list-members with a <key>=<value> format,
  568. // where each list-member is managed by a vendor or instrumentation library.
  569. func (*propagatorW3c) injectTextMap(spanCtx ddtrace.SpanContext, writer TextMapWriter) error {
  570. ctx, ok := spanCtx.(*spanContext)
  571. if !ok || ctx.traceID == 0 || ctx.spanID == 0 {
  572. return ErrInvalidSpanContext
  573. }
  574. flags := ""
  575. p, ok := ctx.samplingPriority()
  576. if ok && p >= ext.PriorityAutoKeep {
  577. flags = "01"
  578. } else {
  579. flags = "00"
  580. }
  581. var traceID string
  582. // if previous traceparent is valid, do NOT update the trace ID
  583. if ctx.trace != nil && ctx.trace.propagatingTags != nil {
  584. tag := ctx.trace.propagatingTags[w3cTraceIDTag]
  585. if len(tag) == 32 {
  586. id, err := strconv.ParseUint(tag[16:], 16, 64)
  587. if err == nil && id != 0 {
  588. traceID = tag
  589. }
  590. }
  591. }
  592. if len(traceID) == 0 {
  593. traceID = fmt.Sprintf("%032x", ctx.traceID)
  594. }
  595. writer.Set(traceparentHeader, fmt.Sprintf("00-%s-%016x-%v", traceID, ctx.spanID, flags))
  596. // if context priority / origin / tags were updated after extraction,
  597. // or the tracestateHeader doesn't start with `dd=`
  598. // we need to recreate tracestate
  599. if ctx.updated ||
  600. (ctx.trace != nil && ctx.trace.propagatingTags != nil && !strings.HasPrefix(ctx.trace.propagatingTags[tracestateHeader], "dd=")) ||
  601. len(ctx.trace.propagatingTags[tracestateHeader]) == 0 {
  602. writer.Set(tracestateHeader, composeTracestate(ctx, p, ctx.trace.propagatingTags[tracestateHeader]))
  603. } else {
  604. writer.Set(tracestateHeader, ctx.trace.propagatingTags[tracestateHeader])
  605. }
  606. return nil
  607. }
  608. var (
  609. // keyRgx is used to sanitize the keys of the datadog propagating tags.
  610. // Disallowed characters are comma (reserved as a list-member separator),
  611. // equals (reserved for list-member key-value separator),
  612. // space and characters outside the ASCII range 0x20 to 0x7E.
  613. // Disallowed characters must be replaced with the underscore.
  614. keyRgx = regexp.MustCompile(",|=|[^\\x20-\\x7E]+")
  615. // valueRgx is used to sanitize the values of the datadog propagating tags.
  616. // Disallowed characters are comma (reserved as a list-member separator),
  617. // semi-colon (reserved for separator between entries in the dd list-member),
  618. // tilde (reserved, will represent 0x3D (equals) in the encoded tag value,
  619. // and characters outside the ASCII range 0x20 to 0x7E.
  620. // Equals character must be encoded with a tilde.
  621. // Other disallowed characters must be replaced with the underscore.
  622. valueRgx = regexp.MustCompile(",|;|~|[^\\x20-\\x7E]+")
  623. // originRgx is used to sanitize the value of the datadog origin tag.
  624. // Disallowed characters are comma (reserved as a list-member separator),
  625. // semi-colon (reserved for separator between entries in the dd list-member),
  626. // equals (reserved for list-member key-value separator),
  627. // and characters outside the ASCII range 0x21 to 0x7E.
  628. // Disallowed characters must be replaced with the underscore.
  629. originRgx = regexp.MustCompile(",|=|;|[^\\x21-\\x7E]+")
  630. )
  631. // composeTracestate creates a tracestateHeader from the spancontext.
  632. // The Datadog tracing library is only responsible for managing the list member with key dd,
  633. // which holds the values of the sampling decision(`s:<value>`), origin(`o:<origin>`),
  634. // and propagated tags prefixed with `t.`(e.g. _dd.p.usr.id:usr_id tag will become `t.usr.id:usr_id`).
  635. func composeTracestate(ctx *spanContext, priority int, oldState string) string {
  636. var b strings.Builder
  637. b.WriteString(fmt.Sprintf("dd=s:%d", priority))
  638. listLength := 1
  639. if ctx.origin != "" {
  640. b.WriteString(fmt.Sprintf(";o:%s",
  641. originRgx.ReplaceAllString(ctx.origin, "_")))
  642. }
  643. for k, v := range ctx.trace.propagatingTags {
  644. if !strings.HasPrefix(k, "_dd.p.") {
  645. continue
  646. }
  647. // Datadog propagating tags must be appended to the tracestateHeader
  648. // with the `t.` prefix. Tag value must have all `=` signs replaced with a tilde (`~`).
  649. tag := fmt.Sprintf("t.%s:%s",
  650. keyRgx.ReplaceAllString(k[len("_dd.p."):], "_"),
  651. strings.ReplaceAll(valueRgx.ReplaceAllString(v, "_"), "=", "~"))
  652. if b.Len()+len(tag) > 256 {
  653. break
  654. }
  655. b.WriteString(";")
  656. b.WriteString(tag)
  657. }
  658. // the old state is split by vendors, must be concatenated with a `,`
  659. if len(oldState) == 0 {
  660. return b.String()
  661. }
  662. for _, s := range strings.Split(strings.Trim(oldState, " \t"), ",") {
  663. if strings.HasPrefix(s, "dd=") {
  664. continue
  665. }
  666. listLength++
  667. // if the resulting tracestateHeader exceeds 32 list-members,
  668. // remove the rightmost list-member(s)
  669. if listLength > 32 {
  670. break
  671. }
  672. b.WriteString("," + strings.Trim(s, " \t"))
  673. }
  674. return b.String()
  675. }
  676. func (p *propagatorW3c) Extract(carrier interface{}) (ddtrace.SpanContext, error) {
  677. switch c := carrier.(type) {
  678. case TextMapReader:
  679. return p.extractTextMap(c)
  680. default:
  681. return nil, ErrInvalidCarrier
  682. }
  683. }
  684. func (*propagatorW3c) extractTextMap(reader TextMapReader) (ddtrace.SpanContext, error) {
  685. var parentHeader string
  686. var stateHeader string
  687. // to avoid parsing tracestate header(s) if traceparent is invalid
  688. if err := reader.ForeachKey(func(k, v string) error {
  689. key := strings.ToLower(k)
  690. switch key {
  691. case traceparentHeader:
  692. if parentHeader != "" {
  693. return ErrSpanContextCorrupted
  694. }
  695. parentHeader = v
  696. case tracestateHeader:
  697. stateHeader = v
  698. }
  699. return nil
  700. }); err != nil {
  701. return nil, err
  702. }
  703. var ctx spanContext
  704. if err := parseTraceparent(&ctx, parentHeader); err != nil {
  705. return nil, err
  706. }
  707. if err := parseTracestate(&ctx, stateHeader); err != nil {
  708. return nil, err
  709. }
  710. return &ctx, nil
  711. }
  712. // parseTraceparent attempts to parse traceparentHeader which describes the position
  713. // of the incoming request in its trace graph in a portable, fixed-length format.
  714. // The format of the traceparentHeader is `-` separated string with in the
  715. // following format: `version-traceId-spanID-flags`,
  716. // where:
  717. // - version - represents the version of the W3C Tracecontext Propagation format in hex format.
  718. // - traceId - represents the propagated traceID in the format of 32 hex-encoded digits.
  719. // - spanID - represents the propagated spanID (parentID) in the format of 16 hex-encoded digits.
  720. // - flags - represents the propagated flags in the format of 2 hex-encoded digits, and supports 8 unique flags.
  721. // Example value of HTTP `traceparent` header: `00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01`,
  722. // Currently, Go tracer doesn't support 128-bit traceIDs, so the full traceID (32 hex-encoded digits) must be
  723. // stored into a field that is accessible from the span’s context. TraceId will be parsed from the least significant 16
  724. // hex-encoded digits into a 64-bit number.
  725. func parseTraceparent(ctx *spanContext, header string) error {
  726. nonWordCutset := "_-\t \n"
  727. header = strings.ToLower(strings.Trim(header, "\t -"))
  728. if len(header) == 0 {
  729. return ErrSpanContextNotFound
  730. }
  731. if len(header) != 55 {
  732. return ErrSpanContextCorrupted
  733. }
  734. parts := strings.Split(header, "-")
  735. if len(parts) != 4 {
  736. return ErrSpanContextCorrupted
  737. }
  738. version := strings.Trim(parts[0], nonWordCutset)
  739. if len(version) != 2 {
  740. return ErrSpanContextCorrupted
  741. }
  742. if v, err := strconv.ParseUint(version, 16, 64); err != nil || v == 255 {
  743. return ErrSpanContextCorrupted
  744. }
  745. // parsing traceID
  746. fullTraceID := strings.Trim(parts[1], nonWordCutset)
  747. if len(fullTraceID) != 32 {
  748. return ErrSpanContextCorrupted
  749. }
  750. // checking that the entire TraceID is a valid hex string
  751. if ok, err := regexp.MatchString("^[a-f0-9]+$", fullTraceID); !ok || err != nil {
  752. return ErrSpanContextCorrupted
  753. }
  754. var err error
  755. if ctx.traceID, err = strconv.ParseUint(fullTraceID[16:], 16, 64); err != nil {
  756. return ErrSpanContextCorrupted
  757. }
  758. if ctx.traceID == 0 {
  759. if strings.Trim(fullTraceID[:16], "0") == "" {
  760. return ErrSpanContextNotFound
  761. }
  762. }
  763. // setting trace-id to be used for span context propagation
  764. setPropagatingTag(ctx, w3cTraceIDTag, fullTraceID)
  765. // parsing spanID
  766. spanID := strings.Trim(parts[2], nonWordCutset)
  767. if len(spanID) != 16 {
  768. return ErrSpanContextCorrupted
  769. }
  770. if ok, err := regexp.MatchString("[a-f0-9]+", spanID); !ok || err != nil {
  771. return ErrSpanContextCorrupted
  772. }
  773. if ctx.spanID, err = strconv.ParseUint(spanID, 16, 64); err != nil {
  774. return ErrSpanContextCorrupted
  775. }
  776. if ctx.spanID == 0 {
  777. return ErrSpanContextNotFound
  778. }
  779. // parsing flags
  780. flags := parts[3]
  781. f, err := strconv.ParseInt(flags, 16, 8)
  782. if err != nil {
  783. return ErrSpanContextCorrupted
  784. }
  785. ctx.setSamplingPriority(int(f)&0x1, samplernames.Unknown)
  786. return nil
  787. }
  788. // parseTracestate attempts to parse tracestateHeader which is a list
  789. // with up to 32 comma-separated (,) list-members.
  790. // An example value would be: `vendorname1=opaqueValue1,vendorname2=opaqueValue2,dd=s:1;o:synthetics`,
  791. // Where `dd` list contains values that would be in x-datadog-tags as well as those needed for propagation information.
  792. // The keys to the “dd“ values have been shortened as follows to save space:
  793. // `sampling_priority` = `s`
  794. // `origin` = `o`
  795. // `_dd.p.` prefix = `t.`
  796. func parseTracestate(ctx *spanContext, header string) error {
  797. // if multiple headers are present, they must be combined and stored
  798. setPropagatingTag(ctx, tracestateHeader, header)
  799. list := strings.Split(strings.Trim(header, "\t "), ",")
  800. for _, s := range list {
  801. if !strings.HasPrefix(s, "dd=") {
  802. continue
  803. }
  804. dd := strings.Split(s[len("dd="):], ";")
  805. for _, val := range dd {
  806. x := strings.SplitN(val, ":", 2)
  807. if len(x) != 2 {
  808. continue
  809. }
  810. k, v := x[0], x[1]
  811. if k == "o" {
  812. ctx.origin = v
  813. } else if k == "s" {
  814. p, err := strconv.Atoi(v)
  815. if err != nil {
  816. // if the tracestate priority is absent, relying on traceparent value
  817. continue
  818. }
  819. flagPriority, _ := ctx.samplingPriority()
  820. if (flagPriority == 1 && p > 0) || (flagPriority == 0 && p <= 0) {
  821. ctx.setSamplingPriority(p, samplernames.Unknown)
  822. }
  823. } else if strings.HasPrefix(k, "t.") {
  824. k = k[len("t."):]
  825. v = strings.ReplaceAll(v, "~", "=")
  826. setPropagatingTag(ctx, "_dd.p."+k, v)
  827. }
  828. }
  829. }
  830. return nil
  831. }