1
#include "source/extensions/tracers/opentelemetry/opentelemetry_tracer_impl.h"
2

            
3
#include <string>
4

            
5
#include "envoy/common/optref.h"
6
#include "envoy/config/trace/v3/opentelemetry.pb.h"
7

            
8
#include "source/common/common/empty_string.h"
9
#include "source/common/common/logger.h"
10
#include "source/common/config/utility.h"
11
#include "source/common/http/http_service_headers.h"
12
#include "source/common/tracing/http_tracer_impl.h"
13
#include "source/extensions/tracers/opentelemetry/grpc_trace_exporter.h"
14
#include "source/extensions/tracers/opentelemetry/http_trace_exporter.h"
15
#include "source/extensions/tracers/opentelemetry/resource_detectors/resource_detector.h"
16
#include "source/extensions/tracers/opentelemetry/resource_detectors/resource_provider.h"
17
#include "source/extensions/tracers/opentelemetry/samplers/sampler.h"
18
#include "source/extensions/tracers/opentelemetry/span_context.h"
19
#include "source/extensions/tracers/opentelemetry/span_context_extractor.h"
20
#include "source/extensions/tracers/opentelemetry/trace_exporter.h"
21
#include "source/extensions/tracers/opentelemetry/tracer.h"
22

            
23
#include "opentelemetry/proto/collector/trace/v1/trace_service.pb.h"
24
#include "opentelemetry/proto/trace/v1/trace.pb.h"
25

            
26
namespace Envoy {
27
namespace Extensions {
28
namespace Tracers {
29
namespace OpenTelemetry {
30

            
31
namespace {
32

            
33
// Default max cache size for OpenTelemetry tracer
34
static constexpr uint64_t DEFAULT_MAX_CACHE_SIZE = 1024;
35

            
36
SamplerSharedPtr
37
tryCreateSamper(const envoy::config::trace::v3::OpenTelemetryConfig& opentelemetry_config,
38
56
                Server::Configuration::TracerFactoryContext& context) {
39
56
  SamplerSharedPtr sampler;
40
56
  if (opentelemetry_config.has_sampler()) {
41
21
    auto& sampler_config = opentelemetry_config.sampler();
42
21
    auto* factory = Envoy::Config::Utility::getFactory<SamplerFactory>(sampler_config);
43
21
    if (!factory) {
44
1
      throw EnvoyException(fmt::format("Sampler factory not found: '{}'", sampler_config.name()));
45
1
    }
46
20
    sampler = factory->createSampler(sampler_config.typed_config(), context);
47
20
  }
48
55
  return sampler;
49
56
}
50

            
51
62
OTelSpanKind getSpanKind(const Tracing::Config& config) {
52
  // If this is downstream span that be created by 'startSpan' for downstream request, then
53
  // set the span type based on the spawnUpstreamSpan flag and traffic direction:
54
  // * If separate tracing span will be created for upstream request, then set span type to
55
  //   SERVER because the downstream span should be server span in trace chain.
56
  // * If separate tracing span will not be created for upstream request, that means the
57
  //   Envoy will not be treated as independent hop in trace chain and then set span type
58
  //   based on the traffic direction.
59
62
  return (config.spawnUpstreamSpan() ? ::opentelemetry::proto::trace::v1::Span::SPAN_KIND_SERVER
60
62
          : config.operationName() == Tracing::OperationName::Egress
61
47
              ? ::opentelemetry::proto::trace::v1::Span::SPAN_KIND_CLIENT
62
47
              : ::opentelemetry::proto::trace::v1::Span::SPAN_KIND_SERVER);
63
62
}
64

            
65
} // namespace
66

            
67
Driver::Driver(const envoy::config::trace::v3::OpenTelemetryConfig& opentelemetry_config,
68
               Server::Configuration::TracerFactoryContext& context)
69
35
    : Driver(opentelemetry_config, context, ResourceProviderImpl{}) {}
70

            
71
Driver::Driver(const envoy::config::trace::v3::OpenTelemetryConfig& opentelemetry_config,
72
               Server::Configuration::TracerFactoryContext& context,
73
               const ResourceProvider& resource_provider)
74
57
    : tls_slot_ptr_(context.serverFactoryContext().threadLocal().allocateSlot()),
75
57
      tracing_stats_{OPENTELEMETRY_TRACER_STATS(
76
57
          POOL_COUNTER_PREFIX(context.serverFactoryContext().scope(), "tracing.opentelemetry"))} {
77
57
  auto& factory_context = context.serverFactoryContext();
78

            
79
57
  Resource resource = resource_provider.getResource(
80
57
      opentelemetry_config.resource_detectors(), context.serverFactoryContext(),
81
57
      opentelemetry_config.service_name().empty() ? kDefaultServiceName
82
57
                                                  : opentelemetry_config.service_name());
83
57
  ResourceConstSharedPtr resource_ptr = std::make_shared<Resource>(std::move(resource));
84

            
85
57
  if (opentelemetry_config.has_grpc_service() && opentelemetry_config.has_http_service()) {
86
1
    throw EnvoyException(
87
1
        "OpenTelemetry Tracer cannot have both gRPC and HTTP exporters configured. "
88
1
        "OpenTelemetry tracer will be disabled.");
89
1
  }
90

            
91
  // Create the sampler if configured
92
56
  SamplerSharedPtr sampler = tryCreateSamper(opentelemetry_config, context);
93

            
94
  // Create the headers applicator on the main thread if HTTP export is configured.
95
56
  std::shared_ptr<const Http::HttpServiceHeadersApplicator> headers_applicator;
96
56
  if (opentelemetry_config.has_http_service()) {
97
6
    absl::Status creation_status = absl::OkStatus();
98
6
    headers_applicator = std::make_shared<Http::HttpServiceHeadersApplicator>(
99
6
        opentelemetry_config.http_service(), factory_context, creation_status);
100
6
    THROW_IF_NOT_OK_REF(creation_status);
101
6
  }
102

            
103
  // Create the tracer in Thread Local Storage.
104
56
  tls_slot_ptr_->set([opentelemetry_config, &factory_context, this, resource_ptr, sampler,
105
81
                      headers_applicator](Event::Dispatcher& dispatcher) {
106
80
    OpenTelemetryTraceExporterPtr exporter;
107
80
    if (opentelemetry_config.has_grpc_service()) {
108
71
      auto factory_or_error =
109
71
          factory_context.clusterManager().grpcAsyncClientManager().factoryForGrpcService(
110
71
              opentelemetry_config.grpc_service(), factory_context.scope(), true);
111
71
      THROW_IF_NOT_OK_REF(factory_or_error.status());
112
71
      Grpc::AsyncClientFactoryPtr&& factory = std::move(factory_or_error.value());
113
71
      const Grpc::RawAsyncClientSharedPtr& async_client_shared_ptr =
114
71
          THROW_OR_RETURN_VALUE(factory->createUncachedRawAsyncClient(), Grpc::RawAsyncClientPtr);
115
71
      exporter = std::make_unique<OpenTelemetryGrpcTraceExporter>(async_client_shared_ptr);
116
76
    } else if (opentelemetry_config.has_http_service()) {
117
7
      ASSERT(headers_applicator != nullptr);
118
7
      exporter = std::make_unique<OpenTelemetryHttpTraceExporter>(
119
7
          factory_context.clusterManager(), opentelemetry_config.http_service(),
120
7
          headers_applicator);
121
7
    }
122
    // Get the max cache size from config
123
80
    uint64_t max_cache_size = PROTOBUF_GET_WRAPPED_OR_DEFAULT(opentelemetry_config, max_cache_size,
124
80
                                                              DEFAULT_MAX_CACHE_SIZE);
125
80
    TracerPtr tracer =
126
80
        std::make_unique<Tracer>(std::move(exporter), factory_context.timeSource(),
127
80
                                 factory_context.api().randomGenerator(), factory_context.runtime(),
128
80
                                 dispatcher, tracing_stats_, resource_ptr, sampler, max_cache_size);
129
80
    return std::make_shared<TlsTracer>(std::move(tracer));
130
80
  });
131
56
}
132

            
133
Tracing::SpanPtr Driver::startSpan(const Tracing::Config& config,
134
                                   Tracing::TraceContext& trace_context,
135
                                   const StreamInfo::StreamInfo& stream_info,
136
                                   const std::string& operation_name,
137
62
                                   Tracing::Decision tracing_decision) {
138
  // Get tracer from TLS and start span.
139
62
  auto& tracer = tls_slot_ptr_->getTyped<Driver::TlsTracer>().tracer();
140
62
  SpanContextExtractor extractor(trace_context);
141
62
  const auto span_kind = getSpanKind(config);
142
62
  if (!extractor.propagationHeaderPresent()) {
143
    // No propagation header, so we can create a fresh span with the given decision.
144
48
    Tracing::SpanPtr new_open_telemetry_span =
145
48
        tracer.startSpan(operation_name, stream_info, stream_info.startTime(), tracing_decision,
146
48
                         trace_context, span_kind);
147
48
    return new_open_telemetry_span;
148
54
  } else {
149
    // Try to extract the span context. If we can't, just return a null span.
150
14
    absl::StatusOr<SpanContext> span_context = extractor.extractSpanContext();
151
14
    if (span_context.ok()) {
152
13
      return tracer.startSpan(operation_name, stream_info, stream_info.startTime(),
153
13
                              span_context.value(), trace_context, span_kind);
154
13
    } else {
155
1
      ENVOY_LOG(trace, "Unable to extract span context: ", span_context.status());
156
1
      return std::make_unique<Tracing::NullSpan>();
157
1
    }
158
14
  }
159
62
}
160

            
161
80
Driver::TlsTracer::TlsTracer(TracerPtr tracer) : tracer_(std::move(tracer)) {}
162

            
163
62
Tracer& Driver::TlsTracer::tracer() {
164
62
  ASSERT(tracer_);
165
62
  return *tracer_;
166
62
}
167

            
168
} // namespace OpenTelemetry
169
} // namespace Tracers
170
} // namespace Extensions
171
} // namespace Envoy