/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 |