Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for trace analytics #92

Merged
merged 2 commits into from
May 1, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions include/datadog/tags.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#ifndef DD_INCLUDE_TAGS_H
#define DD_INCLUDE_TAGS_H

namespace datadog {
namespace tags {

const std::string environment = "env";
const std::string service_name = "service.name";
const std::string span_type = "span.type";
const std::string operation_name = "operation";
const std::string resource_name = "resource.name";
const std::string analytics_event = "analytics.event";

} // namespace tags
} // namespace datadog

#endif // DD_INCLUDE_TAGS_H
44 changes: 32 additions & 12 deletions src/span.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#include "span.h"
#include <datadog/tags.h>
#include <opentracing/ext/tags.h>
#include <iostream>
#include <nlohmann/json.hpp>
Expand All @@ -8,21 +9,16 @@
#include "span_buffer.h"
#include "tracer.h"

namespace ot = opentracing;
namespace tags = datadog::tags;
using json = nlohmann::json;

namespace datadog {
namespace opentracing {

namespace {
const std::string datadog_span_type_tag = "span.type";
const std::string datadog_resource_name_tag = "resource.name";
const std::string datadog_service_name_tag = "service.name";
const std::string operation_name_tag = "operation";
const std::string event_sample_rate_metric = "_dd1.sr.eausr";
} // namespace

const std::string environment_tag = "env";

SpanData::SpanData(std::string type, std::string service, ot::string_view resource,
std::string name, uint64_t trace_id, uint64_t span_id, uint64_t parent_id,
int64_t start, int64_t duration, int32_t error)
Expand All @@ -43,7 +39,7 @@ uint64_t SpanData::traceId() const { return trace_id; }
uint64_t SpanData::spanId() const { return span_id; }

const std::string SpanData::env() const {
const auto &env = meta.find(environment_tag);
const auto &env = meta.find(tags::environment);
if (env == meta.end()) {
return "";
}
Expand Down Expand Up @@ -123,22 +119,22 @@ void Span::FinishWithOptions(
std::chrono::duration_cast<std::chrono::nanoseconds>(end_time - start_time_).count();
// Override operation name if needed.
if (operation_name_override_ != "") {
span_->meta[operation_name_tag] = span_->name;
span_->meta[tags::operation_name] = span_->name;
span_->name = operation_name_override_;
}
// Apply special tags.
// If we add any more cases; then abstract this. For now, KISS.
auto tag = span_->meta.find(datadog_span_type_tag);
auto tag = span_->meta.find(tags::span_type);
if (tag != span_->meta.end()) {
span_->type = tag->second;
span_->meta.erase(tag);
}
tag = span_->meta.find(datadog_resource_name_tag);
tag = span_->meta.find(tags::resource_name);
if (tag != span_->meta.end()) {
span_->resource = tag->second;
span_->meta.erase(tag);
}
tag = span_->meta.find(datadog_service_name_tag);
tag = span_->meta.find(tags::service_name);
if (tag != span_->meta.end()) {
span_->service = tag->second;
span_->meta.erase(tag);
Expand All @@ -154,6 +150,30 @@ void Span::FinishWithOptions(
}
// Don't erase the tag, in case it is populated with interesting information.
}
tag = span_->meta.find(tags::analytics_event);
if (tag != span_->meta.end()) {
// tag->second is the JSON-serialized value of the variadic type given to SetTag.
// Apply boolean, valid integer and valid double's.
if (tag->second == "true" || tag->second == "1") {
palazzem marked this conversation as resolved.
Show resolved Hide resolved
span_->metrics[event_sample_rate_metric] = 1.0;
} else if (tag->second == "false" || tag->second == "0" || tag->second == "") {
span_->metrics[event_sample_rate_metric] = 0.0;
} else {
// Check if the value is a double between 0.0 and 1.0 (inclusive).
try {
double value = std::stod(tag->second);
if (value >= 0.0 && value <= 1.0) {
span_->metrics[event_sample_rate_metric] = value;
}
} catch (const std::invalid_argument &ia) {
// Ignore invalid value.
} catch (const std::out_of_range &oor) {
// Ignore values not in range.
}
}

span_->meta.erase(tag);
}
// Audit and finish span.
audit(span_.get());
buffer_->finishSpan(std::move(span_), sampler_);
Expand Down
4 changes: 1 addition & 3 deletions src/span.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@ namespace ot = opentracing;
namespace datadog {
namespace opentracing {

extern const std::string environment_tag;

class Tracer;
class SampleProvider;
class SpanBuffer;
Expand Down Expand Up @@ -50,7 +48,7 @@ struct SpanData {
int64_t duration;
int32_t error;
std::unordered_map<std::string, std::string> meta; // Aka, tags.
std::unordered_map<std::string, int> metrics;
std::unordered_map<std::string, double> metrics;

uint64_t traceId() const;
uint64_t spanId() const;
Expand Down
4 changes: 3 additions & 1 deletion src/tracer.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#include <datadog/opentracing.h>
#include <datadog/tags.h>
#include <opentracing/ext/tags.h>
#include <pthread.h>
#include <cstdlib>
Expand All @@ -8,6 +9,7 @@
#include "tracer.h"

namespace ot = opentracing;
namespace tags = datadog::tags;

namespace datadog {
namespace opentracing {
Expand Down Expand Up @@ -96,7 +98,7 @@ std::unique_ptr<ot::Span> Tracer::StartSpanWithOptions(ot::string_view operation
span->SetTag(tag.first, tag.second);
}
if (is_trace_root && opts_.environment != "") {
span->SetTag(environment_tag, opts_.environment);
span->SetTag(tags::environment, opts_.environment);
}
return std::move(span);
} catch (const std::bad_alloc &) {
Expand Down
63 changes: 54 additions & 9 deletions test/span_test.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
#include "../src/span.h"
#include <datadog/tags.h>
#include <opentracing/ext/tags.h>
#include <ctime>
#include <nlohmann/json.hpp>
#include <thread>
Expand All @@ -8,6 +10,7 @@
#define CATCH_CONFIG_MAIN
#include <catch2/catch.hpp>
using namespace datadog::opentracing;
namespace tags = datadog::tags;
using json = nlohmann::json;

TEST_CASE("span") {
Expand Down Expand Up @@ -97,12 +100,12 @@ TEST_CASE("span") {
span_id, span_id, 0, SpanContext{span_id, span_id, "", {}},
get_time(), "", "", "",
"", ""};
span.SetTag("http.url", test_case.first);
span.SetTag(ot::ext::http_url, test_case.first);
const ot::FinishSpanOptions finish_options;
span.FinishWithOptions(finish_options);

auto& result = buffer->traces(span_id).finished_spans->back();
REQUIRE(result->meta.find("http.url")->second == test_case.second);
REQUIRE(result->meta.find(ot::ext::http_url)->second == test_case.second);
}
}

Expand Down Expand Up @@ -179,9 +182,10 @@ TEST_CASE("span") {
"original span name",
"original resource",
""};
span.SetTag("span.type", "new type");
span.SetTag("resource.name", "new resource");
span.SetTag("service.name", "new service");
span.SetTag(tags::service_name, "new service");
span.SetTag(tags::span_type, "new type");
span.SetTag(tags::resource_name, "new resource");
span.SetTag(tags::analytics_event, true);
span.SetTag("tag with no special meaning", "ayy lmao");

span.FinishWithOptions(finish_options);
Expand All @@ -191,9 +195,50 @@ TEST_CASE("span") {
REQUIRE(result->meta == std::unordered_map<std::string, std::string>{
{"tag with no special meaning", "ayy lmao"}});
REQUIRE(result->name == "original span name");
REQUIRE(result->resource == "new resource");
REQUIRE(result->service == "new service");
REQUIRE(result->type == "new type");
REQUIRE(result->resource == "new resource");
REQUIRE(result->metrics == std::unordered_map<std::string, double>{{"_dd1.sr.eausr", 1}});
}

SECTION("values for analytics_event tag") {
auto span_id = get_id();
Span span{nullptr, buffer, get_time, sampler,
span_id, span_id, 0, SpanContext{span_id, span_id, "", {}},
get_time(), "", "", "",
"", ""};

struct AnalyticsEventTagTestCase {
ot::Value tag_value;
bool expected;
double metric_value;
};
auto test_case =
GENERATE(values<AnalyticsEventTagTestCase>({{true, true, 1.0},
{false, true, 0.0},
{1, true, 1.0},
{0, true, 0.0},
{1.0, true, 1.0},
{0.5, true, 0.5},
{0.0, true, 0.0},
{"", true, 0.0},
{-1, false, 0},
{2, false, 0},
{-0.1, false, 0},
{1.1, false, 0},
{"not a number at all", false, 0}}));

span.SetTag(tags::analytics_event, test_case.tag_value);
span.FinishWithOptions(finish_options);
auto& result = buffer->traces(100).finished_spans->at(0);
auto metric = result->metrics.find("_dd1.sr.eausr");

if (test_case.expected) {
REQUIRE(metric != result->metrics.end());
REQUIRE(metric->second == test_case.metric_value);
} else {
REQUIRE(metric == result->metrics.end());
}
}

SECTION("error tag sets error") {
Expand Down Expand Up @@ -341,7 +386,7 @@ TEST_CASE("span") {

auto& result = buffer->traces(100).finished_spans->at(0);
REQUIRE(result->metrics ==
std::unordered_map<std::string, int>{{"_sampling_priority_v1", 1}});
std::unordered_map<std::string, double>{{"_sampling_priority_v1", 1}});
}

SECTION("non-root spans may be sampled, as long as the trace is not yet distributed") {
Expand Down Expand Up @@ -380,7 +425,7 @@ TEST_CASE("span") {

auto& result = buffer->traces(42).finished_spans->at(0);
REQUIRE(result->metrics ==
std::unordered_map<std::string, int>{{"_sampling_priority_v1", 1}});
std::unordered_map<std::string, double>{{"_sampling_priority_v1", 1}});
}

SECTION("spans with an existing sampling priority may not be given a new one at Finish") {
Expand All @@ -401,7 +446,7 @@ TEST_CASE("span") {

auto& result = buffer->traces(100).finished_spans->at(0);
REQUIRE(result->metrics ==
std::unordered_map<std::string, int>{{"_sampling_priority_v1", -1}});
std::unordered_map<std::string, double>{{"_sampling_priority_v1", -1}});
}
}
}