b3_propagator.h 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198
  1. // Copyright The OpenTelemetry Authors
  2. // SPDX-License-Identifier: Apache-2.0
  3. #pragma once
  4. #include <array>
  5. #include "detail/hex.h"
  6. #include "detail/string.h"
  7. #include "opentelemetry/context/propagation/text_map_propagator.h"
  8. #include "opentelemetry/trace/context.h"
  9. #include "opentelemetry/trace/default_span.h"
  10. #include "opentelemetry/version.h"
  11. OPENTELEMETRY_BEGIN_NAMESPACE
  12. namespace trace
  13. {
  14. namespace propagation
  15. {
  16. static const nostd::string_view kB3CombinedHeader = "b3";
  17. static const nostd::string_view kB3TraceIdHeader = "X-B3-TraceId";
  18. static const nostd::string_view kB3SpanIdHeader = "X-B3-SpanId";
  19. static const nostd::string_view kB3SampledHeader = "X-B3-Sampled";
  20. /*
  21. B3, single header:
  22. b3: 80f198ee56343ba864fe8b2a57d3eff7-e457b5a2e4d86bd1-1-05e3ac9a4f6e3b90
  23. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^ ^ ^^^^^^^^^^^^^^^^
  24. 0 TraceId 31 33 SpanId 48 | 52 ParentSpanId 68
  25. 50 Debug flag
  26. Multiheader version: X-B3-Sampled
  27. X-B3-TraceId X-B3-SpanId X-B3-ParentSpanId (ignored)
  28. */
  29. static const int kTraceIdHexStrLength = 32;
  30. static const int kSpanIdHexStrLength = 16;
  31. // The B3PropagatorExtractor class provides an interface that enables extracting context from
  32. // headers of HTTP requests. HTTP frameworks and clients can integrate with B3Propagator by
  33. // providing the object containing the headers, and a getter function for the extraction. Based on:
  34. // https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/context/api-propagators.md#b3-extract
  35. class B3PropagatorExtractor : public context::propagation::TextMapPropagator
  36. {
  37. public:
  38. // Returns the context that is stored in the HTTP header carrier.
  39. context::Context Extract(const context::propagation::TextMapCarrier &carrier,
  40. context::Context &context) noexcept override
  41. {
  42. SpanContext span_context = ExtractImpl(carrier);
  43. nostd::shared_ptr<Span> sp{new DefaultSpan(span_context)};
  44. if (span_context.IsValid())
  45. {
  46. return trace::SetSpan(context, sp);
  47. }
  48. else
  49. {
  50. return context;
  51. }
  52. }
  53. static TraceId TraceIdFromHex(nostd::string_view trace_id)
  54. {
  55. uint8_t buf[kTraceIdHexStrLength / 2];
  56. detail::HexToBinary(trace_id, buf, sizeof(buf));
  57. return TraceId(buf);
  58. }
  59. static SpanId SpanIdFromHex(nostd::string_view span_id)
  60. {
  61. uint8_t buf[kSpanIdHexStrLength / 2];
  62. detail::HexToBinary(span_id, buf, sizeof(buf));
  63. return SpanId(buf);
  64. }
  65. static TraceFlags TraceFlagsFromHex(nostd::string_view trace_flags)
  66. {
  67. if (trace_flags.length() != 1 || (trace_flags[0] != '1' && trace_flags[0] != 'd'))
  68. { // check for invalid length of flags and treat 'd' as sampled
  69. return TraceFlags(0);
  70. }
  71. return TraceFlags(TraceFlags::kIsSampled);
  72. }
  73. private:
  74. static SpanContext ExtractImpl(const context::propagation::TextMapCarrier &carrier)
  75. {
  76. nostd::string_view trace_id_hex;
  77. nostd::string_view span_id_hex;
  78. nostd::string_view trace_flags_hex;
  79. // first let's try a single-header variant
  80. auto singleB3Header = carrier.Get(kB3CombinedHeader);
  81. if (!singleB3Header.empty())
  82. {
  83. std::array<nostd::string_view, 3> fields{};
  84. // https://github.com/openzipkin/b3-propagation/blob/master/RATIONALE.md
  85. if (detail::SplitString(singleB3Header, '-', fields.data(), 3) < 2)
  86. {
  87. return SpanContext::GetInvalid();
  88. }
  89. trace_id_hex = fields[0];
  90. span_id_hex = fields[1];
  91. trace_flags_hex = fields[2];
  92. }
  93. else
  94. {
  95. trace_id_hex = carrier.Get(kB3TraceIdHeader);
  96. span_id_hex = carrier.Get(kB3SpanIdHeader);
  97. trace_flags_hex = carrier.Get(kB3SampledHeader);
  98. }
  99. if (!detail::IsValidHex(trace_id_hex) || !detail::IsValidHex(span_id_hex))
  100. {
  101. return SpanContext::GetInvalid();
  102. }
  103. TraceId trace_id = TraceIdFromHex(trace_id_hex);
  104. SpanId span_id = SpanIdFromHex(span_id_hex);
  105. if (!trace_id.IsValid() || !span_id.IsValid())
  106. {
  107. return SpanContext::GetInvalid();
  108. }
  109. return SpanContext(trace_id, span_id, TraceFlagsFromHex(trace_flags_hex), true);
  110. }
  111. };
  112. // The B3Propagator class provides interface that enables extracting and injecting context into
  113. // single header of HTTP Request.
  114. class B3Propagator : public B3PropagatorExtractor
  115. {
  116. public:
  117. // Sets the context for a HTTP header carrier with self defined rules.
  118. void Inject(context::propagation::TextMapCarrier &carrier,
  119. const context::Context &context) noexcept override
  120. {
  121. SpanContext span_context = trace::GetSpan(context)->GetContext();
  122. if (!span_context.IsValid())
  123. {
  124. return;
  125. }
  126. char trace_identity[kTraceIdHexStrLength + kSpanIdHexStrLength + 3];
  127. static_assert(sizeof(trace_identity) == 51, "b3 trace identity buffer size mismatch");
  128. span_context.trace_id().ToLowerBase16(nostd::span<char, 2 * TraceId::kSize>{
  129. &trace_identity[0], static_cast<std::size_t>(kTraceIdHexStrLength)});
  130. trace_identity[kTraceIdHexStrLength] = '-';
  131. span_context.span_id().ToLowerBase16(nostd::span<char, 2 * SpanId::kSize>{
  132. &trace_identity[kTraceIdHexStrLength + 1], static_cast<std::size_t>(kSpanIdHexStrLength)});
  133. trace_identity[kTraceIdHexStrLength + kSpanIdHexStrLength + 1] = '-';
  134. trace_identity[kTraceIdHexStrLength + kSpanIdHexStrLength + 2] =
  135. span_context.trace_flags().IsSampled() ? '1' : '0';
  136. carrier.Set(kB3CombinedHeader, nostd::string_view(trace_identity, sizeof(trace_identity)));
  137. }
  138. bool Fields(nostd::function_ref<bool(nostd::string_view)> callback) const noexcept override
  139. {
  140. return callback(kB3CombinedHeader);
  141. }
  142. };
  143. class B3PropagatorMultiHeader : public B3PropagatorExtractor
  144. {
  145. public:
  146. void Inject(context::propagation::TextMapCarrier &carrier,
  147. const context::Context &context) noexcept override
  148. {
  149. SpanContext span_context = GetSpan(context)->GetContext();
  150. if (!span_context.IsValid())
  151. {
  152. return;
  153. }
  154. char trace_id[32];
  155. TraceId(span_context.trace_id()).ToLowerBase16(trace_id);
  156. char span_id[16];
  157. SpanId(span_context.span_id()).ToLowerBase16(span_id);
  158. char trace_flags[2];
  159. TraceFlags(span_context.trace_flags()).ToLowerBase16(trace_flags);
  160. carrier.Set(kB3TraceIdHeader, nostd::string_view(trace_id, sizeof(trace_id)));
  161. carrier.Set(kB3SpanIdHeader, nostd::string_view(span_id, sizeof(span_id)));
  162. carrier.Set(kB3SampledHeader, nostd::string_view(trace_flags + 1, 1));
  163. }
  164. bool Fields(nostd::function_ref<bool(nostd::string_view)> callback) const noexcept override
  165. {
  166. return callback(kB3TraceIdHeader) && callback(kB3SpanIdHeader) && callback(kB3SampledHeader);
  167. }
  168. };
  169. } // namespace propagation
  170. } // namespace trace
  171. OPENTELEMETRY_END_NAMESPACE