1
#include "source/extensions/tracers/datadog/tracer.h"
2

            
3
#include <memory>
4
#include <utility>
5

            
6
#include "envoy/tracing/trace_context.h"
7

            
8
#include "source/common/common/assert.h"
9
#include "source/common/config/utility.h"
10
#include "source/common/tracing/null_span_impl.h"
11
#include "source/extensions/tracers/datadog/agent_http_client.h"
12
#include "source/extensions/tracers/datadog/dict_util.h"
13
#include "source/extensions/tracers/datadog/event_scheduler.h"
14
#include "source/extensions/tracers/datadog/logger.h"
15
#include "source/extensions/tracers/datadog/span.h"
16
#include "source/extensions/tracers/datadog/time_util.h"
17

            
18
#include "datadog/dict_reader.h"
19
#include "datadog/error.h"
20
#include "datadog/sampling_priority.h"
21
#include "datadog/span_config.h"
22
#include "datadog/trace_segment.h"
23
#include "datadog/tracer_config.h"
24

            
25
namespace Envoy {
26
namespace Extensions {
27
namespace Tracers {
28
namespace Datadog {
29
namespace {
30

            
31
std::shared_ptr<Tracer::ThreadLocalTracer> makeThreadLocalTracer(
32
    datadog::tracing::TracerConfig config, Upstream::ClusterManager& cluster_manager,
33
    const std::string& collector_cluster, const std::string& collector_reference_host,
34
    TracerStats& tracer_stats, Event::Dispatcher& dispatcher, spdlog::logger& logger,
35
34
    TimeSource& time_source) {
36
34
  config.logger = std::make_shared<Logger>(logger);
37
34
  config.agent.event_scheduler = std::make_shared<EventScheduler>(dispatcher);
38
34
  config.agent.http_client = std::make_shared<AgentHTTPClient>(
39
34
      cluster_manager, collector_cluster, collector_reference_host, tracer_stats, time_source);
40

            
41
  // Disable telemetry to avoid needing a separate HTTP client for telemetry.
42
  // Envoy has its own telemetry system and doesn't need dd-trace-cpp's telemetry.
43
34
  config.telemetry.enabled = false;
44

            
45
34
  datadog::tracing::Expected<datadog::tracing::FinalizedTracerConfig> maybe_config =
46
34
      datadog::tracing::finalize_config(config);
47
34
  if (datadog::tracing::Error* error = maybe_config.if_error()) {
48
1
    datadog::tracing::StringView prefix =
49
1
        "Unable to configure Datadog tracer. Tracing is now disabled. Error: ";
50
1
    config.logger->log_error(error->with_prefix(prefix));
51
1
    return std::make_shared<Tracer::ThreadLocalTracer>();
52
1
  }
53

            
54
33
  return std::make_shared<Tracer::ThreadLocalTracer>(*maybe_config);
55
34
}
56

            
57
} // namespace
58

            
59
Tracer::ThreadLocalTracer::ThreadLocalTracer(const datadog::tracing::FinalizedTracerConfig& config)
60
33
    : tracer(config) {}
61

            
62
Tracer::Tracer(const std::string& collector_cluster, const std::string& collector_reference_host,
63
               const datadog::tracing::TracerConfig& config,
64
               Upstream::ClusterManager& cluster_manager, Stats::Scope& scope,
65
               ThreadLocal::SlotAllocator& thread_local_slot_allocator, TimeSource& time_source)
66
36
    : tracer_stats_(makeTracerStats(scope)),
67
      thread_local_slot_(
68
36
          ThreadLocal::TypedSlot<ThreadLocalTracer>::makeUnique(thread_local_slot_allocator)) {
69
36
  const bool allow_added_via_api = true;
70
36
  THROW_IF_NOT_OK_REF(Config::Utility::checkCluster("envoy.tracers.datadog", collector_cluster,
71
36
                                                    cluster_manager, allow_added_via_api)
72
36
                          .status());
73

            
74
34
  thread_local_slot_->set([&logger = ENVOY_LOGGER(), collector_cluster, collector_reference_host,
75
34
                           config, &tracer_stats = tracer_stats_, &cluster_manager,
76
34
                           &time_source](Event::Dispatcher& dispatcher) {
77
34
    return makeThreadLocalTracer(config, cluster_manager, collector_cluster,
78
34
                                 collector_reference_host, tracer_stats, dispatcher, logger,
79
34
                                 time_source);
80
34
  });
81
34
}
82

            
83
// Tracer::TracingDriver
84

            
85
Tracing::SpanPtr Tracer::startSpan(const Tracing::Config&, Tracing::TraceContext& trace_context,
86
                                   const StreamInfo::StreamInfo& stream_info,
87
                                   const std::string& operation_name,
88
29
                                   Tracing::Decision tracing_decision) {
89
29
  ThreadLocalTracer& thread_local_tracer = **thread_local_slot_;
90
29
  if (!thread_local_tracer.tracer) {
91
1
    return std::make_unique<Tracing::NullSpan>();
92
1
  }
93

            
94
  // The OpenTracing implementation ignored the `Tracing::Config` argument,
95
  // so we will as well.
96
28
  datadog::tracing::SpanConfig span_config;
97
  // The `operation_name` parameter to this function more closely matches
98
  // Datadog's concept of "resource name." Datadog's "span name," or "operation
99
  // name," instead describes the category of operation being performed, which
100
  // here we hard-code.
101
28
  span_config.name = "envoy.proxy";
102
28
  span_config.resource = operation_name;
103
28
  span_config.start = estimateTime(stream_info.startTime());
104

            
105
28
  TraceContextReader reader{trace_context};
106
28
  datadog::tracing::Span span =
107
28
      extractOrCreateSpan(*thread_local_tracer.tracer, span_config, reader);
108

            
109
  // If we did not extract a sampling decision, and if Envoy is telling us to
110
  // drop the trace, then we treat that as a "user drop" (manual override).
111
  //
112
  // If Envoy is telling us to keep the trace, then we leave it up to the
113
  // tracer's internal sampler (which might decide to drop the trace anyway).
114
28
  const bool use_local_decision = !span.trace_segment().sampling_decision().has_value();
115
28
  if (use_local_decision && !tracing_decision.traced) {
116
    // TODO(wbpcode): use USER_KEEP to indicate that the trace should be kept if the
117
    // Envoy is telling us to keep the trace.
118
3
    span.trace_segment().override_sampling_priority(
119
3
        int(datadog::tracing::SamplingPriority::USER_DROP));
120
3
  }
121

            
122
28
  return std::make_unique<Span>(std::move(span), use_local_decision);
123
29
}
124

            
125
datadog::tracing::Span Tracer::extractOrCreateSpan(datadog::tracing::Tracer& tracer,
126
                                                   const datadog::tracing::SpanConfig& span_config,
127
28
                                                   const datadog::tracing::DictReader& reader) {
128
28
  datadog::tracing::Expected<datadog::tracing::Span> maybe_span =
129
28
      tracer.extract_span(reader, span_config);
130
28
  if (datadog::tracing::Error* error = maybe_span.if_error()) {
131
    // We didn't extract a span. Either there's no span to extract, or an
132
    // error occurred during extraction.
133
    //
134
    // Either way, we're going to create a new root span, but if an error
135
    // occurred we're going to log the error.
136
6
    if (error->code != datadog::tracing::Error::NO_SPAN_TO_EXTRACT) {
137
1
      ENVOY_LOG(
138
1
          error,
139
1
          "Unable to extract span context. Creating a new trace instead. Error [error {}]: {}",
140
1
          int(error->code), error->message);
141
1
    }
142

            
143
6
    return tracer.create_span(span_config);
144
6
  }
145

            
146
22
  return std::move(*maybe_span);
147
28
}
148

            
149
} // namespace Datadog
150
} // namespace Tracers
151
} // namespace Extensions
152
} // namespace Envoy