Skip to content

Commit 6971362

Browse files
mabdinurdmehalazacharycmontoya
authored
testing(parametric): update start span parametric endpoint (#170)
Co-authored-by: Damien Mehala <[email protected]> Co-authored-by: Zach Montoya <[email protected]>
1 parent 0b36277 commit 6971362

File tree

3 files changed

+90
-32
lines changed

3 files changed

+90
-32
lines changed

test/system-tests/main.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,10 @@ int main(int argc, char* argv[]) {
108108
[&handler](const httplib::Request& req, httplib::Response& res) {
109109
handler.on_inject_headers(req, res);
110110
});
111+
svr.Post("/trace/span/extract_headers",
112+
[&handler](const httplib::Request& req, httplib::Response& res) {
113+
handler.on_extract_headers(req, res);
114+
});
111115
svr.Post("/trace/span/flush",
112116
[&handler](const httplib::Request& req, httplib::Response& res) {
113117
handler.on_span_flush(req, res);

test/system-tests/request_handler.cpp

Lines changed: 70 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -94,13 +94,6 @@ void RequestHandler::on_span_start(const httplib::Request& req,
9494
span_cfg.resource = *resource;
9595
}
9696

97-
if (auto origin =
98-
utils::get_if_exists<std::string_view>(request_json, "origin")) {
99-
logger_->log_info(
100-
"[start_span] origin, but this can only be set via the "
101-
"'x-datadog-origin' header");
102-
}
103-
10497
auto success = [](const datadog::tracing::Span& span,
10598
httplib::Response& res) {
10699
// clang-format off
@@ -113,37 +106,50 @@ void RequestHandler::on_span_start(const httplib::Request& req,
113106
res.set_content(response_body.dump(), "application/json");
114107
};
115108

116-
if (auto parent_id =
117-
utils::get_if_exists<uint64_t>(request_json, "parent_id")) {
118-
if (*parent_id != 0) {
119-
auto parent_span_it = active_spans_.find(*parent_id);
120-
if (parent_span_it == active_spans_.cend()) {
121-
const auto msg = "on_span_start: span not found for id " +
122-
std::to_string(*parent_id);
123-
VALIDATION_ERROR(res, msg);
124-
}
109+
auto parent_id = utils::get_if_exists<uint64_t>(request_json, "parent_id");
125110

126-
auto span = parent_span_it->second.create_child(span_cfg);
127-
success(span, res);
128-
active_spans_.emplace(span.id(), std::move(span));
129-
return;
130-
}
111+
// No `parent_id` field OR parent is `0` -> create a span.
112+
if (!parent_id || *parent_id == 0) {
113+
auto span = tracer_.create_span(span_cfg);
114+
success(span, res);
115+
active_spans_.emplace(span.id(), std::move(span));
116+
return;
117+
}
118+
119+
// If there's a parent ID -> Extract using the tracing context stored earlier
120+
// OR -> Create a child span from the span.
121+
auto parent_span_it = active_spans_.find(*parent_id);
122+
if (parent_span_it != active_spans_.cend()) {
123+
auto span = parent_span_it->second.create_child(span_cfg);
124+
success(span, res);
125+
active_spans_.emplace(span.id(), std::move(span));
126+
return;
131127
}
132128

133-
if (auto http_headers = utils::get_if_exists<nlohmann::json::array_t>(
134-
request_json, "http_headers")) {
135-
if (!http_headers->empty()) {
136-
auto span = tracer_.extract_or_create_span(
137-
utils::HeaderReader(*http_headers), span_cfg);
138-
success(span, res);
139-
active_spans_.emplace(span.id(), std::move(span));
129+
auto context_it = tracing_context_.find(*parent_id);
130+
if (context_it != tracing_context_.cend()) {
131+
auto span =
132+
tracer_.extract_span(utils::HeaderReader(context_it->second), span_cfg);
133+
if (!span) {
134+
const auto msg =
135+
"on_span_start: unable to create span from http_headers "
136+
"identified "
137+
"by parent_id " +
138+
std::to_string(*parent_id);
139+
VALIDATION_ERROR(res, msg);
140140
return;
141141
}
142+
success(*span, res);
143+
active_spans_.emplace(span->id(), std::move(*span));
144+
return;
142145
}
143146

144-
auto span = tracer_.create_span(span_cfg);
145-
success(span, res);
146-
active_spans_.emplace(span.id(), std::move(span));
147+
// Safeguard
148+
if (*parent_id != 0) {
149+
const auto msg = "on_span_start: span or http_headers not found for id " +
150+
std::to_string(*parent_id);
151+
VALIDATION_ERROR(res, msg);
152+
}
147153
}
148154

149155
void RequestHandler::on_span_end(const httplib::Request& req,
@@ -162,7 +168,6 @@ void RequestHandler::on_span_end(const httplib::Request& req,
162168
VALIDATION_ERROR(res, msg);
163169
}
164170

165-
active_spans_.erase(span_it);
166171
res.status = 200;
167172
}
168173

@@ -240,9 +245,42 @@ void RequestHandler::on_inject_headers(const httplib::Request& req,
240245
res.set_content(response_json.dump(), "application/json");
241246
}
242247

248+
void RequestHandler::on_extract_headers(const httplib::Request& req,
249+
httplib::Response& res) {
250+
const auto request_json = nlohmann::json::parse(req.body);
251+
auto http_headers = utils::get_if_exists<nlohmann::json::array_t>(
252+
request_json, "http_headers");
253+
if (!http_headers) {
254+
VALIDATION_ERROR(res, "on_extract_headers: missing `http_headers` field.");
255+
}
256+
257+
auto span = tracer_.extract_span(utils::HeaderReader(*http_headers));
258+
259+
if (span.if_error()) {
260+
const auto response_body_fail = nlohmann::json{
261+
{"span_id", nullptr},
262+
};
263+
res.set_content(response_body_fail.dump(), "application/json");
264+
return;
265+
}
266+
267+
const auto response_body = nlohmann::json{
268+
{"span_id", span->parent_id().value()},
269+
};
270+
271+
tracing_context_[*span->parent_id()] = std::move(*http_headers);
272+
273+
// The span below will not be finished and flushed.
274+
blackhole_.emplace_back(std::move(*span));
275+
276+
res.set_content(response_body.dump(), "application/json");
277+
}
278+
243279
void RequestHandler::on_span_flush(const httplib::Request& /* req */,
244280
httplib::Response& res) {
245281
scheduler_->flush_telemetry();
282+
active_spans_.clear();
283+
tracing_context_.clear();
246284
res.status = 200;
247285
}
248286

test/system-tests/request_handler.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
#include <datadog/tracer.h>
66
#include <datadog/tracer_config.h>
77

8+
#include <datadog/json.hpp>
9+
810
#include "developer_noise.h"
911
#include "httplib.h"
1012
#include "manual_scheduler.h"
@@ -25,6 +27,7 @@ class RequestHandler final {
2527
void on_set_meta(const httplib::Request& req, httplib::Response& res);
2628
void on_set_metric(const httplib::Request& /* req */, httplib::Response& res);
2729
void on_inject_headers(const httplib::Request& req, httplib::Response& res);
30+
void on_extract_headers(const httplib::Request& req, httplib::Response& res);
2831
void on_span_flush(const httplib::Request& /* req */, httplib::Response& res);
2932
void on_stats_flush(const httplib::Request& /* req */,
3033
httplib::Response& res);
@@ -35,6 +38,19 @@ class RequestHandler final {
3538
std::shared_ptr<ManualScheduler> scheduler_;
3639
std::shared_ptr<DeveloperNoiseLogger> logger_;
3740
std::unordered_map<uint64_t, datadog::tracing::Span> active_spans_;
41+
std::unordered_map<uint64_t, nlohmann::json::array_t> tracing_context_;
42+
43+
// Previously, `/trace/span/start` was used to create new spans or create
44+
// child spans from the extracted tracing context.
45+
//
46+
// The logic has been split into two distinct endpoint, with the addition of
47+
// `extract_headers`. However, the public API does not expose a method to just
48+
// extract tracing context.
49+
//
50+
// For now, the workaround is to extract and create a span from tracing
51+
// context and keep the span alive until the process terminate, thus
52+
// explaining the name :)
53+
std::vector<datadog::tracing::Span> blackhole_;
3854

3955
#undef VALIDATION_ERROR
4056
};

0 commit comments

Comments
 (0)