Line data Source code
1 : #include "source/extensions/tracers/xray/xray_tracer_impl.h" 2 : 3 : #include "source/common/common/macros.h" 4 : #include "source/common/common/utility.h" 5 : #include "source/common/http/headers.h" 6 : #include "source/extensions/tracers/xray/localized_sampling.h" 7 : #include "source/extensions/tracers/xray/tracer.h" 8 : #include "source/extensions/tracers/xray/xray_configuration.h" 9 : 10 : namespace Envoy { 11 : namespace Extensions { 12 : namespace Tracers { 13 : namespace XRay { 14 : 15 : namespace { 16 : constexpr auto DefaultDaemonEndpoint = "127.0.0.1:2000"; 17 0 : XRayHeader parseXRayHeader(const Http::LowerCaseString& header) { 18 0 : const auto& lowered_header = header.get(); 19 0 : XRayHeader result; 20 0 : for (const auto& token : StringUtil::splitToken(lowered_header, ";")) { 21 0 : if (absl::StartsWith(token, "root=")) { 22 0 : result.trace_id_ = std::string(StringUtil::cropLeft(token, "=")); 23 0 : } else if (absl::StartsWith(token, "parent=")) { 24 0 : result.parent_id_ = std::string(StringUtil::cropLeft(token, "=")); 25 0 : } else if (absl::StartsWith(token, "sampled=")) { 26 0 : const auto s = StringUtil::cropLeft(token, "="); 27 0 : if (s == "1") { 28 0 : result.sample_decision_ = SamplingDecision::Sampled; 29 0 : } else if (s == "0") { 30 0 : result.sample_decision_ = SamplingDecision::NotSampled; 31 0 : } else { 32 0 : result.sample_decision_ = SamplingDecision::Unknown; 33 0 : } 34 0 : } 35 0 : } 36 0 : return result; 37 0 : } 38 : } // namespace 39 : 40 : Driver::Driver(const XRayConfiguration& config, 41 : Server::Configuration::TracerFactoryContext& context) 42 : : xray_config_(config), 43 0 : tls_slot_ptr_(context.serverFactoryContext().threadLocal().allocateSlot()) { 44 : 45 0 : const std::string daemon_endpoint = 46 0 : config.daemon_endpoint_.empty() ? DefaultDaemonEndpoint : config.daemon_endpoint_; 47 : 48 0 : ENVOY_LOG(debug, "send X-Ray generated segments to daemon address on {}", daemon_endpoint); 49 0 : sampling_strategy_ = std::make_unique<XRay::LocalizedSamplingStrategy>( 50 0 : xray_config_.sampling_rules_, context.serverFactoryContext().api().randomGenerator(), 51 0 : context.serverFactoryContext().timeSource()); 52 : 53 0 : tls_slot_ptr_->set([this, daemon_endpoint, 54 0 : &context](Event::Dispatcher&) -> ThreadLocal::ThreadLocalObjectSharedPtr { 55 0 : DaemonBrokerPtr broker = std::make_unique<DaemonBrokerImpl>(daemon_endpoint); 56 0 : TracerPtr tracer = std::make_unique<Tracer>( 57 0 : xray_config_.segment_name_, xray_config_.origin_, xray_config_.aws_metadata_, 58 0 : std::move(broker), context.serverFactoryContext().timeSource(), 59 0 : context.serverFactoryContext().api().randomGenerator()); 60 0 : return std::make_shared<XRay::Driver::TlsTracer>(std::move(tracer), *this); 61 0 : }); 62 0 : } 63 : 64 : Tracing::SpanPtr Driver::startSpan(const Tracing::Config& config, 65 : Tracing::TraceContext& trace_context, 66 : const StreamInfo::StreamInfo& stream_info, 67 : const std::string& operation_name, 68 0 : Tracing::Decision tracing_decision) { 69 : // First thing is to determine whether this request will be sampled or not. 70 : // if there's a X-Ray header and it has a sampling decision already determined (i.e. Sample=1) 71 : // then we can move on; otherwise, we ask the sampling strategy whether this request should be 72 : // sampled or not. 73 : // 74 : // The second step is create a Span. 75 : // If we have a XRay TraceID in the headers, then we create a SpanContext to pass that trace-id 76 : // around if no TraceID (which means no x-ray header) then this is a brand new span. 77 : 78 : // TODO(suniltheta) - how do we factor this into the logic above 79 0 : UNREFERENCED_PARAMETER(tracing_decision); 80 0 : const auto header = xRayTraceHeader().get(trace_context); 81 0 : absl::optional<bool> should_trace; 82 0 : XRayHeader xray_header; 83 0 : if (header.has_value()) { 84 : // This is an implicitly untrusted header, so only the first value is used. 85 0 : Http::LowerCaseString lowered_header_value{header.value()}; 86 0 : xray_header = parseXRayHeader(lowered_header_value); 87 : // if the sample_decision in the x-ray header is unknown then we try to make a decision based 88 : // on the sampling strategy 89 0 : if (xray_header.sample_decision_ == SamplingDecision::Sampled) { 90 0 : should_trace = true; 91 0 : } else if (xray_header.sample_decision_ == SamplingDecision::NotSampled) { 92 0 : should_trace = false; 93 0 : } else { 94 0 : ENVOY_LOG( 95 0 : trace, 96 0 : "Unable to determine from the X-Ray trace header whether request is sampled or not"); 97 0 : } 98 0 : } 99 : 100 0 : if (!should_trace.has_value()) { 101 0 : const SamplingRequest request{trace_context.host(), trace_context.method(), 102 0 : trace_context.path()}; 103 : 104 0 : should_trace = sampling_strategy_->shouldTrace(request); 105 0 : } 106 : 107 0 : auto* tracer = tls_slot_ptr_->getTyped<Driver::TlsTracer>().tracer_.get(); 108 0 : if (should_trace.value()) { 109 0 : return tracer->startSpan(config, operation_name, stream_info.startTime(), 110 0 : header.has_value() ? absl::optional<XRayHeader>(xray_header) 111 0 : : absl::nullopt, 112 0 : xForwardedForHeader().get(trace_context)); 113 0 : } 114 : 115 : // Instead of returning nullptr, we return a Span that is marked as not-sampled. 116 : // This is important to communicate that information to upstream services (see injectContext()). 117 : // Otherwise, the upstream service can decide to sample the request regardless and we end up with 118 : // more samples than we asked for. 119 0 : return tracer->createNonSampledSpan(header.has_value() ? absl::optional<XRayHeader>(xray_header) 120 0 : : absl::nullopt); 121 0 : } 122 : 123 : } // namespace XRay 124 : } // namespace Tracers 125 : } // namespace Extensions 126 : } // namespace Envoy