Coverage Report

Created: 2024-09-19 09:45

/proc/self/cwd/source/extensions/tracers/opentelemetry/tracer.cc
Line
Count
Source (jump to first uncovered line)
1
#include "source/extensions/tracers/opentelemetry/tracer.h"
2
3
#include <cstdint>
4
#include <string>
5
6
#include "envoy/config/trace/v3/opentelemetry.pb.h"
7
8
#include "source/common/common/empty_string.h"
9
#include "source/common/common/hex.h"
10
#include "source/common/tracing/common_values.h"
11
#include "source/common/tracing/trace_context_impl.h"
12
#include "source/extensions/tracers/opentelemetry/otlp_utils.h"
13
14
#include "opentelemetry/proto/collector/trace/v1/trace_service.pb.h"
15
#include "opentelemetry/proto/trace/v1/trace.pb.h"
16
17
namespace Envoy {
18
namespace Extensions {
19
namespace Tracers {
20
namespace OpenTelemetry {
21
22
constexpr absl::string_view kDefaultVersion = "00";
23
24
using opentelemetry::proto::collector::trace::v1::ExportTraceServiceRequest;
25
26
namespace {
27
28
0
const Tracing::TraceContextHandler& traceParentHeader() {
29
0
  CONSTRUCT_ON_FIRST_USE(Tracing::TraceContextHandler, "traceparent");
30
0
}
31
32
0
const Tracing::TraceContextHandler& traceStateHeader() {
33
0
  CONSTRUCT_ON_FIRST_USE(Tracing::TraceContextHandler, "tracestate");
34
0
}
35
36
void callSampler(SamplerSharedPtr sampler, const absl::optional<SpanContext> span_context,
37
                 Span& new_span, const std::string& operation_name,
38
0
                 OptRef<const Tracing::TraceContext> trace_context) {
39
0
  if (!sampler) {
40
0
    return;
41
0
  }
42
0
  const auto sampling_result = sampler->shouldSample(
43
0
      span_context, new_span.getTraceId(), operation_name, new_span.spankind(), trace_context, {});
44
0
  new_span.setSampled(sampling_result.isSampled());
45
46
0
  if (sampling_result.attributes) {
47
0
    for (auto const& attribute : *sampling_result.attributes) {
48
0
      new_span.setAttribute(attribute.first, attribute.second);
49
0
    }
50
0
  }
51
0
  if (!sampling_result.tracestate.empty()) {
52
0
    new_span.setTracestate(sampling_result.tracestate);
53
0
  }
54
0
}
55
56
} // namespace
57
58
Span::Span(const std::string& name, SystemTime start_time, Envoy::TimeSource& time_source,
59
           Tracer& parent_tracer, OTelSpanKind span_kind)
60
0
    : parent_tracer_(parent_tracer), time_source_(time_source) {
61
0
  span_ = ::opentelemetry::proto::trace::v1::Span();
62
63
0
  span_.set_kind(span_kind);
64
65
0
  span_.set_name(name);
66
0
  span_.set_start_time_unix_nano(std::chrono::nanoseconds(start_time.time_since_epoch()).count());
67
0
}
68
69
Tracing::SpanPtr Span::spawnChild(const Tracing::Config&, const std::string& name,
70
0
                                  SystemTime start_time) {
71
  // Build span_context from the current span, then generate the child span from that context.
72
0
  SpanContext span_context(kDefaultVersion, getTraceId(), spanId(), sampled(), tracestate());
73
0
  return parent_tracer_.startSpan(name, start_time, span_context, {},
74
0
                                  ::opentelemetry::proto::trace::v1::Span::SPAN_KIND_CLIENT);
75
0
}
76
77
0
void Span::finishSpan() {
78
  // Call into the parent tracer so we can access the shared exporter.
79
0
  span_.set_end_time_unix_nano(
80
0
      std::chrono::nanoseconds(time_source_.systemTime().time_since_epoch()).count());
81
0
  if (sampled()) {
82
0
    parent_tracer_.sendSpan(span_);
83
0
  }
84
0
}
85
86
0
void Span::setOperation(absl::string_view operation) { span_.set_name(operation); };
87
88
0
void Span::injectContext(Tracing::TraceContext& trace_context, const Tracing::UpstreamContext&) {
89
0
  std::string trace_id_hex = absl::BytesToHexString(span_.trace_id());
90
0
  std::string span_id_hex = absl::BytesToHexString(span_.span_id());
91
0
  std::vector<uint8_t> trace_flags_vec{sampled()};
92
0
  std::string trace_flags_hex = Hex::encode(trace_flags_vec);
93
0
  std::string traceparent_header_value =
94
0
      absl::StrCat(kDefaultVersion, "-", trace_id_hex, "-", span_id_hex, "-", trace_flags_hex);
95
  // Set the traceparent in the trace_context.
96
0
  traceParentHeader().setRefKey(trace_context, traceparent_header_value);
97
  // Also set the tracestate.
98
0
  traceStateHeader().setRefKey(trace_context, span_.trace_state());
99
0
}
100
101
0
void Span::setAttribute(absl::string_view name, const OTelAttribute& attribute_value) {
102
  // The attribute key MUST be a non-null and non-empty string.
103
0
  if (name.empty()) {
104
0
    return;
105
0
  }
106
  // Attribute keys MUST be unique.
107
  // If a value already exists for this key, overwrite it.
108
0
  for (auto& key_value : *span_.mutable_attributes()) {
109
0
    if (key_value.key() == name) {
110
0
      OtlpUtils::populateAnyValue(*key_value.mutable_value(), attribute_value);
111
0
      return;
112
0
    }
113
0
  }
114
  // If we haven't found an existing match already, we can add a new key/value.
115
0
  opentelemetry::proto::common::v1::KeyValue key_value =
116
0
      opentelemetry::proto::common::v1::KeyValue();
117
0
  opentelemetry::proto::common::v1::AnyValue value_proto =
118
0
      opentelemetry::proto::common::v1::AnyValue();
119
0
  OtlpUtils::populateAnyValue(value_proto, attribute_value);
120
0
  key_value.set_key(std::string{name});
121
0
  *key_value.mutable_value() = value_proto;
122
0
  *span_.add_attributes() = key_value;
123
0
}
124
125
0
void Span::setTag(absl::string_view name, absl::string_view value) {
126
0
  if (name == Tracing::Tags::get().HttpStatusCode) {
127
0
    uint64_t status_code;
128
    // For HTTP status codes in the 5xx range, as well as any other code the client failed to
129
    // interpret, span status MUST be set to Error.
130
    //
131
    // For HTTP status codes in the 4xx range span status MUST be left unset in case of
132
    // SpanKind.SERVER and MUST be set to Error in case of SpanKind.CLIENT.
133
0
    if (absl::SimpleAtoi(value, &status_code)) {
134
0
      if (status_code >= 500 ||
135
0
          (status_code >= 400 &&
136
0
           span_.kind() == ::opentelemetry::proto::trace::v1::Span::SPAN_KIND_CLIENT)) {
137
0
        span_.mutable_status()->set_code(
138
0
            ::opentelemetry::proto::trace::v1::Status::STATUS_CODE_ERROR);
139
0
      }
140
0
    }
141
0
  }
142
0
  setAttribute(name, value);
143
0
}
144
145
Tracer::Tracer(OpenTelemetryTraceExporterPtr exporter, Envoy::TimeSource& time_source,
146
               Random::RandomGenerator& random, Runtime::Loader& runtime,
147
               Event::Dispatcher& dispatcher, OpenTelemetryTracerStats tracing_stats,
148
               const ResourceConstSharedPtr resource, SamplerSharedPtr sampler)
149
    : exporter_(std::move(exporter)), time_source_(time_source), random_(random), runtime_(runtime),
150
0
      tracing_stats_(tracing_stats), resource_(resource), sampler_(sampler) {
151
0
  flush_timer_ = dispatcher.createTimer([this]() -> void {
152
0
    tracing_stats_.timer_flushed_.inc();
153
0
    flushSpans();
154
0
    enableTimer();
155
0
  });
156
0
  enableTimer();
157
0
}
158
159
0
void Tracer::enableTimer() {
160
0
  const uint64_t flush_interval =
161
0
      runtime_.snapshot().getInteger("tracing.opentelemetry.flush_interval_ms", 5000U);
162
0
  flush_timer_->enableTimer(std::chrono::milliseconds(flush_interval));
163
0
}
164
165
0
void Tracer::flushSpans() {
166
0
  ExportTraceServiceRequest request;
167
  // A request consists of ResourceSpans.
168
0
  ::opentelemetry::proto::trace::v1::ResourceSpans* resource_span = request.add_resource_spans();
169
0
  resource_span->set_schema_url(resource_->schema_url_);
170
171
  // add resource attributes
172
0
  for (auto const& att : resource_->attributes_) {
173
0
    opentelemetry::proto::common::v1::KeyValue key_value =
174
0
        opentelemetry::proto::common::v1::KeyValue();
175
0
    opentelemetry::proto::common::v1::AnyValue value_proto =
176
0
        opentelemetry::proto::common::v1::AnyValue();
177
0
    value_proto.set_string_value(std::string{att.second});
178
0
    key_value.set_key(std::string{att.first});
179
0
    *key_value.mutable_value() = value_proto;
180
0
    (*resource_span->mutable_resource()->add_attributes()) = key_value;
181
0
  }
182
183
0
  ::opentelemetry::proto::trace::v1::ScopeSpans* scope_span = resource_span->add_scope_spans();
184
0
  for (const auto& pending_span : span_buffer_) {
185
0
    (*scope_span->add_spans()) = pending_span;
186
0
  }
187
0
  if (exporter_) {
188
0
    tracing_stats_.spans_sent_.add(span_buffer_.size());
189
0
    if (!exporter_->log(request)) {
190
      // TODO: should there be any sort of retry or reporting here?
191
0
      ENVOY_LOG(trace, "Unsuccessful log request to OpenTelemetry trace collector.");
192
0
    }
193
0
  } else {
194
0
    ENVOY_LOG(info, "Skipping log request to OpenTelemetry: no exporter configured");
195
0
  }
196
0
  span_buffer_.clear();
197
0
}
198
199
0
void Tracer::sendSpan(::opentelemetry::proto::trace::v1::Span& span) {
200
0
  span_buffer_.push_back(span);
201
0
  const uint64_t min_flush_spans =
202
0
      runtime_.snapshot().getInteger("tracing.opentelemetry.min_flush_spans", 5U);
203
0
  if (span_buffer_.size() >= min_flush_spans) {
204
0
    flushSpans();
205
0
  }
206
0
}
207
208
Tracing::SpanPtr Tracer::startSpan(const std::string& operation_name, SystemTime start_time,
209
                                   Tracing::Decision tracing_decision,
210
                                   OptRef<const Tracing::TraceContext> trace_context,
211
0
                                   OTelSpanKind span_kind) {
212
  // Create an Tracers::OpenTelemetry::Span class that will contain the OTel span.
213
0
  Span new_span(operation_name, start_time, time_source_, *this, span_kind);
214
0
  uint64_t trace_id_high = random_.random();
215
0
  uint64_t trace_id = random_.random();
216
0
  new_span.setTraceId(absl::StrCat(Hex::uint64ToHex(trace_id_high), Hex::uint64ToHex(trace_id)));
217
0
  uint64_t span_id = random_.random();
218
0
  new_span.setId(Hex::uint64ToHex(span_id));
219
0
  if (sampler_) {
220
0
    callSampler(sampler_, absl::nullopt, new_span, operation_name, trace_context);
221
0
  } else {
222
0
    new_span.setSampled(tracing_decision.traced);
223
0
  }
224
0
  return std::make_unique<Span>(new_span);
225
0
}
226
227
Tracing::SpanPtr Tracer::startSpan(const std::string& operation_name, SystemTime start_time,
228
                                   const SpanContext& previous_span_context,
229
                                   OptRef<const Tracing::TraceContext> trace_context,
230
0
                                   OTelSpanKind span_kind) {
231
  // Create a new span and populate details from the span context.
232
0
  Span new_span(operation_name, start_time, time_source_, *this, span_kind);
233
0
  new_span.setTraceId(previous_span_context.traceId());
234
0
  if (!previous_span_context.parentId().empty()) {
235
0
    new_span.setParentId(previous_span_context.parentId());
236
0
  }
237
  // Generate a new identifier for the span id.
238
0
  uint64_t span_id = random_.random();
239
0
  new_span.setId(Hex::uint64ToHex(span_id));
240
0
  if (sampler_) {
241
    // Sampler should make a sampling decision and set tracestate
242
0
    callSampler(sampler_, previous_span_context, new_span, operation_name, trace_context);
243
0
  } else {
244
    // Respect the previous span's sampled flag.
245
0
    new_span.setSampled(previous_span_context.sampled());
246
0
    if (!previous_span_context.tracestate().empty()) {
247
0
      new_span.setTracestate(std::string{previous_span_context.tracestate()});
248
0
    }
249
0
  }
250
0
  return std::make_unique<Span>(new_span);
251
0
}
252
253
} // namespace OpenTelemetry
254
} // namespace Tracers
255
} // namespace Extensions
256
} // namespace Envoy