Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 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
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,6 @@ dist/
out/
MODULE.bazel.lock
.vscode
.cache/
.cursor/
.DS_Store
1 change: 1 addition & 0 deletions bin/format
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ docker_clang_format() {
process_arg "$arg"
done | xargs -0 \
docker run \
--platform linux/amd64 \

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Is this change necessary? IIRC docker would use the right platform depending on the host platform.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

oh yes I was just getting WARNING: The requested image's platform (linux/amd64) does not match the detected host platform (linux/arm64/v8) and no specific platform was requested on my machine and added that but I can get rid of it

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

fixed in 382e80f

--interactive \
--rm \
--volume /etc/passwd:/etc/passwd:ro \
Expand Down
68 changes: 66 additions & 2 deletions include/datadog/config.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
#pragma once

#include <string>
#include <type_traits>
#include <unordered_map>
#include <utility>
#include <vector>

#include "error.h"
#include "optional.h"

Expand Down Expand Up @@ -56,14 +62,72 @@ struct ConfigMetadata {
: name(n), value(std::move(v)), origin(orig), error(std::move(err)) {}
};

// Return the chosen configuration value, in order of precedence: `from_env`,
// `from_user`, and `fallback`.
// This overload directly populates both telemetry_configs[config_name] and
// metadata[config_name] using the stringified value, with all values found,
// from lowest to highest precedence. Returns the chosen value directly.
//
// The fallback parameter is optional and defaults to nullptr.
template <typename Value, typename Stringifier = std::nullptr_t,
typename DefaultValue = std::nullptr_t>
Value pick(const Optional<Value>& from_env, const Optional<Value>& from_user,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This function does more than just pick a value from a list anymore. Either the configuration chain should be structured differently, or the function should be renamed to better reflect its behavior.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Good point, renamed to resolve_and_record_config it in 382e80f and improved documentation

std::unordered_map<ConfigName, std::vector<ConfigMetadata>>*
telemetry_configs,
std::unordered_map<ConfigName, ConfigMetadata>* metadata,
ConfigName config_name, DefaultValue fallback = nullptr,
Stringifier to_string_fn = nullptr) {
auto stringify = [&](const Value& v) -> std::string {
if constexpr (!std::is_same_v<Stringifier, std::nullptr_t>) {
return to_string_fn(v); // use provided function
} else if constexpr (std::is_constructible_v<std::string, Value>) {
return std::string(v); // default behaviour (works for string-like types)
} else {
static_assert(!std::is_same_v<Value, Value>,
"Non-string types require a stringifier function");
return ""; // unreachable
}
};

std::vector<ConfigMetadata> telemetry_entries;
Optional<Value> chosen_value;

auto add_entry = [&](ConfigMetadata::Origin origin, const Value& val) {
std::string val_str = stringify(val);
telemetry_entries.emplace_back(
ConfigMetadata{config_name, val_str, origin});
chosen_value = val;
};

// Add DEFAULT entry if fallback was provided (detected by type)
if constexpr (!std::is_same_v<DefaultValue, std::nullptr_t>) {
add_entry(ConfigMetadata::Origin::DEFAULT, fallback);
}

if (from_user) {
add_entry(ConfigMetadata::Origin::CODE, *from_user);
}

if (from_env) {
add_entry(ConfigMetadata::Origin::ENVIRONMENT_VARIABLE, *from_env);
}

(*telemetry_configs)[config_name] = std::move(telemetry_entries);
if (!(*telemetry_configs)[config_name].empty()) {
(*metadata)[config_name] = (*telemetry_configs)[config_name].back();
}

return chosen_value.value_or(Value{});
}

// Return a pair containing the configuration origin and value of a
// configuration value chosen from one of the specified `from_env`,
// `from_config`, and `fallback`. This function defines the relative precedence
// among configuration values originating from the environment, programmatic
// configuration, and default configuration.
template <typename Value, typename DefaultValue>
std::pair<ConfigMetadata::Origin, Value> pick(const Optional<Value> &from_env,
const Optional<Value> &from_user,
std::pair<ConfigMetadata::Origin, Value> pick(const Optional<Value>& from_env,
const Optional<Value>& from_user,
DefaultValue fallback) {
if (from_env) {
return {ConfigMetadata::Origin::ENVIRONMENT_VARIABLE, *from_env};
Expand Down
3 changes: 2 additions & 1 deletion include/datadog/telemetry/product.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

#include <string>
#include <unordered_map>
#include <vector>

namespace datadog::telemetry {

Expand All @@ -30,7 +31,7 @@ struct Product final {
/// Optional error message related to the product status.
tracing::Optional<std::string> error_message;
/// Map of configuration settings for the product.
std::unordered_map<tracing::ConfigName, tracing::ConfigMetadata>
std::unordered_map<tracing::ConfigName, std::vector<tracing::ConfigMetadata>>
configurations;
};

Expand Down
9 changes: 5 additions & 4 deletions src/datadog/telemetry/telemetry_impl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -587,11 +587,12 @@ std::string Telemetry::app_started_payload() {

for (const auto& product : config_.products) {
auto& configurations = product.configurations;
for (const auto& [_, config_metadata] : configurations) {
for (const auto& [_, config_metadatas] : configurations) {
// if (config_metadata.value.empty()) continue;

configuration_json.emplace_back(
generate_configuration_field(config_metadata));
for (const auto& config_metadata : config_metadatas) {
configuration_json.emplace_back(
generate_configuration_field(config_metadata));
}
}

/// NOTE(@dmehala): Telemetry API is tightly related to APM tracing and
Expand Down
10 changes: 6 additions & 4 deletions src/datadog/trace_sampler_config.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -225,10 +225,12 @@ Expected<FinalizedTraceSamplerConfig> finalize_config(
result.rules.emplace_back(std::move(finalized_rule));
}

const auto [origin, max_per_second] =
pick(env_config->max_per_second, config.max_per_second, 100);
result.metadata[ConfigName::TRACE_SAMPLING_LIMIT] = ConfigMetadata(
ConfigName::TRACE_SAMPLING_LIMIT, std::to_string(max_per_second), origin);
std::unordered_map<ConfigName, std::vector<ConfigMetadata>>
telemetry_configs_tmp;
double max_per_second = pick(
env_config->max_per_second, config.max_per_second, &telemetry_configs_tmp,
&result.metadata, ConfigName::TRACE_SAMPLING_LIMIT, 100.0,
[](const double &d) { return std::to_string(d); });

const auto allowed_types = {FP_NORMAL, FP_SUBNORMAL};
if (!(max_per_second > 0) ||
Expand Down
156 changes: 75 additions & 81 deletions src/datadog/tracer_config.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,13 @@
#include <unordered_map>
#include <vector>

#include "datadog/optional.h"
#include "datadog_agent.h"
#include "json.hpp"
#include "null_logger.h"
#include "parse_util.h"
#include "platform_util.h"
#include "string_util.h"
#include "tags.h"
#include "threaded_event_scheduler.h"

namespace datadog {
Expand Down Expand Up @@ -284,81 +284,81 @@ Expected<FinalizedTracerConfig> finalize_config(const TracerConfig &user_config,
final_config.clock = clock;
final_config.logger = logger;

ConfigMetadata::Origin origin;
std::unordered_map<ConfigName, std::vector<ConfigMetadata>>
all_sources_configs;

std::tie(origin, final_config.defaults.service) =
pick(env_config->service, user_config.service, "");

if (final_config.defaults.service.empty()) {
final_config.defaults.service = get_process_name();
}

final_config.metadata[ConfigName::SERVICE_NAME] = ConfigMetadata(
ConfigName::SERVICE_NAME, final_config.defaults.service, origin);
// DD_SERVICE
final_config.defaults.service = pick(
env_config->service, user_config.service, &all_sources_configs,
&final_config.metadata, ConfigName::SERVICE_NAME, get_process_name());

// Service type
final_config.defaults.service_type =
value_or(env_config->service_type, user_config.service_type, "web");

// DD_ENV
std::tie(origin, final_config.defaults.environment) =
pick(env_config->environment, user_config.environment, "");
final_config.metadata[ConfigName::SERVICE_ENV] = ConfigMetadata(
ConfigName::SERVICE_ENV, final_config.defaults.environment, origin);
final_config.defaults.environment = pick(
env_config->environment, user_config.environment, &all_sources_configs,
&final_config.metadata, ConfigName::SERVICE_ENV);

// DD_VERSION
std::tie(origin, final_config.defaults.version) =
pick(env_config->version, user_config.version, "");
final_config.metadata[ConfigName::SERVICE_VERSION] = ConfigMetadata(
ConfigName::SERVICE_VERSION, final_config.defaults.version, origin);
final_config.defaults.version =
pick(env_config->version, user_config.version, &all_sources_configs,
&final_config.metadata, ConfigName::SERVICE_VERSION);

// Span name
final_config.defaults.name = value_or(env_config->name, user_config.name, "");

// DD_TAGS
std::tie(origin, final_config.defaults.tags) =
pick(env_config->tags, user_config.tags,
std::unordered_map<std::string, std::string>{});
final_config.metadata[ConfigName::TAGS] = ConfigMetadata(
ConfigName::TAGS, join_tags(final_config.defaults.tags), origin);
final_config.defaults.tags =
pick(env_config->tags, user_config.tags, &all_sources_configs,
&final_config.metadata, ConfigName::TAGS,
std::unordered_map<std::string, std::string>{},
[](const auto &tags) { return join_tags(tags); });

// Extraction Styles
const std::vector<PropagationStyle> default_propagation_styles{
PropagationStyle::DATADOG, PropagationStyle::W3C,
PropagationStyle::BAGGAGE};

std::tie(origin, final_config.extraction_styles) =
final_config.extraction_styles =
pick(env_config->extraction_styles, user_config.extraction_styles,
default_propagation_styles);
&all_sources_configs, &final_config.metadata,
ConfigName::EXTRACTION_STYLES, default_propagation_styles,
[](const std::vector<PropagationStyle> &styles) {
return join_propagation_styles(styles);
});

if (final_config.extraction_styles.empty()) {
return Error{Error::MISSING_SPAN_EXTRACTION_STYLE,
"At least one extraction style must be specified."};
}
final_config.metadata[ConfigName::EXTRACTION_STYLES] = ConfigMetadata(
ConfigName::EXTRACTION_STYLES,
join_propagation_styles(final_config.extraction_styles), origin);

// Injection Styles
std::tie(origin, final_config.injection_styles) =
final_config.injection_styles =
pick(env_config->injection_styles, user_config.injection_styles,
default_propagation_styles);
&all_sources_configs, &final_config.metadata,
ConfigName::INJECTION_STYLES, default_propagation_styles,
[](const std::vector<PropagationStyle> &styles) {
return join_propagation_styles(styles);
});

if (final_config.injection_styles.empty()) {
return Error{Error::MISSING_SPAN_INJECTION_STYLE,
"At least one injection style must be specified."};
}
final_config.metadata[ConfigName::INJECTION_STYLES] = ConfigMetadata(
ConfigName::INJECTION_STYLES,
join_propagation_styles(final_config.injection_styles), origin);

// Startup Logs
std::tie(origin, final_config.log_on_startup) =
pick(env_config->log_on_startup, user_config.log_on_startup, true);
final_config.metadata[ConfigName::STARTUP_LOGS] = ConfigMetadata(
ConfigName::STARTUP_LOGS, to_string(final_config.log_on_startup), origin);
final_config.log_on_startup = pick(
env_config->log_on_startup, user_config.log_on_startup,
&all_sources_configs, &final_config.metadata, ConfigName::STARTUP_LOGS,
true, [](const bool &b) { return to_string(b); });

// Report traces
std::tie(origin, final_config.report_traces) =
pick(env_config->report_traces, user_config.report_traces, true);
final_config.metadata[ConfigName::REPORT_TRACES] = ConfigMetadata(
ConfigName::REPORT_TRACES, to_string(final_config.report_traces), origin);
final_config.report_traces = pick(
env_config->report_traces, user_config.report_traces,
&all_sources_configs, &final_config.metadata, ConfigName::REPORT_TRACES,
true, [](const bool &b) { return to_string(b); });

// Report hostname
final_config.report_hostname =
Expand All @@ -369,12 +369,11 @@ Expected<FinalizedTracerConfig> finalize_config(const TracerConfig &user_config,
env_config->max_tags_header_size, user_config.max_tags_header_size, 512);

// 128b Trace IDs
std::tie(origin, final_config.generate_128bit_trace_ids) =
final_config.generate_128bit_trace_ids =
pick(env_config->generate_128bit_trace_ids,
user_config.generate_128bit_trace_ids, true);
final_config.metadata[ConfigName::GENEREATE_128BIT_TRACE_IDS] =
ConfigMetadata(ConfigName::GENEREATE_128BIT_TRACE_IDS,
to_string(final_config.generate_128bit_trace_ids), origin);
user_config.generate_128bit_trace_ids, &all_sources_configs,
&final_config.metadata, ConfigName::GENEREATE_128BIT_TRACE_IDS, true,
[](const bool &b) { return to_string(b); });

// Integration name & version
final_config.integration_name = value_or(
Expand All @@ -383,18 +382,19 @@ Expected<FinalizedTracerConfig> finalize_config(const TracerConfig &user_config,
value_or(env_config->integration_version, user_config.integration_version,
tracer_version);

// Baggage
std::tie(origin, final_config.baggage_opts.max_items) =
pick(env_config->baggage_max_items, user_config.baggage_max_items, 64);
final_config.metadata[ConfigName::TRACE_BAGGAGE_MAX_ITEMS] = ConfigMetadata(
ConfigName::TRACE_BAGGAGE_MAX_ITEMS,
std::to_string(final_config.baggage_opts.max_items), origin);

std::tie(origin, final_config.baggage_opts.max_bytes) =
pick(env_config->baggage_max_bytes, user_config.baggage_max_bytes, 8192);
final_config.metadata[ConfigName::TRACE_BAGGAGE_MAX_BYTES] = ConfigMetadata(
ConfigName::TRACE_BAGGAGE_MAX_BYTES,
std::to_string(final_config.baggage_opts.max_bytes), origin);
// Baggage - max items
final_config.baggage_opts.max_items =
pick(env_config->baggage_max_items, user_config.baggage_max_items,
&all_sources_configs, &final_config.metadata,
ConfigName::TRACE_BAGGAGE_MAX_ITEMS, 64UL,
[](const size_t &i) { return std::to_string(i); });

// Baggage - max bytes
final_config.baggage_opts.max_bytes =
pick(env_config->baggage_max_bytes, user_config.baggage_max_bytes,
&all_sources_configs, &final_config.metadata,
ConfigName::TRACE_BAGGAGE_MAX_BYTES, 8192UL,
[](const size_t &i) { return std::to_string(i); });

if (final_config.baggage_opts.max_items <= 0 ||
final_config.baggage_opts.max_bytes < 3) {
Expand Down Expand Up @@ -453,39 +453,33 @@ Expected<FinalizedTracerConfig> finalize_config(const TracerConfig &user_config,
final_config.telemetry = std::move(*telemetry_final_config);
final_config.telemetry.products.emplace_back(telemetry::Product{
telemetry::Product::Name::tracing, true, tracer_version, nullopt,
nullopt, final_config.metadata});
nullopt, all_sources_configs});
} else {
return std::move(telemetry_final_config.error());
}

// APM Tracing Enabled
std::tie(origin, final_config.tracing_enabled) =
pick(env_config->tracing_enabled, user_config.tracing_enabled, true);
final_config.metadata[ConfigName::APM_TRACING_ENABLED] =
ConfigMetadata(ConfigName::APM_TRACING_ENABLED,
to_string(final_config.tracing_enabled), origin);
final_config.tracing_enabled =
pick(env_config->tracing_enabled, user_config.tracing_enabled,
&all_sources_configs, &final_config.metadata,
ConfigName::APM_TRACING_ENABLED, true,
[](const bool &b) { return to_string(b); });

{
// Resource Renaming Enabled
bool resource_renaming_enabled;
std::tie(origin, resource_renaming_enabled) =
pick(env_config->resource_renaming_enabled,
user_config.resource_renaming_enabled, false);

final_config.metadata[ConfigName::TRACE_RESOURCE_RENAMING_ENABLED] =
ConfigMetadata(ConfigName::TRACE_RESOURCE_RENAMING_ENABLED,
to_string(resource_renaming_enabled), origin);
const bool resource_renaming_enabled = pick(
env_config->resource_renaming_enabled,
user_config.resource_renaming_enabled, &all_sources_configs,
&final_config.metadata, ConfigName::TRACE_RESOURCE_RENAMING_ENABLED,
false, [](const bool &b) { return to_string(b); });

// Resource Renaming Always Simplified Endpoint
bool resource_renaming_always_simplified_endpoint;
std::tie(origin, resource_renaming_always_simplified_endpoint) =
const bool resource_renaming_always_simplified_endpoint =
pick(env_config->resource_renaming_always_simplified_endpoint,
user_config.resource_renaming_always_simplified_endpoint, false);
final_config.metadata
[ConfigName::TRACE_RESOURCE_RENAMING_ALWAYS_SIMPLIFIED_ENDPOINT] =
ConfigMetadata(
ConfigName::TRACE_RESOURCE_RENAMING_ALWAYS_SIMPLIFIED_ENDPOINT,
to_string(resource_renaming_always_simplified_endpoint), origin);
user_config.resource_renaming_always_simplified_endpoint,
&all_sources_configs, &final_config.metadata,
ConfigName::TRACE_RESOURCE_RENAMING_ALWAYS_SIMPLIFIED_ENDPOINT,
false, [](const bool &b) { return to_string(b); });

if (!resource_renaming_enabled) {
final_config.resource_renaming_mode =
Expand Down
Loading