LCOV - code coverage report
Current view: top level - source/extensions/tracers/xray - tracer.cc (source / functions) Hit Total Coverage
Test: coverage.dat Lines: 0 158 0.0 %
Date: 2024-01-05 06:35:25 Functions: 0 9 0.0 %

          Line data    Source code
       1             : #include "source/extensions/tracers/xray/tracer.h"
       2             : 
       3             : #include <algorithm>
       4             : #include <chrono>
       5             : #include <string>
       6             : 
       7             : #include "envoy/http/header_map.h"
       8             : #include "envoy/network/listener.h"
       9             : 
      10             : #include "source/common/common/assert.h"
      11             : #include "source/common/common/fmt.h"
      12             : #include "source/common/protobuf/utility.h"
      13             : #include "source/common/tracing/http_tracer_impl.h"
      14             : #include "source/extensions/tracers/xray/daemon.pb.validate.h"
      15             : 
      16             : namespace Envoy {
      17             : namespace Extensions {
      18             : namespace Tracers {
      19             : namespace XRay {
      20             : 
      21             : namespace {
      22             : constexpr absl::string_view XRaySerializationVersion = "1";
      23             : constexpr absl::string_view DirectionKey = "direction";
      24             : 
      25             : // X-Ray Trace ID Format
      26             : //
      27             : // A trace_id consists of three parts separated by hyphens.
      28             : // For example, 1-58406cf0-a006649127e371903a2de979.
      29             : // This includes:
      30             : //
      31             : // - The version number, that is, 1.
      32             : // - The time of the original request, in Unix epoch time, in 8 hexadecimal digits.
      33             : // - A 96-bit unique identifier in 24 hexadecimal digits.
      34             : //
      35             : // For more details see:
      36             : // https://docs.aws.amazon.com/xray/latest/devguide/xray-api-segmentdocuments.html#api-segmentdocuments-fields
      37           0 : std::string generateTraceId(SystemTime point_in_time, Random::RandomGenerator& random) {
      38           0 :   using std::chrono::seconds;
      39           0 :   using std::chrono::time_point_cast;
      40             :   // epoch in seconds represented as 8 hexadecimal characters
      41           0 :   const auto epoch = time_point_cast<seconds>(point_in_time).time_since_epoch().count();
      42           0 :   std::string uuid = random.uuid();
      43             :   // unique id represented as 24 hexadecimal digits and no dashes
      44           0 :   uuid.erase(std::remove(uuid.begin(), uuid.end(), '-'), uuid.end());
      45           0 :   ASSERT(uuid.length() >= 24);
      46           0 :   const std::string out =
      47           0 :       absl::StrCat(XRaySerializationVersion, "-", Hex::uint32ToHex(epoch), "-", uuid.substr(0, 24));
      48           0 :   return out;
      49           0 : }
      50             : 
      51             : } // namespace
      52             : 
      53           0 : void Span::finishSpan() {
      54           0 :   using std::chrono::time_point_cast;
      55           0 :   using namespace source::extensions::tracers::xray;
      56             :   // X-Ray expects timestamps to be in epoch seconds with milli/micro-second precision as a fraction
      57           0 :   using SecondsWithFraction = std::chrono::duration<double>;
      58           0 :   if (!sampled()) {
      59           0 :     return;
      60           0 :   }
      61             : 
      62           0 :   daemon::Segment s;
      63           0 :   s.set_name(name());
      64           0 :   s.set_id(id());
      65           0 :   s.set_trace_id(traceId());
      66           0 :   s.set_start_time(time_point_cast<SecondsWithFraction>(startTime()).time_since_epoch().count());
      67           0 :   s.set_end_time(
      68           0 :       time_point_cast<SecondsWithFraction>(time_source_.systemTime()).time_since_epoch().count());
      69           0 :   s.set_origin(origin());
      70           0 :   s.set_parent_id(parentId());
      71           0 :   s.set_error(clientError());
      72           0 :   s.set_fault(serverError());
      73           0 :   s.set_throttle(isThrottled());
      74           0 :   if (type() == Subsegment) {
      75           0 :     s.set_type(std::string(Subsegment));
      76           0 :   }
      77           0 :   auto* aws = s.mutable_aws()->mutable_fields();
      78           0 :   for (const auto& field : aws_metadata_) {
      79           0 :     aws->insert({field.first, field.second});
      80           0 :   }
      81             : 
      82           0 :   auto* request_fields = s.mutable_http()->mutable_request()->mutable_fields();
      83           0 :   for (const auto& field : http_request_annotations_) {
      84           0 :     request_fields->insert({field.first, field.second});
      85           0 :   }
      86             : 
      87           0 :   auto* response_fields = s.mutable_http()->mutable_response()->mutable_fields();
      88           0 :   for (const auto& field : http_response_annotations_) {
      89           0 :     response_fields->insert({field.first, field.second});
      90           0 :   }
      91             : 
      92           0 :   for (const auto& item : custom_annotations_) {
      93           0 :     s.mutable_annotations()->insert({item.first, item.second});
      94           0 :   }
      95             :   // `direction` will be either "ingress" or "egress"
      96           0 :   s.mutable_annotations()->insert({std::string(DirectionKey), direction()});
      97             : 
      98           0 :   const std::string json = MessageUtil::getJsonStringFromMessageOrError(
      99           0 :       s, false /* pretty_print  */, false /* always_print_primitive_fields */);
     100             : 
     101           0 :   broker_.send(json);
     102           0 : } // namespace XRay
     103             : 
     104           0 : const Tracing::TraceContextHandler& xRayTraceHeader() {
     105           0 :   CONSTRUCT_ON_FIRST_USE(Tracing::TraceContextHandler, "x-amzn-trace-id");
     106           0 : }
     107             : 
     108           0 : const Tracing::TraceContextHandler& xForwardedForHeader() {
     109           0 :   CONSTRUCT_ON_FIRST_USE(Tracing::TraceContextHandler, "x-forwarded-for");
     110           0 : }
     111             : 
     112             : void Span::injectContext(Tracing::TraceContext& trace_context,
     113           0 :                          const Upstream::HostDescriptionConstSharedPtr&) {
     114           0 :   const std::string xray_header_value =
     115           0 :       fmt::format("Root={};Parent={};Sampled={}", traceId(), id(), sampled() ? "1" : "0");
     116           0 :   xRayTraceHeader().setRefKey(trace_context, xray_header_value);
     117           0 : }
     118             : 
     119             : Tracing::SpanPtr Span::spawnChild(const Tracing::Config& config, const std::string& operation_name,
     120           0 :                                   Envoy::SystemTime start_time) {
     121           0 :   auto child_span = std::make_unique<XRay::Span>(time_source_, random_, broker_);
     122           0 :   child_span->setName(operation_name);
     123           0 :   child_span->setOperation(operation_name);
     124           0 :   child_span->setDirection(Tracing::TracerUtility::toString(config.operationName()));
     125           0 :   child_span->setStartTime(start_time);
     126           0 :   child_span->setParentId(id());
     127           0 :   child_span->setTraceId(traceId());
     128           0 :   child_span->setSampled(sampled());
     129           0 :   child_span->setType(Subsegment);
     130           0 :   return child_span;
     131           0 : }
     132             : 
     133             : Tracing::SpanPtr Tracer::startSpan(const Tracing::Config& config, const std::string& operation_name,
     134             :                                    Envoy::SystemTime start_time,
     135             :                                    const absl::optional<XRayHeader>& xray_header,
     136           0 :                                    const absl::optional<absl::string_view> client_ip) {
     137             : 
     138           0 :   auto span_ptr = std::make_unique<XRay::Span>(time_source_, random_, *daemon_broker_);
     139           0 :   span_ptr->setName(segment_name_);
     140           0 :   span_ptr->setOperation(operation_name);
     141           0 :   span_ptr->setDirection(Tracing::TracerUtility::toString(config.operationName()));
     142             :   // Even though we have a TimeSource member in the tracer, we assume the start_time argument has a
     143             :   // more precise value than calling the systemTime() at this point in time.
     144           0 :   span_ptr->setStartTime(start_time);
     145           0 :   span_ptr->setOrigin(origin_);
     146           0 :   span_ptr->setAwsMetadata(aws_metadata_);
     147           0 :   if (client_ip) {
     148           0 :     span_ptr->addToHttpRequestAnnotations(SpanClientIp,
     149           0 :                                           ValueUtil::stringValue(std::string(*client_ip)));
     150             :     // The `client_ip` is the address specified in the HTTP X-Forwarded-For header.
     151           0 :     span_ptr->addToHttpRequestAnnotations(SpanXForwardedFor, ValueUtil::boolValue(true));
     152           0 :   }
     153             : 
     154           0 :   if (xray_header) {
     155             :     // There's a previous span that this span should be based-on.
     156           0 :     span_ptr->setParentId(xray_header->parent_id_);
     157           0 :     span_ptr->setTraceId(xray_header->trace_id_);
     158           0 :     switch (xray_header->sample_decision_) {
     159           0 :     case SamplingDecision::Sampled:
     160           0 :       span_ptr->setSampled(true);
     161           0 :       break;
     162           0 :     case SamplingDecision::NotSampled:
     163             :       // should never get here. If the header has Sampled=0 then we never call startSpan().
     164           0 :       IS_ENVOY_BUG("unexpected code path hit");
     165           0 :     default:
     166           0 :       break;
     167           0 :     }
     168           0 :   } else {
     169           0 :     span_ptr->setTraceId(generateTraceId(time_source_.systemTime(), random_));
     170           0 :   }
     171           0 :   return span_ptr;
     172           0 : }
     173             : 
     174           0 : XRay::SpanPtr Tracer::createNonSampledSpan(const absl::optional<XRayHeader>& xray_header) const {
     175           0 :   auto span_ptr = std::make_unique<XRay::Span>(time_source_, random_, *daemon_broker_);
     176           0 :   if (xray_header) {
     177             :     // There's a previous span that this span should be based-on.
     178           0 :     span_ptr->setParentId(xray_header->parent_id_);
     179           0 :     span_ptr->setTraceId(xray_header->trace_id_);
     180           0 :   } else {
     181           0 :     span_ptr->setTraceId(generateTraceId(time_source_.systemTime(), random_));
     182           0 :   }
     183           0 :   span_ptr->setSampled(false);
     184           0 :   return span_ptr;
     185           0 : }
     186             : 
     187           0 : void Span::setTag(absl::string_view name, absl::string_view value) {
     188             :   // For the full set of values see:
     189             :   // https://docs.aws.amazon.com/xray/latest/devguide/xray-api-segmentdocuments.html#api-segmentdocuments-http
     190           0 :   constexpr auto SpanContentLength = "content_length";
     191           0 :   constexpr auto SpanMethod = "method";
     192           0 :   constexpr auto SpanUrl = "url";
     193             : 
     194           0 :   if (name.empty() || value.empty()) {
     195           0 :     return;
     196           0 :   }
     197             : 
     198           0 :   if (name == Tracing::Tags::get().HttpUrl) {
     199           0 :     addToHttpRequestAnnotations(SpanUrl, ValueUtil::stringValue(std::string(value)));
     200           0 :   } else if (name == Tracing::Tags::get().HttpMethod) {
     201           0 :     addToHttpRequestAnnotations(SpanMethod, ValueUtil::stringValue(std::string(value)));
     202           0 :   } else if (name == Tracing::Tags::get().UserAgent) {
     203           0 :     addToHttpRequestAnnotations(Tracing::Tags::get().UserAgent,
     204           0 :                                 ValueUtil::stringValue(std::string(value)));
     205           0 :   } else if (name == Tracing::Tags::get().HttpStatusCode) {
     206           0 :     uint64_t status_code;
     207           0 :     if (!absl::SimpleAtoi(value, &status_code)) {
     208           0 :       ENVOY_LOG(debug, "{} must be a number, given: {}", Tracing::Tags::get().HttpStatusCode,
     209           0 :                 value);
     210           0 :       return;
     211           0 :     }
     212           0 :     setResponseStatusCode(status_code);
     213           0 :     addToHttpResponseAnnotations(Tracing::Tags::get().Status, ValueUtil::numberValue(status_code));
     214           0 :   } else if (name == Tracing::Tags::get().ResponseSize) {
     215           0 :     uint64_t response_size;
     216           0 :     if (!absl::SimpleAtoi(value, &response_size)) {
     217           0 :       ENVOY_LOG(debug, "{} must be a number, given: {}", Tracing::Tags::get().ResponseSize, value);
     218           0 :       return;
     219           0 :     }
     220           0 :     addToHttpResponseAnnotations(SpanContentLength, ValueUtil::numberValue(response_size));
     221           0 :   } else if (name == Tracing::Tags::get().PeerAddress) {
     222             :     // Use PeerAddress if client_ip is not already set from the header.
     223           0 :     if (!hasKeyInHttpRequestAnnotations(SpanClientIp)) {
     224           0 :       addToHttpRequestAnnotations(SpanClientIp, ValueUtil::stringValue(std::string(value)));
     225             :       // In this case, PeerAddress refers to the client's actual IP address, not
     226             :       // the address specified in the HTTP X-Forwarded-For header.
     227           0 :       addToHttpRequestAnnotations(SpanXForwardedFor, ValueUtil::boolValue(false));
     228           0 :     }
     229           0 :   } else if (name == Tracing::Tags::get().Error && value == Tracing::Tags::get().True) {
     230           0 :     setServerError();
     231           0 :   } else {
     232           0 :     custom_annotations_.emplace(name, value);
     233           0 :   }
     234           0 : }
     235             : 
     236             : } // namespace XRay
     237             : } // namespace Tracers
     238             : } // namespace Extensions
     239             : } // namespace Envoy

Generated by: LCOV version 1.15