You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

124 lines
3.6 KiB

  1. // Copyright 2018, OpenCensus Authors
  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 b3 contains a propagation.HTTPFormat implementation
  15. // for B3 propagation. See https://github.com/openzipkin/b3-propagation
  16. // for more details.
  17. package b3 // import "go.opencensus.io/plugin/ochttp/propagation/b3"
  18. import (
  19. "encoding/hex"
  20. "net/http"
  21. "go.opencensus.io/trace"
  22. "go.opencensus.io/trace/propagation"
  23. )
  24. // B3 headers that OpenCensus understands.
  25. const (
  26. TraceIDHeader = "X-B3-TraceId"
  27. SpanIDHeader = "X-B3-SpanId"
  28. SampledHeader = "X-B3-Sampled"
  29. )
  30. // HTTPFormat implements propagation.HTTPFormat to propagate
  31. // traces in HTTP headers in B3 propagation format.
  32. // HTTPFormat skips the X-B3-ParentId and X-B3-Flags headers
  33. // because there are additional fields not represented in the
  34. // OpenCensus span context. Spans created from the incoming
  35. // header will be the direct children of the client-side span.
  36. // Similarly, receiver of the outgoing spans should use client-side
  37. // span created by OpenCensus as the parent.
  38. type HTTPFormat struct{}
  39. var _ propagation.HTTPFormat = (*HTTPFormat)(nil)
  40. // SpanContextFromRequest extracts a B3 span context from incoming requests.
  41. func (f *HTTPFormat) SpanContextFromRequest(req *http.Request) (sc trace.SpanContext, ok bool) {
  42. tid, ok := ParseTraceID(req.Header.Get(TraceIDHeader))
  43. if !ok {
  44. return trace.SpanContext{}, false
  45. }
  46. sid, ok := ParseSpanID(req.Header.Get(SpanIDHeader))
  47. if !ok {
  48. return trace.SpanContext{}, false
  49. }
  50. sampled, _ := ParseSampled(req.Header.Get(SampledHeader))
  51. return trace.SpanContext{
  52. TraceID: tid,
  53. SpanID: sid,
  54. TraceOptions: sampled,
  55. }, true
  56. }
  57. // ParseTraceID parses the value of the X-B3-TraceId header.
  58. func ParseTraceID(tid string) (trace.TraceID, bool) {
  59. if tid == "" {
  60. return trace.TraceID{}, false
  61. }
  62. b, err := hex.DecodeString(tid)
  63. if err != nil {
  64. return trace.TraceID{}, false
  65. }
  66. var traceID trace.TraceID
  67. if len(b) <= 8 {
  68. // The lower 64-bits.
  69. start := 8 + (8 - len(b))
  70. copy(traceID[start:], b)
  71. } else {
  72. start := 16 - len(b)
  73. copy(traceID[start:], b)
  74. }
  75. return traceID, true
  76. }
  77. // ParseSpanID parses the value of the X-B3-SpanId or X-B3-ParentSpanId headers.
  78. func ParseSpanID(sid string) (spanID trace.SpanID, ok bool) {
  79. if sid == "" {
  80. return trace.SpanID{}, false
  81. }
  82. b, err := hex.DecodeString(sid)
  83. if err != nil {
  84. return trace.SpanID{}, false
  85. }
  86. start := 8 - len(b)
  87. copy(spanID[start:], b)
  88. return spanID, true
  89. }
  90. // ParseSampled parses the value of the X-B3-Sampled header.
  91. func ParseSampled(sampled string) (trace.TraceOptions, bool) {
  92. switch sampled {
  93. case "true", "1":
  94. return trace.TraceOptions(1), true
  95. default:
  96. return trace.TraceOptions(0), false
  97. }
  98. }
  99. // SpanContextToRequest modifies the given request to include B3 headers.
  100. func (f *HTTPFormat) SpanContextToRequest(sc trace.SpanContext, req *http.Request) {
  101. req.Header.Set(TraceIDHeader, hex.EncodeToString(sc.TraceID[:]))
  102. req.Header.Set(SpanIDHeader, hex.EncodeToString(sc.SpanID[:]))
  103. var sampled string
  104. if sc.IsSampled() {
  105. sampled = "1"
  106. } else {
  107. sampled = "0"
  108. }
  109. req.Header.Set(SampledHeader, sampled)
  110. }