1
#pragma once
2

            
3
#include <string>
4
#include <utility>
5
#include <vector>
6

            
7
#include "envoy/common/time.h"
8
#include "envoy/tracing/tracer.h"
9

            
10
#include "source/common/common/empty_string.h"
11
#include "source/common/common/hex.h"
12
#include "source/common/common/random_generator.h"
13
#include "source/common/http/codes.h"
14
#include "source/common/protobuf/utility.h"
15
#include "source/common/tracing/common_values.h"
16
#include "source/common/tracing/trace_context_impl.h"
17
#include "source/extensions/tracers/xray/daemon_broker.h"
18
#include "source/extensions/tracers/xray/sampling_strategy.h"
19
#include "source/extensions/tracers/xray/xray_configuration.h"
20

            
21
#include "absl/container/flat_hash_map.h"
22
#include "absl/strings/string_view.h"
23

            
24
namespace Envoy {
25
namespace Extensions {
26
namespace Tracers {
27
namespace XRay {
28

            
29
constexpr absl::string_view SpanClientIp = "client_ip";
30
constexpr absl::string_view SpanXForwardedFor = "x_forwarded_for";
31
constexpr absl::string_view Subsegment = "subsegment";
32

            
33
const Tracing::TraceContextHandler& xRayTraceHeader();
34
const Tracing::TraceContextHandler& xForwardedForHeader();
35

            
36
class Span : public Tracing::Span, Logger::Loggable<Logger::Id::config> {
37
public:
38
  /**
39
   * Creates a new Span.
40
   *
41
   * @param time_source A time source to get the span end time
42
   * @param random random generator for generating unique child span ids
43
   * @param broker Facilitates communication with the X-Ray daemon.
44
   */
45
  Span(TimeSource& time_source, Random::RandomGenerator& random, DaemonBroker& broker)
46
28
      : time_source_(time_source), random_(random), broker_(broker),
47
28
        id_(Hex::uint64ToHex(random_.random())) {}
48

            
49
  /**
50
   * Sets the Span's trace ID.
51
   */
52
28
  void setTraceId(absl::string_view trace_id) { trace_id_ = std::string(trace_id); };
53

            
54
  /**
55
   * Gets the Span's trace ID.
56
   */
57
17
  const std::string& traceId() const { return trace_id_; }
58

            
59
  /**
60
   * Completes the current span, serialize it and send it to the X-Ray daemon.
61
   */
62
  void finishSpan() override;
63

            
64
  /**
65
   * Sets the current operation name on the Span.
66
   * This information will be included in the X-Ray span's metadata.
67
   */
68
19
  void setOperation(absl::string_view operation) override {
69
19
    operation_name_ = std::string(operation);
70
19
  }
71

            
72
  /**
73
   * Sets the current direction on the Span.
74
   * This information will be included in the X-Ray span's annotation.
75
   */
76
19
  void setDirection(absl::string_view direction) { direction_ = std::string(direction); }
77

            
78
  /**
79
   * Sets the name of the Span.
80
   */
81
19
  void setName(absl::string_view name) { name_ = std::string(name); }
82

            
83
  /**
84
   * Sets the origin of the Span.
85
   */
86
18
  void setOrigin(absl::string_view origin) { origin_ = std::string(origin); }
87

            
88
  /**
89
   * Gets the origin of the Span.
90
   */
91
9
  const std::string& origin() { return origin_; }
92

            
93
  /**
94
   * Adds a key-value pair to either the Span's annotations or metadata.
95
   * An allowlist of keys are added to the annotations, everything else is added to the metadata.
96
   */
97
  void setTag(absl::string_view name, absl::string_view value) override;
98

            
99
  /**
100
   * Sets the ID of the parent segment. This is different from the Trace ID.
101
   * The parent ID is used if the request originated from an instrumented application.
102
   * For more information see:
103
   * https://docs.aws.amazon.com/xray/latest/devguide/xray-concepts.html#xray-concepts-tracingheader
104
   */
105
9
  void setParentId(absl::string_view parent_segment_id) {
106
9
    parent_segment_id_ = std::string(parent_segment_id);
107
9
  }
108

            
109
  /**
110
   * Sets the type of the Span. In X-Ray, an independent subsegment has a type of "subsegment".
111
   * https://docs.aws.amazon.com/xray/latest/devguide/xray-api-segmentdocuments.html#api-segmentdocuments-subsegments
112
   */
113
1
  void setType(absl::string_view type) { type_ = std::string(type); }
114

            
115
  /**
116
   * Sets the aws metadata field of the Span.
117
   */
118
18
  void setAwsMetadata(const absl::flat_hash_map<std::string, Protobuf::Value>& aws_metadata) {
119
18
    aws_metadata_ = aws_metadata;
120
18
  }
121

            
122
  /*
123
   * Adds to the http request annotation field of the Span.
124
   */
125
27
  void addToHttpRequestAnnotations(absl::string_view key, const Protobuf::Value& value) {
126
27
    http_request_annotations_.emplace(std::string(key), value);
127
27
  }
128

            
129
  /*
130
   * Check if key is set in http request annotation field of a Span.
131
   */
132
6
  bool hasKeyInHttpRequestAnnotations(absl::string_view key) {
133
6
    return http_request_annotations_.find(key) != http_request_annotations_.end();
134
6
  }
135

            
136
  /*
137
   * Adds to the http response annotation field of the Span.
138
   */
139
8
  void addToHttpResponseAnnotations(absl::string_view key, const Protobuf::Value& value) {
140
8
    http_response_annotations_.emplace(std::string(key), value);
141
8
  }
142

            
143
  /**
144
   * Sets the recording start time of the traced operation/request.
145
   */
146
19
  void setStartTime(Envoy::SystemTime start_time) { start_time_ = start_time; }
147

            
148
  /**
149
   * Marks the span as either "sampled" or "not-sampled".
150
   * By default, Spans are "sampled".
151
   * This is handy in cases where the sampling decision has already been determined either by Envoy
152
   * or by a downstream service.
153
   */
154
11
  void setSampled(bool sampled) override { sampled_ = sampled; };
155

            
156
  // X-Ray tracer does not use the sampling decision from Envoy anyway.
157
  bool useLocalDecision() const override { return false; }
158

            
159
  /**
160
   * Sets the server error as true for the traced operation/request.
161
   */
162
1
  void setServerError() { server_error_ = true; };
163

            
164
  /**
165
   * Sets the http response status code for the traced operation/request.
166
   */
167
6
  void setResponseStatusCode(uint64_t status_code) { response_status_code_ = status_code; };
168

            
169
  /**
170
   * Adds X-Ray trace header to the set of outgoing headers.
171
   */
172
  void injectContext(Tracing::TraceContext& trace_context,
173
                     const Tracing::UpstreamContext&) override;
174

            
175
  /**
176
   * Gets the start time of this Span.
177
   */
178
9
  Envoy::SystemTime startTime() const { return start_time_; }
179

            
180
  /**
181
   * Gets this Span's ID.
182
   */
183
12
  const std::string& id() const { return id_; }
184

            
185
  /**
186
   * Gets this Span's parent ID.
187
   */
188
12
  const std::string& parentId() const { return parent_segment_id_; }
189

            
190
  /**
191
   * Gets this Span's direction.
192
   */
193
9
  const std::string& direction() const { return direction_; }
194

            
195
  /**
196
   * Gets this Span's type.
197
   */
198
10
  const std::string& type() const { return type_; }
199

            
200
  /**
201
   * Gets this Span's name.
202
   */
203
9
  const std::string& name() const { return name_; }
204

            
205
  /**
206
   * Determines whether this span is sampled.
207
   */
208
20
  bool sampled() const { return sampled_; }
209

            
210
  /**
211
   * Determines if a server error occurred (response status code was 5XX Server Error).
212
   */
213
9
  bool serverError() const { return server_error_; }
214

            
215
  /**
216
   * Determines if a client error occurred (response status code was 4XX Client Error).
217
   */
218
9
  bool clientError() const { return Http::CodeUtility::is4xx(response_status_code_); }
219

            
220
  /**
221
   * Determines if a request was throttled (response status code was 429 Too Many Requests).
222
   */
223
9
  bool isThrottled() const {
224
9
    return Http::Code::TooManyRequests == static_cast<Http::Code>(response_status_code_);
225
9
  }
226

            
227
  /**
228
   * Not used by X-Ray because the Spans are "logged" (serialized) to the X-Ray daemon.
229
   */
230
1
  void log(Envoy::SystemTime, const std::string&) override {}
231

            
232
  // X-Ray doesn't support baggage, so noop these OpenTracing functions.
233
1
  void setBaggage(absl::string_view, absl::string_view) override {}
234
1
  std::string getBaggage(absl::string_view) override { return EMPTY_STRING; }
235

            
236
1
  std::string getTraceId() const override { return trace_id_; };
237

            
238
  // TODO(#34412): This method is unimplemented for X-Ray.
239
1
  std::string getSpanId() const override { return EMPTY_STRING; };
240

            
241
  /**
242
   * Creates a child span.
243
   * In X-Ray terms this creates a sub-segment and sets its parent ID to the current span's ID.
244
   * @param operation_name The span of the child span.
245
   * @param start_time The time at which this child span started.
246
   */
247
  Tracing::SpanPtr spawnChild(const Tracing::Config&, const std::string& operation_name,
248
                              Envoy::SystemTime start_time) override;
249

            
250
private:
251
  Envoy::TimeSource& time_source_;
252
  Random::RandomGenerator& random_;
253
  DaemonBroker& broker_;
254
  Envoy::SystemTime start_time_;
255
  std::string operation_name_;
256
  std::string direction_;
257
  std::string id_;
258
  std::string trace_id_;
259
  std::string parent_segment_id_;
260
  std::string name_;
261
  std::string origin_;
262
  std::string type_;
263
  absl::flat_hash_map<std::string, Protobuf::Value> aws_metadata_;
264
  absl::flat_hash_map<std::string, Protobuf::Value> http_request_annotations_;
265
  absl::flat_hash_map<std::string, Protobuf::Value> http_response_annotations_;
266
  absl::flat_hash_map<std::string, std::string> custom_annotations_;
267
  bool server_error_{false};
268
  uint64_t response_status_code_{0};
269
  bool sampled_{true};
270
};
271

            
272
using SpanPtr = std::unique_ptr<Span>;
273

            
274
class Tracer {
275
public:
276
  Tracer(absl::string_view segment_name, absl::string_view origin,
277
         const absl::flat_hash_map<std::string, Protobuf::Value>& aws_metadata,
278
         DaemonBrokerPtr daemon_broker, TimeSource& time_source, Random::RandomGenerator& random)
279
30
      : segment_name_(segment_name), origin_(origin), aws_metadata_(aws_metadata),
280
30
        daemon_broker_(std::move(daemon_broker)), time_source_(time_source), random_(random) {}
281
  /**
282
   * Starts a tracing span for X-Ray
283
   */
284
  Tracing::SpanPtr startSpan(const Tracing::Config&, const std::string& operation_name,
285
                             Envoy::SystemTime start_time,
286
                             const absl::optional<XRayHeader>& xray_header,
287
                             const absl::optional<absl::string_view> client_ip);
288
  /**
289
   * Creates a Span that is marked as not-sampled.
290
   * This is useful when the sampling decision is done in Envoy's X-Ray and we want to avoid
291
   * overruling that decision in the upstream service in case that service itself uses X-Ray for
292
   * tracing. Also at the same time if X-Ray header is set then preserve its value.
293
   */
294
  XRay::SpanPtr createNonSampledSpan(const absl::optional<XRayHeader>& xray_header) const;
295

            
296
private:
297
  const std::string segment_name_;
298
  const std::string origin_;
299
  const absl::flat_hash_map<std::string, Protobuf::Value> aws_metadata_;
300
  const DaemonBrokerPtr daemon_broker_;
301
  Envoy::TimeSource& time_source_;
302
  Random::RandomGenerator& random_;
303
};
304

            
305
using TracerPtr = std::unique_ptr<Tracer>;
306

            
307
} // namespace XRay
308
} // namespace Tracers
309
} // namespace Extensions
310
} // namespace Envoy