propagation.go 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121
  1. // Copyright (c) 2017 Uber Technologies, Inc.
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. package zipkin
  15. import (
  16. "strconv"
  17. "strings"
  18. opentracing "github.com/opentracing/opentracing-go"
  19. "github.com/uber/jaeger-client-go"
  20. )
  21. // Option is a function that sets an option on Propagator
  22. type Option func(propagator *Propagator)
  23. // BaggagePrefix is a function that sets baggage prefix on Propagator
  24. func BaggagePrefix(prefix string) Option {
  25. return func(propagator *Propagator) {
  26. propagator.baggagePrefix = prefix
  27. }
  28. }
  29. // Propagator is an Injector and Extractor
  30. type Propagator struct {
  31. baggagePrefix string
  32. }
  33. // NewZipkinB3HTTPHeaderPropagator creates a Propagator for extracting and injecting
  34. // Zipkin HTTP B3 headers into SpanContexts. Baggage is by default enabled and uses prefix
  35. // 'baggage-'.
  36. func NewZipkinB3HTTPHeaderPropagator(opts ...Option) Propagator {
  37. p := Propagator{baggagePrefix: "baggage-"}
  38. for _, opt := range opts {
  39. opt(&p)
  40. }
  41. return p
  42. }
  43. // Inject conforms to the Injector interface for decoding Zipkin HTTP B3 headers
  44. func (p Propagator) Inject(
  45. sc jaeger.SpanContext,
  46. abstractCarrier interface{},
  47. ) error {
  48. textMapWriter, ok := abstractCarrier.(opentracing.TextMapWriter)
  49. if !ok {
  50. return opentracing.ErrInvalidCarrier
  51. }
  52. textMapWriter.Set("x-b3-traceid", sc.TraceID().String())
  53. if sc.ParentID() != 0 {
  54. textMapWriter.Set("x-b3-parentspanid", strconv.FormatUint(uint64(sc.ParentID()), 16))
  55. }
  56. textMapWriter.Set("x-b3-spanid", strconv.FormatUint(uint64(sc.SpanID()), 16))
  57. if sc.IsSampled() {
  58. textMapWriter.Set("x-b3-sampled", "1")
  59. } else {
  60. textMapWriter.Set("x-b3-sampled", "0")
  61. }
  62. sc.ForeachBaggageItem(func(k, v string) bool {
  63. textMapWriter.Set(p.baggagePrefix+k, v)
  64. return true
  65. })
  66. return nil
  67. }
  68. // Extract conforms to the Extractor interface for encoding Zipkin HTTP B3 headers
  69. func (p Propagator) Extract(abstractCarrier interface{}) (jaeger.SpanContext, error) {
  70. textMapReader, ok := abstractCarrier.(opentracing.TextMapReader)
  71. if !ok {
  72. return jaeger.SpanContext{}, opentracing.ErrInvalidCarrier
  73. }
  74. var traceID jaeger.TraceID
  75. var spanID uint64
  76. var parentID uint64
  77. sampled := false
  78. var baggage map[string]string
  79. err := textMapReader.ForeachKey(func(rawKey, value string) error {
  80. key := strings.ToLower(rawKey) // TODO not necessary for plain TextMap
  81. var err error
  82. if key == "x-b3-traceid" {
  83. traceID, err = jaeger.TraceIDFromString(value)
  84. } else if key == "x-b3-parentspanid" {
  85. parentID, err = strconv.ParseUint(value, 16, 64)
  86. } else if key == "x-b3-spanid" {
  87. spanID, err = strconv.ParseUint(value, 16, 64)
  88. } else if key == "x-b3-sampled" && (value == "1" || value == "true") {
  89. sampled = true
  90. } else if strings.HasPrefix(key, p.baggagePrefix) {
  91. if baggage == nil {
  92. baggage = make(map[string]string)
  93. }
  94. baggage[key[len(p.baggagePrefix):]] = value
  95. }
  96. return err
  97. })
  98. if err != nil {
  99. return jaeger.SpanContext{}, err
  100. }
  101. if !traceID.IsValid() {
  102. return jaeger.SpanContext{}, opentracing.ErrSpanContextNotFound
  103. }
  104. return jaeger.NewSpanContext(
  105. traceID,
  106. jaeger.SpanID(spanID),
  107. jaeger.SpanID(parentID),
  108. sampled, baggage), nil
  109. }