Skip to content

Commit 9e9e8dc

Browse files
committed
[part 7] refactor!(telemetry): improve telemetry lifecycle
Changes: - add support for install_signature. - add support for product in configuration that will be reported in app-started message. - unit tests
1 parent c85660b commit 9e9e8dc

File tree

13 files changed

+292
-163
lines changed

13 files changed

+292
-163
lines changed

BUILD.bazel

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ cc_library(
44
"src/datadog/telemetry/configuration.cpp",
55
"src/datadog/telemetry/metrics.cpp",
66
"src/datadog/telemetry/log.h",
7+
"src/datadog/telemetry/product.h",
78
"src/datadog/telemetry/telemetry.cpp",
89
"src/datadog/telemetry/telemetry_impl.h",
910
"src/datadog/telemetry/telemetry_impl.cpp",

include/datadog/environment.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,10 @@ namespace environment {
5656
MACRO(DD_TELEMETRY_DEBUG) \
5757
MACRO(DD_TRACE_BAGGAGE_MAX_ITEMS) \
5858
MACRO(DD_TRACE_BAGGAGE_MAX_BYTES) \
59-
MACRO(DD_TELEMETRY_LOG_COLLECTION_ENABLED)
59+
MACRO(DD_TELEMETRY_LOG_COLLECTION_ENABLED) \
60+
MACRO(DD_INSTRUMENTATION_INSTALL_ID) \
61+
MACRO(DD_INSTRUMENTATION_INSTALL_TYPE) \
62+
MACRO(DD_INSTRUMENTATION_INSTALL_TIME)
6063

6164
#define WITH_COMMA(ARG) ARG,
6265

include/datadog/telemetry/configuration.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
#pragma once
22

3+
#include <datadog/config.h>
34
#include <datadog/expected.h>
45
#include <datadog/optional.h>
6+
#include <datadog/telemetry/product.h>
57

68
#include <chrono>
79
#include <string>
10+
#include <vector>
811

912
namespace datadog::telemetry {
1013

@@ -38,6 +41,8 @@ struct Configuration {
3841
// Can be overriden by the `DD_TELEMETRY_LOG_COLLECTION_ENABLED` environment
3942
// variable.
4043
tracing::Optional<bool> report_logs;
44+
// List of products reported in the `app-started` message.
45+
std::vector<Product> products;
4146
};
4247

4348
struct FinalizedConfiguration {
@@ -49,6 +54,13 @@ struct FinalizedConfiguration {
4954
std::chrono::steady_clock::duration heartbeat_interval;
5055
std::string integration_name;
5156
std::string integration_version;
57+
std::vector<Product> products;
58+
59+
// Onboarding metadata coming from `DD_INSTRUMENTATION_INSTALL_*` environment
60+
// variables.
61+
tracing::Optional<std::string> install_id;
62+
tracing::Optional<std::string> install_type;
63+
tracing::Optional<std::string> install_time;
5264

5365
friend tracing::Expected<FinalizedConfiguration> finalize_config(
5466
const Configuration&);

include/datadog/telemetry/telemetry.h

Lines changed: 3 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,9 @@
1919
namespace datadog::telemetry {
2020

2121
/// Initialize the telemetry module
22-
/// Once initialized, the telemetry module is running for the entier lifecycle
23-
/// of the application.
22+
/// Once initialized, sends a notification indicating the the application has
23+
/// started. The telemetry module is running for the entire lifecycle of the
24+
/// application.
2425
///
2526
/// @param configuration The finalized configuration settings.
2627
/// @param logger User logger instance.
@@ -36,21 +37,6 @@ void init(FinalizedConfiguration configuration,
3637
tracing::HTTPClient::URL agent_url,
3738
tracing::Clock clock = tracing::default_clock);
3839

39-
/// Sends a notification indicating that the application has started.
40-
///
41-
/// This function is responsible for reporting the application has successfully
42-
/// started. It takes a configuration map as a parameter, which contains various
43-
/// configuration settings helping to understand how our product are used.
44-
///
45-
/// @param conf A map containing configuration names and their corresponding
46-
/// metadata.
47-
///
48-
/// @note This function should be called after the application has completed its
49-
/// initialization process to ensure that all components are aware of the
50-
/// application's startup status.
51-
void send_app_started(const std::unordered_map<tracing::ConfigName,
52-
tracing::ConfigMetadata>& conf);
53-
5440
/// Sends configuration changes.
5541
///
5642
/// This function is responsible for sending reported configuration changes

src/datadog/telemetry/configuration.cpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,22 @@ tracing::Expected<FinalizedConfiguration> finalize_config(
122122
pick(env_config->integration_version, user_config.integration_version,
123123
tracing::tracer_version);
124124

125+
// products
126+
result.products = user_config.products;
127+
128+
// onboarding data
129+
if (auto install_id = lookup(environment::DD_INSTRUMENTATION_INSTALL_ID)) {
130+
result.install_id = std::string(*install_id);
131+
}
132+
if (auto install_type =
133+
lookup(environment::DD_INSTRUMENTATION_INSTALL_TYPE)) {
134+
result.install_type = std::string(*install_type);
135+
}
136+
if (auto install_time =
137+
lookup(environment::DD_INSTRUMENTATION_INSTALL_TIME)) {
138+
result.install_time = std::string(*install_time);
139+
}
140+
125141
return result;
126142
}
127143

src/datadog/telemetry/telemetry.cpp

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -61,16 +61,6 @@ void init(FinalizedConfiguration configuration,
6161
agent_url, clock});
6262
}
6363

64-
void send_app_started(const std::unordered_map<tracing::ConfigName,
65-
tracing::ConfigMetadata>& conf) {
66-
std::visit(
67-
details::Overload{
68-
[&](Telemetry& telemetry) { telemetry.send_app_started(conf); },
69-
[](NoopTelemetry) {},
70-
},
71-
instance());
72-
}
73-
7464
void send_configuration_change() {
7565
std::visit(
7666
details::Overload{

src/datadog/telemetry/telemetry_impl.cpp

Lines changed: 91 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -163,13 +163,15 @@ Telemetry::Telemetry(FinalizedConfiguration config,
163163
"Error occurred during HTTP request for telemetry: "));
164164
};
165165

166+
send_telemetry("app-started", app_started());
166167
schedule_tasks();
167168
}
168169

169170
void Telemetry::schedule_tasks() {
170171
tasks_.emplace_back(scheduler_->schedule_recurring_event(
171-
config_.heartbeat_interval,
172-
[this]() { send_heartbeat_and_telemetry(); }));
172+
config_.heartbeat_interval, [this]() {
173+
send_telemetry("app-heartbeat", heartbeat_and_telemetry());
174+
}));
173175

174176
if (config_.report_metrics) {
175177
tasks_.emplace_back(scheduler_->schedule_recurring_event(
@@ -183,7 +185,7 @@ Telemetry::~Telemetry() {
183185
capture_metrics();
184186
// The app-closing message is bundled with a message containing the
185187
// final metric values.
186-
send_app_closing();
188+
send_telemetry("app-closing", app_closing());
187189
http_client_->drain(clock_().tick + 1s);
188190
}
189191
}
@@ -239,7 +241,7 @@ Telemetry::Telemetry(Telemetry&& rhs)
239241
metrics_snapshots_.emplace_back(*m, MetricSnapshot{});
240242
}
241243

242-
cancel_tasks(rhs.tasks_);
244+
cancel_tasks(tasks_);
243245
schedule_tasks();
244246
}
245247

@@ -302,8 +304,6 @@ Telemetry& Telemetry::operator=(Telemetry&& rhs) {
302304
return *this;
303305
}
304306

305-
// TODO(@dmehala): Move `report_logs` check in the serialization once
306-
// `TracerTelemetry` will be removed.
307307
void Telemetry::log_error(std::string message) {
308308
if (!config_.report_logs) return;
309309
log(std::move(message), LogLevel::ERROR);
@@ -340,7 +340,6 @@ void Telemetry::send_telemetry(StringView request_type, std::string payload) {
340340
}
341341
};
342342

343-
// TODO(@dmehala): make `clock::instance()` a singleton
344343
auto post_result = http_client_->post(
345344
telemetry_endpoint_, set_telemetry_headers, std::move(payload),
346345
telemetry_on_response_, telemetry_on_error_,
@@ -351,24 +350,24 @@ void Telemetry::send_telemetry(StringView request_type, std::string payload) {
351350
}
352351
}
353352

354-
void Telemetry::send_app_started(
355-
const std::unordered_map<tracing::ConfigName, tracing::ConfigMetadata>&
356-
config_metadata) {
357-
send_telemetry("app-started", app_started(config_metadata));
358-
}
359-
360-
void Telemetry::send_app_closing() {
361-
send_telemetry("app-closing", app_closing());
362-
}
353+
void Telemetry::send_configuration_change() {
354+
if (configuration_snapshot_.empty()) return;
363355

364-
void Telemetry::send_heartbeat_and_telemetry() {
365-
send_telemetry("app-heartbeat", heartbeat_and_telemetry());
366-
}
356+
std::vector<ConfigMetadata> current_configuration;
357+
std::swap(current_configuration, configuration_snapshot_);
367358

368-
void Telemetry::send_configuration_change() {
369-
if (auto payload = configuration_change()) {
370-
send_telemetry("app-client-configuration-change", *payload);
359+
auto configuration_json = nlohmann::json::array();
360+
for (const auto& config_metadata : current_configuration) {
361+
configuration_json.emplace_back(
362+
generate_configuration_field(config_metadata));
371363
}
364+
365+
auto telemetry_body =
366+
generate_telemetry_body("app-client-configuration-change");
367+
telemetry_body["payload"] =
368+
nlohmann::json{{"configuration", configuration_json}};
369+
370+
send_telemetry("app-client-configuration-change", telemetry_body.dump());
372371
}
373372

374373
std::string Telemetry::heartbeat_and_telemetry() {
@@ -523,72 +522,95 @@ std::string Telemetry::app_closing() {
523522
return message_batch_payload;
524523
}
525524

526-
std::string Telemetry::app_started(
527-
const std::unordered_map<ConfigName, ConfigMetadata>& configurations) {
525+
std::string Telemetry::app_started() {
528526
auto configuration_json = nlohmann::json::array();
529-
for (const auto& [_, config_metadata] : configurations) {
530-
// if (config_metadata.value.empty()) continue;
527+
auto product_json = nlohmann::json::object();
531528

532-
configuration_json.emplace_back(
533-
generate_configuration_field(config_metadata));
529+
for (const auto& product : config_.products) {
530+
auto& configurations = product.configurations;
531+
for (const auto& [_, config_metadata] : configurations) {
532+
// if (config_metadata.value.empty()) continue;
533+
534+
configuration_json.emplace_back(
535+
generate_configuration_field(config_metadata));
536+
}
537+
538+
/// NOTE(@dmehala): Telemetry API is tightly related to APM tracing and
539+
/// assumes telemetry event can only be generated from a tracer. The
540+
/// assumption is that the tracing product is always enabled and there
541+
/// is no need to declare it.
542+
if (product.name == Product::Name::tracing) continue;
543+
544+
auto p = nlohmann::json{
545+
{to_string(product.name),
546+
nlohmann::json{
547+
{"version", product.version},
548+
{"enabled", product.enabled},
549+
}},
550+
};
551+
552+
if (product.error_code || product.error_message) {
553+
auto p_error = nlohmann::json{};
554+
if (product.error_code) {
555+
p_error.emplace("code", *product.error_code);
556+
}
557+
if (product.error_message) {
558+
p_error.emplace("message", *product.error_message);
559+
}
560+
561+
p.emplace("error", std::move(p_error));
562+
}
563+
564+
product_json.emplace(std::move(p));
534565
}
535566

536-
// clang-format off
537567
auto app_started_msg = nlohmann::json{
538-
{"request_type", "app-started"},
539-
{"payload", nlohmann::json{
540-
{"configuration", configuration_json}
541-
}}
568+
{"request_type", "app-started"},
569+
{
570+
"payload",
571+
nlohmann::json{
572+
{"configuration", configuration_json},
573+
{"products", product_json},
574+
},
575+
},
542576
};
543577

578+
if (config_.install_id.has_value()) {
579+
app_started_msg["payload"].emplace(
580+
"install_signature", nlohmann::json{
581+
{"install_id", *config_.install_id},
582+
{"install_type", *config_.install_type},
583+
{"install_time", *config_.install_time},
584+
});
585+
}
586+
544587
auto batch = generate_telemetry_body("message-batch");
545-
batch["payload"] = nlohmann::json::array({
546-
std::move(app_started_msg)
547-
});
548-
// clang-format on
588+
batch["payload"] = nlohmann::json::array({std::move(app_started_msg)});
549589

550590
if (!config_.integration_name.empty()) {
551-
// clang-format off
552591
auto integration_msg = nlohmann::json{
553-
{"request_type", "app-integrations-change"},
554-
{"payload", nlohmann::json{
555-
{"integrations", nlohmann::json::array({
556-
nlohmann::json{
557-
{"name", config_.integration_name},
558-
{"version", config_.integration_version},
559-
{"enabled", true}
560-
}
561-
})}
562-
}}
592+
{"request_type", "app-integrations-change"},
593+
{
594+
"payload",
595+
nlohmann::json{
596+
{
597+
"integrations",
598+
nlohmann::json::array({
599+
nlohmann::json{{"name", config_.integration_name},
600+
{"version", config_.integration_version},
601+
{"enabled", true}},
602+
}),
603+
},
604+
},
605+
},
563606
};
564-
// clang-format on
565607

566608
batch["payload"].emplace_back(std::move(integration_msg));
567609
}
568610

569611
return batch.dump();
570612
}
571613

572-
Optional<std::string> Telemetry::configuration_change() {
573-
if (configuration_snapshot_.empty()) return nullopt;
574-
575-
std::vector<ConfigMetadata> current_configuration;
576-
std::swap(current_configuration, configuration_snapshot_);
577-
578-
auto configuration_json = nlohmann::json::array();
579-
for (const auto& config_metadata : current_configuration) {
580-
configuration_json.emplace_back(
581-
generate_configuration_field(config_metadata));
582-
}
583-
584-
auto telemetry_body =
585-
generate_telemetry_body("app-client-configuration-change");
586-
telemetry_body["payload"] =
587-
nlohmann::json{{"configuration", configuration_json}};
588-
589-
return telemetry_body.dump();
590-
}
591-
592614
nlohmann::json Telemetry::generate_telemetry_body(std::string request_type) {
593615
std::time_t tracer_time = std::chrono::duration_cast<std::chrono::seconds>(
594616
clock_().wall.time_since_epoch())

0 commit comments

Comments
 (0)