1
#include "source/extensions/tracers/datadog/span.h"
2

            
3
#include <utility>
4

            
5
#include "source/common/tracing/common_values.h"
6
#include "source/common/tracing/null_span_impl.h"
7
#include "source/extensions/tracers/datadog/time_util.h"
8

            
9
#include "absl/strings/str_cat.h"
10
#include "absl/strings/string_view.h"
11
#include "datadog/dict_writer.h"
12
#include "datadog/sampling_priority.h"
13
#include "datadog/span_config.h"
14
#include "datadog/trace_segment.h"
15

            
16
namespace Envoy {
17
namespace Extensions {
18
namespace Tracers {
19
namespace Datadog {
20
namespace {
21

            
22
class TraceContextWriter : public datadog::tracing::DictWriter {
23
public:
24
1
  explicit TraceContextWriter(Tracing::TraceContext& context) : context_(context) {}
25

            
26
5
  void set(datadog::tracing::StringView key, datadog::tracing::StringView value) override {
27
5
    context_.set(key, value);
28
5
  }
29

            
30
private:
31
  Tracing::TraceContext& context_;
32
};
33

            
34
} // namespace
35

            
36
Span::Span(datadog::tracing::Span&& span, bool use_local_decision)
37
50
    : span_(std::move(span)), use_local_decision_(use_local_decision) {}
38

            
39
30
const datadog::tracing::Optional<datadog::tracing::Span>& Span::impl() const { return span_; }
40

            
41
4
void Span::setOperation(absl::string_view operation) {
42
4
  if (!span_) {
43
1
    return;
44
1
  }
45

            
46
  // What Envoy calls the operation name more closely corresponds to what
47
  // Datadog calls the resource name.
48
3
  span_->set_resource_name(operation);
49
3
}
50

            
51
10
void Span::setTag(absl::string_view name, absl::string_view value) {
52
10
  if (!span_) {
53
1
    return;
54
1
  }
55

            
56
9
  const auto& Tags = Envoy::Tracing::Tags::get();
57

            
58
9
  if (name == "resource.name") {
59
    // The special "resource.name" tag is a holdover from when the Datadog
60
    // tracer was OpenTracing-based, and so there was no way to set the Datadog
61
    // resource name directly.
62
    // In Envoy, it's still the case that there's no way to set the Datadog
63
    // resource name directly; so, here if the tag name is "resource.name", we
64
    // actually set the resource name instead of setting a tag.
65
1
    span_->set_resource_name(value);
66
8
  } else if (name == Tags.Error) {
67
    // Envoy marks spans as containing errors by setting the "error" tag.
68
    // Here we translate into the dd-trace-cpp equivalent.
69
4
    if (value == Tags.True) {
70
2
      span_->set_error(true);
71
2
    }
72
4
  } else if (name == Tags.ErrorReason) {
73
    // Envoy conveys information about an error by setting the "error.reason"
74
    // tag.
75
    // Here we translate into the dd-trace-cpp equivalent.
76
1
    span_->set_error_message(value);
77
1
    span_->set_tag(name, value);
78
3
  } else {
79
3
    span_->set_tag(name, value);
80
3
  }
81
9
}
82

            
83
1
void Span::log(SystemTime, const std::string&) {
84
  // Datadog spans don't have in-bound "events" or "logs".
85
1
}
86

            
87
14
void Span::finishSpan() { span_.reset(); }
88

            
89
2
void Span::injectContext(Tracing::TraceContext& trace_context, const Tracing::UpstreamContext&) {
90
2
  if (!span_) {
91
1
    return;
92
1
  }
93

            
94
1
  TraceContextWriter writer{trace_context};
95
1
  span_->inject(writer);
96
1
}
97

            
98
Tracing::SpanPtr Span::spawnChild(const Tracing::Config&, const std::string& name,
99
7
                                  SystemTime start_time) {
100
7
  if (!span_) {
101
    // I don't expect this to happen. This means that `spawnChild` was called
102
    // after `finishSpan`.
103
1
    return std::make_unique<Tracing::NullSpan>();
104
1
  }
105

            
106
  // The OpenTracing implementation ignored the `Tracing::Config` argument,
107
  // so we will as well.
108
  // The `name` parameter to this function more closely matches Datadog's
109
  // concept of "resource name." Datadog's "span name," or "operation name,"
110
  // instead describes the category of operation being performed, which here
111
  // we hard-code.
112
6
  datadog::tracing::SpanConfig config;
113
6
  config.name = "envoy.proxy";
114
6
  config.resource = name;
115
6
  config.start = estimateTime(start_time);
116

            
117
6
  return std::make_unique<Span>(span_->create_child(config));
118
7
}
119

            
120
4
void Span::setSampled(bool sampled) {
121
4
  if (!span_) {
122
2
    return;
123
2
  }
124

            
125
2
  auto priority = static_cast<int>(sampled ? datadog::tracing::SamplingPriority::USER_KEEP
126
2
                                           : datadog::tracing::SamplingPriority::USER_DROP);
127
2
  span_->trace_segment().override_sampling_priority(priority);
128
2
}
129

            
130
3
std::string Span::getBaggage(absl::string_view) {
131
  // not implemented
132
3
  return EMPTY_STRING;
133
3
}
134

            
135
2
void Span::setBaggage(absl::string_view, absl::string_view) {
136
  // not implemented
137
2
}
138

            
139
2
std::string Span::getTraceId() const {
140
2
  if (!span_) {
141
1
    return EMPTY_STRING;
142
1
  }
143
1
  return absl::StrCat(absl::Hex(span_->id()));
144
2
}
145

            
146
2
std::string Span::getSpanId() const {
147
  // TODO(#34412): This method is not yet implemented for Datadog.
148
2
  return EMPTY_STRING;
149
2
}
150

            
151
} // namespace Datadog
152
} // namespace Tracers
153
} // namespace Extensions
154
} // namespace Envoy