Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 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
1 change: 1 addition & 0 deletions BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ cc_library(
srcs = [
"src/datadog/telemetry/configuration.cpp",
"src/datadog/telemetry/metrics.cpp",
"src/datadog/telemetry/log.h",
"src/datadog/telemetry/telemetry.cpp",
"src/datadog/base64.cpp",
"src/datadog/cerr_logger.cpp",
Expand Down
3 changes: 2 additions & 1 deletion include/datadog/environment.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,8 @@ namespace environment {
MACRO(DD_TELEMETRY_HEARTBEAT_INTERVAL) \
MACRO(DD_TELEMETRY_METRICS_ENABLED) \
MACRO(DD_TELEMETRY_METRICS_INTERVAL_SECONDS) \
MACRO(DD_TELEMETRY_DEBUG)
MACRO(DD_TELEMETRY_DEBUG) \
MACRO(DD_TELEMETRY_LOG_COLLECTION_ENABLED)

#define WITH_COMMA(ARG) ARG,

Expand Down
6 changes: 6 additions & 0 deletions include/datadog/telemetry/configuration.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,18 @@ struct Configuration {
// library.
// Example: "1.2.3", "6c44da20", "2020.02.13"
tracing::Optional<std::string> integration_version;
// Enable or disable telemetry logs collection.
// Default: enabled.
// Can be overriden by the `DD_TELEMETRY_LOG_COLLECTION_ENABLED` environment
// variable.
tracing::Optional<bool> report_logs;
};

struct FinalizedConfiguration {
bool debug;
bool enabled;
bool report_metrics;
bool report_logs;
std::chrono::steady_clock::duration metrics_interval;
std::chrono::steady_clock::duration heartbeat_interval;
std::string integration_name;
Expand Down
25 changes: 23 additions & 2 deletions include/datadog/telemetry/telemetry.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,19 +16,40 @@ class TracerTelemetry;

namespace telemetry {

class Telemetry {
/// The telemetry class is responsible for handling internal telemetry data to
/// track Datadog product usage. It _can_ collect and report logs and metrics.
///
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't we have private headers ? If yes, is there a reason to put the file in the public headers and say do not use ?

Copy link
Collaborator Author

@dmehala dmehala Feb 14, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Imagine I am implementing appsec, how Datadog user/integrator outside of the tracer could use it if it's private?

/// IMPORTANT: This is intended for use only by Datadog Engineers.
class Telemetry final {
/// Configuration object containing the validated settings for telemetry
FinalizedConfiguration config_;
/// Shared pointer to the user logger instance.
std::shared_ptr<tracing::Logger> logger_;

/// TODO(@dmehala): Legacy dependency.
std::shared_ptr<tracing::DatadogAgent> datadog_agent_;
std::shared_ptr<tracing::TracerTelemetry> tracer_telemetry_;

public:
/// Constructor for the Telemetry class
///
/// @param configuration The finalized configuration settings.
/// @param logger User logger instance.
/// @param metrics A vector user metrics to report.
Telemetry(FinalizedConfiguration configuration,
std::shared_ptr<tracing::Logger> logger,
std::vector<std::shared_ptr<Metric>> metrics);

~Telemetry() = default;

/// Capture and report internal error message to Datadog.
///
/// @param message The error message.
void log_error(std::string message);

/// capture and report internal warning message to Datadog.
///
/// @param message The warning message to log.
void log_warning(std::string message);
};

} // namespace telemetry
Expand Down
10 changes: 10 additions & 0 deletions src/datadog/telemetry/configuration.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,11 @@ tracing::Expected<Configuration> load_telemetry_env_config() {
env_cfg.report_metrics = !falsy(*metrics_enabled);
}

if (auto logs_enabled =
lookup(environment::DD_TELEMETRY_LOG_COLLECTION_ENABLED)) {
env_cfg.report_logs = !falsy(*logs_enabled);
}

if (auto metrics_interval_seconds =
lookup(environment::DD_TELEMETRY_METRICS_INTERVAL_SECONDS)) {
auto maybe_value = parse_double(*metrics_interval_seconds);
Expand Down Expand Up @@ -66,10 +71,15 @@ tracing::Expected<FinalizedConfiguration> finalize_config(
// NOTE(@dmehala): if the telemetry module is disabled then report metrics
// is also disabled.
result.report_metrics = false;
result.report_logs = false;
} else {
// report_metrics
std::tie(origin, result.report_metrics) =
pick(env_config->report_metrics, user_config.report_metrics, true);

// report_logs
std::tie(origin, result.report_logs) =
pick(env_config->report_logs, user_config.report_logs, true);
}

// debug
Expand Down
12 changes: 12 additions & 0 deletions src/datadog/telemetry/log.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#include <string>

namespace datadog::telemetry {

enum class LogLevel : char { ERROR, WARNING };

struct LogMessage final {
std::string message;
LogLevel level;
};

} // namespace datadog::telemetry
8 changes: 8 additions & 0 deletions src/datadog/telemetry/telemetry.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,5 +39,13 @@ Telemetry::Telemetry(FinalizedConfiguration config,
std::vector<std::shared_ptr<remote_config::Listener>>{});
}

void Telemetry::log_error(std::string message) {
tracer_telemetry_->log(std::move(message), LogLevel::ERROR);
}

void Telemetry::log_warning(std::string message) {
tracer_telemetry_->log(std::move(message), LogLevel::WARNING);
}

} // namespace telemetry
} // namespace datadog
42 changes: 42 additions & 0 deletions src/datadog/tracer_telemetry.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,27 @@ std::string TracerTelemetry::heartbeat_and_telemetry() {
batch_payloads.emplace_back(std::move(generate_metrics));
}

if (!logs_.empty()) {
auto encoded_logs = nlohmann::json::array();
for (const auto& log : logs_) {
auto encoded =
nlohmann::json{{"message", log.message}, {"level", log.level}};
encoded_logs.emplace_back(std::move(encoded));
}

assert(!encoded_logs.empty());

auto logs_payload = nlohmann::json::object({
{"request_type", "logs"},
{"payload",
nlohmann::json{
{"logs", encoded_logs},
}},
});

batch_payloads.emplace_back(std::move(logs_payload));
}

auto telemetry_body = generate_telemetry_body("message-batch");
telemetry_body["payload"] = batch_payloads;
auto message_batch_payload = telemetry_body.dump();
Expand Down Expand Up @@ -348,6 +369,27 @@ std::string TracerTelemetry::app_closing() {
batch_payloads.emplace_back(std::move(generate_metrics));
}

if (!logs_.empty()) {
auto encoded_logs = nlohmann::json::array();
for (const auto& log : logs_) {
auto encoded =
nlohmann::json{{"message", log.message}, {"level", log.level}};
encoded_logs.emplace_back(std::move(encoded));
}

assert(!encoded_logs.empty());

auto logs_payload = nlohmann::json::object({
{"request_type", "logs"},
{"payload",
nlohmann::json{
{"logs", encoded_logs},
}},
});

batch_payloads.emplace_back(std::move(logs_payload));
}

auto telemetry_body = generate_telemetry_body("message-batch");
telemetry_body["payload"] = batch_payloads;
auto message_batch_payload = telemetry_body.dump();
Expand Down
9 changes: 8 additions & 1 deletion src/datadog/tracer_telemetry.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@

#include "json.hpp"
#include "platform_util.h"
#include "telemetry/log.h"

namespace datadog {
namespace tracing {
Expand All @@ -47,7 +48,7 @@ struct SpanDefaults;

class TracerTelemetry {
bool enabled_ = false;
bool debug_ = false;
bool debug_ = true;
Clock clock_;
std::shared_ptr<Logger> logger_;
HostInfo host_info_;
Expand Down Expand Up @@ -120,6 +121,8 @@ class TracerTelemetry {

std::vector<std::shared_ptr<telemetry::Metric>> user_metrics_;

std::vector<telemetry::LogMessage> logs_;

public:
TracerTelemetry(
bool enabled, const Clock& clock, const std::shared_ptr<Logger>& logger,
Expand Down Expand Up @@ -151,6 +154,10 @@ class TracerTelemetry {
std::string app_closing();
// Construct an `app-client-configuration-change` message.
std::string configuration_change();

inline void log(std::string message, telemetry::LogLevel level) {
logs_.emplace_back(telemetry::LogMessage{std::move(message), level});
}
};

} // namespace tracing
Expand Down
15 changes: 13 additions & 2 deletions test/telemetry/test_configuration.cpp
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
#include <datadog/environment.h>
#include <datadog/telemetry/configuration.h>

#include <chrono>

#include "../common/environment.h"
#include "../test.h"

Expand All @@ -19,6 +17,7 @@ TELEMETRY_CONFIGURATION_TEST("defaults") {
REQUIRE(cfg);
CHECK(cfg->debug == false);
CHECK(cfg->enabled == true);
CHECK(cfg->report_logs == true);
CHECK(cfg->report_metrics == true);
CHECK(cfg->metrics_interval == 60s);
CHECK(cfg->heartbeat_interval == 10s);
Expand All @@ -27,6 +26,7 @@ TELEMETRY_CONFIGURATION_TEST("defaults") {
TELEMETRY_CONFIGURATION_TEST("code override") {
telemetry::Configuration cfg;
cfg.enabled = false;
cfg.report_logs = false;
cfg.report_metrics = false;
cfg.metrics_interval_seconds = 1;
cfg.heartbeat_interval_seconds = 2;
Expand All @@ -37,6 +37,7 @@ TELEMETRY_CONFIGURATION_TEST("code override") {
REQUIRE(final_cfg);
CHECK(final_cfg->enabled == false);
CHECK(final_cfg->debug == false);
CHECK(final_cfg->report_logs == false);
CHECK(final_cfg->report_metrics == false);
CHECK(final_cfg->metrics_interval == 1s);
CHECK(final_cfg->heartbeat_interval == 2s);
Expand All @@ -48,11 +49,13 @@ TELEMETRY_CONFIGURATION_TEST("enabled and report metrics precedence") {
SECTION("enabled takes precedence over metrics enabled") {
telemetry::Configuration cfg;
cfg.enabled = false;
cfg.report_logs = true;
cfg.report_metrics = true;

auto final_cfg = finalize_config(cfg);
REQUIRE(final_cfg);
CHECK(final_cfg->enabled == false);
CHECK(final_cfg->report_logs == false);
CHECK(final_cfg->report_metrics == false);
}
}
Expand Down Expand Up @@ -84,6 +87,14 @@ TELEMETRY_CONFIGURATION_TEST("environment environment override") {
CHECK(final_cfg->report_metrics == false);
}

SECTION("Override `report_logs` field") {
cfg.report_logs = true;
ddtest::EnvGuard env("DD_TELEMETRY_LOG_COLLECTION_ENABLED", "false");
auto final_cfg = telemetry::finalize_config(cfg);
REQUIRE(final_cfg);
CHECK(final_cfg->report_logs == false);
}

SECTION("Override metrics interval") {
cfg.metrics_interval_seconds = 88;
ddtest::EnvGuard env("DD_TELEMETRY_METRICS_INTERVAL_SECONDS", "15");
Expand Down