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;
 };