Line data Source code
1 : #include "source/extensions/tracers/opencensus/opencensus_tracer_impl.h"
2 :
3 : #include <grpcpp/grpcpp.h>
4 :
5 : #include "envoy/config/trace/v3/opencensus.pb.h"
6 : #include "envoy/http/header_map.h"
7 :
8 : #include "source/common/common/base64.h"
9 : #include "source/common/common/empty_string.h"
10 : #include "source/common/tracing/trace_context_impl.h"
11 :
12 : #include "absl/strings/str_cat.h"
13 : #include "google/devtools/cloudtrace/v2/tracing.grpc.pb.h"
14 : #include "opencensus/exporters/trace/ocagent/ocagent_exporter.h"
15 : #include "opencensus/exporters/trace/stackdriver/stackdriver_exporter.h"
16 : #include "opencensus/exporters/trace/stdout/stdout_exporter.h"
17 : #include "opencensus/exporters/trace/zipkin/zipkin_exporter.h"
18 : #include "opencensus/trace/propagation/b3.h"
19 : #include "opencensus/trace/propagation/cloud_trace_context.h"
20 : #include "opencensus/trace/propagation/grpc_trace_bin.h"
21 : #include "opencensus/trace/propagation/trace_context.h"
22 : #include "opencensus/trace/sampler.h"
23 : #include "opencensus/trace/span.h"
24 : #include "opencensus/trace/span_context.h"
25 : #include "opencensus/trace/trace_config.h"
26 : #include "opencensus/trace/trace_params.h"
27 :
28 : #ifdef ENVOY_GOOGLE_GRPC
29 : #include "source/common/grpc/google_grpc_utils.h"
30 : #endif
31 :
32 : namespace Envoy {
33 : namespace Extensions {
34 : namespace Tracers {
35 : namespace OpenCensus {
36 :
37 : #ifdef ENVOY_GOOGLE_GRPC
38 : constexpr char GoogleStackdriverTraceAddress[] = "cloudtrace.googleapis.com";
39 : #endif
40 :
41 : namespace {
42 :
43 : class ConstantValues {
44 : public:
45 : const Tracing::TraceContextHandler TRACEPARENT{"traceparent"};
46 : const Tracing::TraceContextHandler GRPC_TRACE_BIN{"grpc-trace-bin"};
47 : const Tracing::TraceContextHandler X_CLOUD_TRACE_CONTEXT{"x-cloud-trace-context"};
48 : const Tracing::TraceContextHandler X_B3_TRACEID{"x-b3-traceid"};
49 : const Tracing::TraceContextHandler X_B3_SPANID{"x-b3-spanid"};
50 : const Tracing::TraceContextHandler X_B3_SAMPLED{"x-b3-sampled"};
51 : const Tracing::TraceContextHandler X_B3_FLAGS{"x-b3-flags"};
52 : };
53 :
54 : using Constants = ConstSingleton<ConstantValues>;
55 :
56 : /**
57 : * OpenCensus tracing implementation of the Envoy Span object.
58 : */
59 : class Span : public Tracing::Span {
60 : public:
61 : Span(const Tracing::Config& config, const envoy::config::trace::v3::OpenCensusConfig& oc_config,
62 : Tracing::TraceContext& trace_context, const std::string& operation_name,
63 : SystemTime start_time, const Tracing::Decision tracing_decision);
64 :
65 : // Used by spawnChild().
66 : Span(const envoy::config::trace::v3::OpenCensusConfig& oc_config,
67 : ::opencensus::trace::Span&& span);
68 :
69 : void setOperation(absl::string_view operation) override;
70 : void setTag(absl::string_view name, absl::string_view value) override;
71 : void log(SystemTime timestamp, const std::string& event) override;
72 : void finishSpan() override;
73 : void injectContext(Tracing::TraceContext& trace_context,
74 : const Upstream::HostDescriptionConstSharedPtr&) override;
75 : Tracing::SpanPtr spawnChild(const Tracing::Config& config, const std::string& name,
76 : SystemTime start_time) override;
77 : void setSampled(bool sampled) override;
78 :
79 : // OpenCensus doesn't support baggage, so noop these OpenTracing functions.
80 0 : void setBaggage(absl::string_view, absl::string_view) override{};
81 0 : std::string getBaggage(absl::string_view) override { return EMPTY_STRING; };
82 :
83 : std::string getTraceIdAsHex() const override;
84 :
85 : private:
86 : ::opencensus::trace::Span span_;
87 : const envoy::config::trace::v3::OpenCensusConfig& oc_config_;
88 : };
89 :
90 : ::opencensus::trace::Span
91 : startSpanHelper(const std::string& name, bool traced, const Tracing::TraceContext& trace_context,
92 0 : const envoy::config::trace::v3::OpenCensusConfig& oc_config) {
93 : // Determine if there is a parent context.
94 0 : using OpenCensusConfig = envoy::config::trace::v3::OpenCensusConfig;
95 0 : ::opencensus::trace::SpanContext parent_ctx;
96 0 : for (const auto& incoming : oc_config.incoming_trace_context()) {
97 0 : bool found = false;
98 0 : switch (incoming) {
99 0 : case OpenCensusConfig::TRACE_CONTEXT: {
100 0 : const auto entry = Constants::get().TRACEPARENT.get(trace_context);
101 0 : if (entry.has_value()) {
102 0 : found = true;
103 : // This is an implicitly untrusted header, so only the first value is used.
104 0 : parent_ctx = ::opencensus::trace::propagation::FromTraceParentHeader(entry.value());
105 0 : }
106 0 : break;
107 0 : }
108 0 : case OpenCensusConfig::GRPC_TRACE_BIN: {
109 0 : const auto entry = Constants::get().GRPC_TRACE_BIN.get(trace_context);
110 0 : if (entry.has_value()) {
111 0 : found = true;
112 : // This is an implicitly untrusted header, so only the first value is used.
113 0 : parent_ctx = ::opencensus::trace::propagation::FromGrpcTraceBinHeader(
114 0 : Base64::decodeWithoutPadding(entry.value()));
115 0 : }
116 0 : break;
117 0 : }
118 0 : case OpenCensusConfig::CLOUD_TRACE_CONTEXT: {
119 0 : const auto entry = Constants::get().X_CLOUD_TRACE_CONTEXT.get(trace_context);
120 0 : if (entry.has_value()) {
121 0 : found = true;
122 : // This is an implicitly untrusted header, so only the first value is used.
123 0 : parent_ctx = ::opencensus::trace::propagation::FromCloudTraceContextHeader(entry.value());
124 0 : }
125 0 : break;
126 0 : }
127 :
128 0 : case OpenCensusConfig::B3: {
129 0 : absl::string_view b3_trace_id;
130 0 : absl::string_view b3_span_id;
131 0 : absl::string_view b3_sampled;
132 0 : absl::string_view b3_flags;
133 0 : const auto h_b3_trace_id = Constants::get().X_B3_TRACEID.get(trace_context);
134 0 : if (h_b3_trace_id.has_value()) {
135 0 : b3_trace_id = h_b3_trace_id.value();
136 0 : }
137 0 : const auto h_b3_span_id = Constants::get().X_B3_SPANID.get(trace_context);
138 0 : if (h_b3_span_id.has_value()) {
139 0 : b3_span_id = h_b3_span_id.value();
140 0 : }
141 0 : const auto h_b3_sampled = Constants::get().X_B3_SAMPLED.get(trace_context);
142 0 : if (h_b3_sampled.has_value()) {
143 0 : b3_sampled = h_b3_sampled.value();
144 0 : }
145 0 : const auto h_b3_flags = Constants::get().X_B3_FLAGS.get(trace_context);
146 0 : if (h_b3_flags.has_value()) {
147 0 : b3_flags = h_b3_flags.value();
148 0 : }
149 0 : if (h_b3_trace_id.has_value() && h_b3_span_id.has_value()) {
150 0 : found = true;
151 0 : parent_ctx = ::opencensus::trace::propagation::FromB3Headers(b3_trace_id, b3_span_id,
152 0 : b3_sampled, b3_flags);
153 0 : }
154 0 : break;
155 0 : }
156 0 : }
157 : // First header found wins.
158 0 : if (found) {
159 0 : break;
160 0 : }
161 0 : }
162 :
163 : // Honor Envoy's tracing decision.
164 0 : ::opencensus::trace::AlwaysSampler always_sampler;
165 0 : ::opencensus::trace::NeverSampler never_sampler;
166 : // This is safe because opts are not used after StartSpan.
167 0 : ::opencensus::trace::StartSpanOptions opts{&never_sampler};
168 0 : if (traced) {
169 0 : opts.sampler = &always_sampler;
170 0 : }
171 :
172 0 : if (parent_ctx.IsValid()) {
173 0 : return ::opencensus::trace::Span::StartSpanWithRemoteParent(name, parent_ctx, opts);
174 0 : }
175 0 : return ::opencensus::trace::Span::StartSpan(name, /*parent=*/nullptr, opts);
176 0 : }
177 :
178 : Span::Span(const Tracing::Config& config,
179 : const envoy::config::trace::v3::OpenCensusConfig& oc_config,
180 : Tracing::TraceContext& trace_context, const std::string& operation_name,
181 : SystemTime /*start_time*/, const Tracing::Decision tracing_decision)
182 : : span_(startSpanHelper(operation_name, tracing_decision.traced, trace_context, oc_config)),
183 0 : oc_config_(oc_config) {
184 0 : span_.AddAttribute("OperationName", config.operationName() == Tracing::OperationName::Ingress
185 0 : ? "Ingress"
186 0 : : "Egress");
187 0 : }
188 :
189 : Span::Span(const envoy::config::trace::v3::OpenCensusConfig& oc_config,
190 : ::opencensus::trace::Span&& span)
191 0 : : span_(std::move(span)), oc_config_(oc_config) {}
192 :
193 0 : void Span::setOperation(absl::string_view operation) { span_.SetName(operation); }
194 :
195 0 : void Span::setTag(absl::string_view name, absl::string_view value) {
196 0 : span_.AddAttribute(name, value);
197 0 : }
198 :
199 0 : void Span::log(SystemTime /*timestamp*/, const std::string& event) {
200 : // timestamp is ignored.
201 0 : span_.AddAnnotation(event);
202 0 : }
203 :
204 0 : void Span::finishSpan() { span_.End(); }
205 :
206 : void Span::injectContext(Tracing::TraceContext& trace_context,
207 0 : const Upstream::HostDescriptionConstSharedPtr&) {
208 0 : using OpenCensusConfig = envoy::config::trace::v3::OpenCensusConfig;
209 0 : const auto& ctx = span_.context();
210 0 : for (const auto& outgoing : oc_config_.outgoing_trace_context()) {
211 0 : switch (outgoing) {
212 0 : case OpenCensusConfig::TRACE_CONTEXT:
213 0 : Constants::get().TRACEPARENT.setRefKey(
214 0 : trace_context, ::opencensus::trace::propagation::ToTraceParentHeader(ctx));
215 0 : break;
216 0 : case OpenCensusConfig::GRPC_TRACE_BIN: {
217 0 : std::string val = ::opencensus::trace::propagation::ToGrpcTraceBinHeader(ctx);
218 0 : val = Base64::encode(val.data(), val.size(), /*add_padding=*/false);
219 0 : Constants::get().GRPC_TRACE_BIN.setRefKey(trace_context, val);
220 0 : break;
221 0 : }
222 0 : case OpenCensusConfig::CLOUD_TRACE_CONTEXT:
223 0 : Constants::get().X_CLOUD_TRACE_CONTEXT.setRefKey(
224 0 : trace_context, ::opencensus::trace::propagation::ToCloudTraceContextHeader(ctx));
225 0 : break;
226 0 : case OpenCensusConfig::B3:
227 0 : Constants::get().X_B3_TRACEID.setRefKey(
228 0 : trace_context, ::opencensus::trace::propagation::ToB3TraceIdHeader(ctx));
229 0 : Constants::get().X_B3_SPANID.setRefKey(
230 0 : trace_context, ::opencensus::trace::propagation::ToB3SpanIdHeader(ctx));
231 0 : Constants::get().X_B3_SAMPLED.setRefKey(
232 0 : trace_context, ::opencensus::trace::propagation::ToB3SampledHeader(ctx));
233 : // OpenCensus's trace context propagation doesn't produce the
234 : // "X-B3-Flags:" header.
235 0 : break;
236 0 : }
237 0 : }
238 0 : }
239 :
240 0 : std::string Span::getTraceIdAsHex() const {
241 0 : const auto& ctx = span_.context();
242 0 : return ctx.trace_id().ToHex();
243 0 : }
244 :
245 : Tracing::SpanPtr Span::spawnChild(const Tracing::Config& /*config*/, const std::string& name,
246 0 : SystemTime /*start_time*/) {
247 0 : return std::make_unique<Span>(oc_config_,
248 0 : ::opencensus::trace::Span::StartSpan(name, /*parent=*/&span_));
249 0 : }
250 :
251 0 : void Span::setSampled(bool sampled) { span_.AddAnnotation("setSampled", {{"sampled", sampled}}); }
252 :
253 : } // namespace
254 :
255 : Driver::Driver(const envoy::config::trace::v3::OpenCensusConfig& oc_config,
256 : const LocalInfo::LocalInfo& localinfo, Api::Api& api)
257 0 : : oc_config_(oc_config), local_info_(localinfo) {
258 : // To give user a chance to correct initially invalid configuration and try to apply it once again
259 : // without a need to restart Envoy, validation checks must be done prior to any side effects.
260 0 : if (oc_config.stackdriver_exporter_enabled() && oc_config.has_stackdriver_grpc_service() &&
261 0 : !oc_config.stackdriver_grpc_service().has_google_grpc()) {
262 0 : throw EnvoyException("Opencensus stackdriver tracer only support GoogleGrpc.");
263 0 : }
264 0 : if (oc_config.ocagent_exporter_enabled() && oc_config.has_ocagent_grpc_service() &&
265 0 : !oc_config.ocagent_grpc_service().has_google_grpc()) {
266 0 : throw EnvoyException("Opencensus ocagent tracer only supports GoogleGrpc.");
267 0 : }
268 : // Process-wide side effects.
269 0 : if (oc_config.has_trace_config()) {
270 0 : applyTraceConfig(oc_config.trace_config());
271 0 : }
272 0 : if (oc_config.stdout_exporter_enabled()) {
273 0 : ::opencensus::exporters::trace::StdoutExporter::Register();
274 0 : }
275 0 : if (oc_config.stackdriver_exporter_enabled()) {
276 0 : ::opencensus::exporters::trace::StackdriverOptions opts;
277 0 : opts.project_id = oc_config.stackdriver_project_id();
278 0 : if (!oc_config.stackdriver_address().empty()) {
279 0 : auto channel =
280 0 : grpc::CreateChannel(oc_config.stackdriver_address(), grpc::InsecureChannelCredentials());
281 0 : opts.trace_service_stub = ::google::devtools::cloudtrace::v2::TraceService::NewStub(channel);
282 0 : } else if (oc_config.has_stackdriver_grpc_service() &&
283 0 : oc_config.stackdriver_grpc_service().has_google_grpc()) {
284 0 : #ifdef ENVOY_GOOGLE_GRPC
285 0 : envoy::config::core::v3::GrpcService stackdriver_service =
286 0 : oc_config.stackdriver_grpc_service();
287 0 : if (stackdriver_service.google_grpc().target_uri().empty()) {
288 : // If stackdriver server address is not provided, the default production stackdriver
289 : // address will be used.
290 0 : stackdriver_service.mutable_google_grpc()->set_target_uri(GoogleStackdriverTraceAddress);
291 0 : }
292 0 : auto channel = Envoy::Grpc::GoogleGrpcUtils::createChannel(stackdriver_service, api);
293 : // TODO(bianpengyuan): add tests for trace_service_stub and initial_metadata options with mock
294 : // stubs.
295 0 : opts.trace_service_stub = ::google::devtools::cloudtrace::v2::TraceService::NewStub(channel);
296 0 : const auto& initial_metadata = stackdriver_service.initial_metadata();
297 0 : if (!initial_metadata.empty()) {
298 0 : opts.prepare_client_context = [initial_metadata](grpc::ClientContext* ctx) {
299 0 : for (const auto& metadata : initial_metadata) {
300 0 : ctx->AddMetadata(metadata.key(), metadata.value());
301 0 : }
302 0 : };
303 0 : }
304 : #else
305 : throw EnvoyException("Opencensus tracer: cannot handle stackdriver google grpc service, "
306 : "google grpc is not built in.");
307 : #endif
308 0 : }
309 0 : ::opencensus::exporters::trace::StackdriverExporter::Register(std::move(opts));
310 0 : }
311 0 : if (oc_config.zipkin_exporter_enabled()) {
312 0 : ::opencensus::exporters::trace::ZipkinExporterOptions opts(oc_config.zipkin_url());
313 0 : opts.service_name = local_info_.clusterName();
314 0 : ::opencensus::exporters::trace::ZipkinExporter::Register(opts);
315 0 : }
316 0 : if (oc_config.ocagent_exporter_enabled()) {
317 0 : ::opencensus::exporters::trace::OcAgentOptions opts;
318 0 : if (!oc_config.ocagent_address().empty()) {
319 0 : opts.address = oc_config.ocagent_address();
320 0 : } else if (oc_config.has_ocagent_grpc_service() &&
321 0 : oc_config.ocagent_grpc_service().has_google_grpc()) {
322 0 : #ifdef ENVOY_GOOGLE_GRPC
323 0 : const envoy::config::core::v3::GrpcService& ocagent_service =
324 0 : oc_config.ocagent_grpc_service();
325 0 : auto channel = Envoy::Grpc::GoogleGrpcUtils::createChannel(ocagent_service, api);
326 0 : opts.trace_service_stub =
327 0 : ::opencensus::proto::agent::trace::v1::TraceService::NewStub(channel);
328 : #else
329 : throw EnvoyException("Opencensus tracer: cannot handle ocagent google grpc service, google "
330 : "grpc is not built in.");
331 : #endif
332 0 : }
333 0 : opts.service_name = local_info_.clusterName();
334 0 : ::opencensus::exporters::trace::OcAgentExporter::Register(std::move(opts));
335 0 : }
336 0 : }
337 :
338 0 : void Driver::applyTraceConfig(const opencensus::proto::trace::v1::TraceConfig& config) {
339 0 : using SamplerCase = opencensus::proto::trace::v1::TraceConfig::SamplerCase;
340 0 : using opencensus::proto::trace::v1::ConstantSampler;
341 0 : constexpr double kDefaultSamplingProbability = 1e-4;
342 0 : double probability = kDefaultSamplingProbability;
343 :
344 0 : switch (config.sampler_case()) {
345 0 : case SamplerCase::kProbabilitySampler:
346 0 : probability = config.probability_sampler().samplingprobability();
347 0 : break;
348 0 : case SamplerCase::kConstantSampler:
349 0 : switch (config.constant_sampler().decision()) {
350 0 : case ConstantSampler::ALWAYS_OFF:
351 0 : probability = 0.;
352 0 : break;
353 0 : case ConstantSampler::ALWAYS_ON:
354 0 : case ConstantSampler::ALWAYS_PARENT:
355 0 : probability = 1.;
356 0 : break;
357 0 : default:
358 0 : break; /* Keep default probability. */
359 0 : }
360 0 : break;
361 0 : case SamplerCase::kRateLimitingSampler:
362 0 : ENVOY_LOG(error, "RateLimitingSampler is not supported.");
363 0 : break;
364 0 : case SamplerCase::SAMPLER_NOT_SET:
365 0 : break; // Keep default.
366 0 : default:
367 0 : ENVOY_LOG(error, "Unknown sampler type in TraceConfig.");
368 0 : }
369 :
370 0 : ::opencensus::trace::TraceConfig::SetCurrentTraceParams(::opencensus::trace::TraceParams{
371 0 : uint32_t(config.max_number_of_attributes()), uint32_t(config.max_number_of_annotations()),
372 0 : uint32_t(config.max_number_of_message_events()), uint32_t(config.max_number_of_links()),
373 0 : ::opencensus::trace::ProbabilitySampler(probability)});
374 0 : }
375 :
376 : Tracing::SpanPtr Driver::startSpan(const Tracing::Config& config,
377 : Tracing::TraceContext& trace_context,
378 : const StreamInfo::StreamInfo& stream_info,
379 : const std::string& operation_name,
380 0 : Tracing::Decision tracing_decision) {
381 0 : return std::make_unique<Span>(config, oc_config_, trace_context, operation_name,
382 0 : stream_info.startTime(), tracing_decision);
383 0 : }
384 :
385 : } // namespace OpenCensus
386 : } // namespace Tracers
387 : } // namespace Extensions
388 : } // namespace Envoy
|