From 0dad3007be1bfcc7faa48eee65d3c5c6fd569373 Mon Sep 17 00:00:00 2001 From: Damien Mehala <damien.mehala@datadoghq.com> Date: Tue, 9 Jan 2024 17:20:41 +0100 Subject: [PATCH 1/4] report integration name and version --- src/datadog/tracer.cpp | 3 +- src/datadog/tracer_config.cpp | 2 ++ src/datadog/tracer_config.h | 6 ++++ src/datadog/tracer_telemetry.cpp | 48 ++++++++++++++++++++++++++------ src/datadog/tracer_telemetry.h | 6 +++- test/test_tracer_telemetry.cpp | 5 +++- 6 files changed, 58 insertions(+), 12 deletions(-) diff --git a/src/datadog/tracer.cpp b/src/datadog/tracer.cpp index 4d0da254..943daec6 100644 --- a/src/datadog/tracer.cpp +++ b/src/datadog/tracer.cpp @@ -43,7 +43,8 @@ Tracer::Tracer(const FinalizedTracerConfig& config, signature_{runtime_id_, config.defaults.service, config.defaults.environment}, tracer_telemetry_(std::make_shared<TracerTelemetry>( - config.report_telemetry, config.clock, logger_, signature_)), + config.report_telemetry, config.clock, logger_, signature_, + config.integration_name, config.integration_version)), span_sampler_( std::make_shared<SpanSampler>(config.span_sampler, config.clock)), generator_(generator), diff --git a/src/datadog/tracer_config.cpp b/src/datadog/tracer_config.cpp index 592a9c22..44065a61 100644 --- a/src/datadog/tracer_config.cpp +++ b/src/datadog/tracer_config.cpp @@ -366,6 +366,8 @@ Expected<FinalizedTracerConfig> finalize_config(const TracerConfig &config, } result.runtime_id = config.runtime_id; + result.integration_name = config.integration_name; + result.integration_version = config.integration_version; return result; } diff --git a/src/datadog/tracer_config.h b/src/datadog/tracer_config.h index b59ad0c3..ca4ae1a5 100644 --- a/src/datadog/tracer_config.h +++ b/src/datadog/tracer_config.h @@ -119,6 +119,10 @@ struct TracerConfig { // such as those in the worker threads/processes of a reverse proxy, might // specify the same `runtime_id` for all tracer instances in the same run. Optional<RuntimeID> runtime_id; + + // TODO + std::string integration_name; + std::string integration_version; }; // `FinalizedTracerConfig` contains `Tracer` implementation details derived from @@ -150,6 +154,8 @@ class FinalizedTracerConfig { bool report_telemetry; Optional<RuntimeID> runtime_id; Clock clock; + std::string integration_name; + std::string integration_version; }; // Return a `FinalizedTracerConfig` from the specified `config` and from any diff --git a/src/datadog/tracer_telemetry.cpp b/src/datadog/tracer_telemetry.cpp index c6a06c4c..9ebda3c5 100644 --- a/src/datadog/tracer_telemetry.cpp +++ b/src/datadog/tracer_telemetry.cpp @@ -10,12 +10,16 @@ namespace tracing { TracerTelemetry::TracerTelemetry(bool enabled, const Clock& clock, const std::shared_ptr<Logger>& logger, - const TracerSignature& tracer_signature) + const TracerSignature& tracer_signature, + const std::string& integration_name, + const std::string& integration_version) : enabled_(enabled), clock_(clock), logger_(logger), tracer_signature_(tracer_signature), - hostname_(get_hostname().value_or("hostname-unavailable")) { + hostname_(get_hostname().value_or("hostname-unavailable")), + integration_name_(integration_name), + integration_version_(integration_version) { if (enabled_) { // Register all the metrics that we're tracking by adding them to the // metrics_snapshots_ container. This allows for simpler iteration logic @@ -80,14 +84,40 @@ nlohmann::json TracerTelemetry::generate_telemetry_body( } std::string TracerTelemetry::app_started() { - auto telemetry_body = generate_telemetry_body("app-started"); - // TODO: environment variables or finalized config details - telemetry_body["payload"] = nlohmann::json::object({ - {"configuration", nlohmann::json::array({})}, - + // clang-format off + auto app_started_msg = nlohmann::json{ + {"request_type", "app-started"}, + {"payload", nlohmann::json{ + {"configuration", nlohmann::json::array()} + }} + }; + + auto batch = generate_telemetry_body("message-batch"); + batch["payload"] = nlohmann::json::array({ + std::move(app_started_msg) }); - auto app_started_payload = telemetry_body.dump(); - return app_started_payload; + // clang-format on + + if (!integration_name_.empty()) { + // clang-format off + auto integration_msg = nlohmann::json{ + {"request_type", "app-integrations-change"}, + {"payload", nlohmann::json{ + {"integrations", nlohmann::json::array({ + nlohmann::json{ + {"name", integration_name_}, + {"version", integration_version_}, + {"enabled", true} + } + })} + }} + }; + // clang-format on + + batch["payload"].emplace_back(std::move(integration_msg)); + } + + return batch.dump(); } void TracerTelemetry::capture_metrics() { diff --git a/src/datadog/tracer_telemetry.h b/src/datadog/tracer_telemetry.h index e9488b94..f29231a6 100644 --- a/src/datadog/tracer_telemetry.h +++ b/src/datadog/tracer_telemetry.h @@ -46,6 +46,8 @@ class TracerTelemetry { std::shared_ptr<Logger> logger_; TracerSignature tracer_signature_; std::string hostname_; + std::string integration_name_; + std::string integration_version_; uint64_t seq_id_ = 0; // This structure contains all the metrics that are exposed by tracer // telemetry. @@ -100,7 +102,9 @@ class TracerTelemetry { public: TracerTelemetry(bool enabled, const Clock& clock, const std::shared_ptr<Logger>& logger, - const TracerSignature& tracer_signature); + const TracerSignature& tracer_signature, + const std::string& integration_name, + const std::string& integration_version); bool enabled() { return enabled_; }; // Provides access to the telemetry metrics for updating the values. // This value should not be stored. diff --git a/test/test_tracer_telemetry.cpp b/test/test_tracer_telemetry.cpp index 05cd00ba..f0ba5992 100644 --- a/test/test_tracer_telemetry.cpp +++ b/test/test_tracer_telemetry.cpp @@ -27,7 +27,10 @@ TEST_CASE("Tracer telemetry") { /* service = */ "testsvc", /* environment = */ "test"}; - TracerTelemetry tracer_telemetry = {true, clock, logger, tracer_signature}; + const std::string ignore{""}; + + TracerTelemetry tracer_telemetry = {true, clock, logger, tracer_signature, + ignore, ignore}; SECTION("generates app-started message") { auto app_started_message = tracer_telemetry.app_started(); From afdfea84e081d189c3fb576d92bd7fbf878ddaed Mon Sep 17 00:00:00 2001 From: Damien Mehala <damien.mehala@datadoghq.com> Date: Wed, 10 Jan 2024 15:09:50 +0100 Subject: [PATCH 2/4] fix test --- test/test_tracer_telemetry.cpp | 33 +++++++++++++++++++++++++++------ 1 file changed, 27 insertions(+), 6 deletions(-) diff --git a/test/test_tracer_telemetry.cpp b/test/test_tracer_telemetry.cpp index f0ba5992..5fe1b144 100644 --- a/test/test_tracer_telemetry.cpp +++ b/test/test_tracer_telemetry.cpp @@ -6,6 +6,7 @@ #include <datadog/tracer_telemetry.h> #include <datadog/json.hpp> +#include <unordered_set> #include "datadog/runtime_id.h" #include "mocks/loggers.h" @@ -13,7 +14,7 @@ using namespace datadog::tracing; -TEST_CASE("Tracer telemetry") { +TEST_CASE("Tracer telemetry", "[telemetry]") { const std::time_t mock_time = 1672484400; const Clock clock = []() { TimePoint result; @@ -29,13 +30,33 @@ TEST_CASE("Tracer telemetry") { const std::string ignore{""}; - TracerTelemetry tracer_telemetry = {true, clock, logger, tracer_signature, - ignore, ignore}; + TracerTelemetry tracer_telemetry{true, clock, logger, tracer_signature, + ignore, ignore}; SECTION("generates app-started message") { - auto app_started_message = tracer_telemetry.app_started(); - auto app_started = nlohmann::json::parse(app_started_message); - REQUIRE(app_started["request_type"] == "app-started"); + SECTION("Without a defined integration") { + auto app_started_message = tracer_telemetry.app_started(); + auto app_started = nlohmann::json::parse(app_started_message); + REQUIRE(app_started["request_type"] == "message-batch"); + REQUIRE(app_started["payload"].size() == 1); + CHECK(app_started["payload"][0]["request_type"] == "app-started"); + } + + SECTION("With an integration") { + TracerTelemetry tracer_telemetry{ + true, clock, logger, tracer_signature, "nginx", "1.25.2"}; + auto app_started_message = tracer_telemetry.app_started(); + auto app_started = nlohmann::json::parse(app_started_message); + REQUIRE(app_started["request_type"] == "message-batch"); + REQUIRE(app_started["payload"].size() == 2); + + const std::unordered_set<std::string> expected{"app-started", + "app-integrations-change"}; + + for (const auto& payload : app_started["payload"]) { + CHECK(expected.find(payload["request_type"]) != expected.cend()); + } + } } SECTION("generates a heartbeat message") { From 3ba3d6c405429772d1f24f333720e5ca82c04f58 Mon Sep 17 00:00:00 2001 From: Damien Mehala <damien.mehala@datadoghq.com> Date: Thu, 11 Jan 2024 14:07:58 +0100 Subject: [PATCH 3/4] code review --- src/datadog/tracer_config.h | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/datadog/tracer_config.h b/src/datadog/tracer_config.h index ca4ae1a5..0a62562a 100644 --- a/src/datadog/tracer_config.h +++ b/src/datadog/tracer_config.h @@ -120,8 +120,12 @@ struct TracerConfig { // specify the same `runtime_id` for all tracer instances in the same run. Optional<RuntimeID> runtime_id; - // TODO + // `integration_name` is the name of the product integrating this library. + // Example: `nginx`, `envoy` or `istio`. std::string integration_name; + // `integration_version` is the version of the product integrating this + // library. + // Example: `1.2.3`, `6c44da20`, `2020.02.13` std::string integration_version; }; From 022a8dad912db16b246db9bb4c55ef406be8966b Mon Sep 17 00:00:00 2001 From: Damien Mehala <damien.mehala@datadoghq.com> Date: Thu, 11 Jan 2024 16:41:03 +0100 Subject: [PATCH 4/4] Update src/datadog/tracer_config.h Co-authored-by: David Goffredo <david.goffredo@datadoghq.com> --- src/datadog/tracer_config.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/datadog/tracer_config.h b/src/datadog/tracer_config.h index 0a62562a..33c7ac36 100644 --- a/src/datadog/tracer_config.h +++ b/src/datadog/tracer_config.h @@ -121,11 +121,11 @@ struct TracerConfig { Optional<RuntimeID> runtime_id; // `integration_name` is the name of the product integrating this library. - // Example: `nginx`, `envoy` or `istio`. + // Example: "nginx", "envoy" or "istio". std::string integration_name; // `integration_version` is the version of the product integrating this // library. - // Example: `1.2.3`, `6c44da20`, `2020.02.13` + // Example: "1.2.3", "6c44da20", "2020.02.13" std::string integration_version; };