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/tracing/http_tracer_impl.h"
12
#include "source/extensions/tracers/opentelemetry/grpc_trace_exporter.h"
13
#include "source/extensions/tracers/opentelemetry/http_trace_exporter.h"
14
#include "source/extensions/tracers/opentelemetry/resource_detectors/resource_detector.h"
15
#include "source/extensions/tracers/opentelemetry/resource_detectors/resource_provider.h"
16
#include "source/extensions/tracers/opentelemetry/samplers/sampler.h"
17
#include "source/extensions/tracers/opentelemetry/span_context.h"
18
#include "source/extensions/tracers/opentelemetry/span_context_extractor.h"
19
#include "source/extensions/tracers/opentelemetry/trace_exporter.h"
20
#include "source/extensions/tracers/opentelemetry/tracer.h"
21

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

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

            
30
namespace {
31

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

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

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

            
64
} // namespace
65

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

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

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

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

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

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

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

            
149
80
Driver::TlsTracer::TlsTracer(TracerPtr tracer) : tracer_(std::move(tracer)) {}
150

            
151
62
Tracer& Driver::TlsTracer::tracer() {
152
62
  ASSERT(tracer_);
153
62
  return *tracer_;
154
62
}
155

            
156
} // namespace OpenTelemetry
157
} // namespace Tracers
158
} // namespace Extensions
159
} // namespace Envoy