Skip to content
Draft
Show file tree
Hide file tree
Changes from all 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
5 changes: 5 additions & 0 deletions BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ cc_library(
"src/datadog/common/hash.h",
"src/datadog/config_manager.cpp",
"src/datadog/config_manager.h",
"src/datadog/config_provider.cpp",
"src/datadog/config_provider.h",
"src/datadog/config_source.h",
"src/datadog/datadog_agent.cpp",
"src/datadog/datadog_agent.h",
"src/datadog/datadog_agent_config.cpp",
Expand All @@ -22,6 +25,8 @@ cc_library(
"src/datadog/endpoint_inferral.cpp",
"src/datadog/endpoint_inferral.h",
"src/datadog/environment.cpp",
"src/datadog/environment_source.cpp",
"src/datadog/environment_source.h",
"src/datadog/error.cpp",
"src/datadog/extracted_data.h",
"src/datadog/extraction_util.cpp",
Expand Down
2 changes: 2 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -181,11 +181,13 @@ target_sources(dd-trace-cpp-objects
src/datadog/cerr_logger.cpp
src/datadog/clock.cpp
src/datadog/config_manager.cpp
src/datadog/config_provider.cpp
src/datadog/collector_response.cpp
src/datadog/datadog_agent_config.cpp
src/datadog/datadog_agent.cpp
src/datadog/endpoint_inferral.cpp
src/datadog/environment.cpp
src/datadog/environment_source.cpp
src/datadog/error.cpp
src/datadog/extraction_util.cpp
src/datadog/glob.cpp
Expand Down
82 changes: 6 additions & 76 deletions include/datadog/config.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,9 @@ struct ConfigMetadata {
ENVIRONMENT_VARIABLE, // Originating from environment variables
CODE, // Defined in code
REMOTE_CONFIG, // Retrieved from remote configuration
DEFAULT // Default value
DEFAULT, // Default value
LOCAL_STABLE_CONFIG, // Local stable configuration file
FLEET_STABLE_CONFIG, // Fleet-managed stable configuration file
};

// Name of the configuration parameter
Expand All @@ -55,88 +57,16 @@ struct ConfigMetadata {
Origin origin;
// Optional error associated with the configuration parameter
Optional<Error> error;
// Optional config identifier (set when the value came from a fleet
// stable config file that included `config_id: ...`).
Optional<std::string> config_id;

ConfigMetadata() = default;
ConfigMetadata(ConfigName n, std::string v, Origin orig,
Optional<Error> err = nullopt)
: name(n), value(std::move(v)), origin(orig), error(std::move(err)) {}
};

// Returns the final configuration value using the following
// precedence order: environment > user code > default, and populates metadata:
// `metadata`: Records ALL configuration sources that were provided,
// ordered from lowest to highest precedence. The last entry has the highest
// precedence and is the winning value.
//
// Template Parameters:
// Value: The type of the configuration value
// Stringifier: Optional function type to convert Value to string
// (defaults to std::nullptr_t, which uses string construction)
// DefaultValue: Type of the fallback value (defaults to std::nullptr_t)
//
// Parameters:
// from_env: Optional value from environment variables (highest precedence)
// from_user: Optional value from user code (middle precedence)
// metadata: Output map that will be populated with all config sources found
// for this config_name, in precedence order (last = highest)
// config_name: The configuration parameter name identifier
// fallback: Optional default value (lowest precedence). Pass nullptr to
// indicate no default.
// to_string_fn: Optional custom function to convert Value to string.
// Required for non-string types. For string-like types, uses
// default string construction if not provided.
//
// Returns:
// The chosen configuration value based on precedence, or Value{} if no value
// was provided.
template <typename Value, typename Stringifier = std::nullptr_t,
typename DefaultValue = std::nullptr_t>
Value resolve_and_record_config(
const Optional<Value>& from_env, const Optional<Value>& from_user,
std::unordered_map<ConfigName, std::vector<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> metadata_entries;
Optional<Value> chosen_value;

auto add_entry = [&](ConfigMetadata::Origin origin, const Value& val) {
std::string val_str = stringify(val);
metadata_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);
}

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

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
Expand Down
8 changes: 8 additions & 0 deletions include/datadog/environment.h
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,14 @@ StringView name(Variable variable);
// `nullopt` if that variable is not set in the environment.
Optional<StringView> lookup(Variable variable);

// Return the value of the environment variable named `name`, or
// `nullopt` if that variable is not set in the environment. Use this
// overload when the variable name is known at runtime (e.g. inside
// `ConfigSource` implementations) rather than at compile time. The
// returned `StringView` borrows from `getenv`'s thread-local storage
// and is valid until the next environment mutation.
Optional<StringView> lookup(StringView name);

std::string to_json();

} // namespace environment
Expand Down
9 changes: 9 additions & 0 deletions include/datadog/propagation_style.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@
// one `std::vector<PropagationStyle>` for extraction and another for injection.
// See `tracer_config.h`.

#include <vector>

#include "expected.h"
#include "optional.h"
#include "string_view.h"

Expand All @@ -30,5 +33,11 @@ StringView to_string_view(PropagationStyle style);

Optional<PropagationStyle> parse_propagation_style(StringView text);

// Parse a comma-or-space-separated list of propagation style names into a
// vector of `PropagationStyle`. Returns an error if any item is unknown
// or if a style is duplicated.
Expected<std::vector<PropagationStyle>> parse_propagation_styles(
StringView input);

} // namespace tracing
} // namespace datadog
139 changes: 139 additions & 0 deletions src/datadog/config_provider.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
#include "config_provider.h"

#include <datadog/expected.h>
#include <datadog/logger.h>

#include <ostream>
#include <utility>

#include "config_source.h"
#include "parse_util.h"
#include "string_util.h"

namespace datadog {
namespace tracing {

void ConfigProvider::record_entry(std::vector<ConfigMetadata>& entries,
ConfigName name, std::string value,
ConfigMetadata::Origin origin,
const Optional<std::string>& config_id) {
ConfigMetadata md(name, std::move(value), origin);
if (config_id) {
md.config_id = *config_id;
}
entries.emplace_back(std::move(md));
}

ConfigProvider::ConfigProvider(
const ConfigSource* fleet, const ConfigSource* env,
const ConfigSource* local,
std::unordered_map<ConfigName, std::vector<ConfigMetadata>>* metadata)
: fleet_(fleet), env_(env), local_(local), metadata_(metadata) {}

std::string ConfigProvider::get_string(ConfigName name, StringView env_key,
const Optional<std::string>& user_value,
std::string default_value) {
auto parse = [](const std::string& s) -> Expected<std::string> { return s; };
auto stringify = [](const std::string& s) { return s; };
return get<std::string>(name, env_key, user_value, std::move(default_value),
parse, stringify, nullptr);
}

bool ConfigProvider::get_bool(ConfigName name, StringView env_key,
const Optional<bool>& user_value,
bool default_value) {
auto parse = [](const std::string& s) -> Expected<bool> {
return !falsy(StringView(s));
};
auto stringify = [](const bool& b) { return to_string(b); };
return get<bool>(name, env_key, user_value, default_value, parse, stringify,
nullptr);
}

std::size_t ConfigProvider::get_uint64(ConfigName name, StringView env_key,
const Optional<std::size_t>& user_value,
std::size_t default_value,
Logger& logger) {
auto parse = [](const std::string& s) -> Expected<std::size_t> {
auto r = parse_uint64(StringView(s), 10);
if (auto* err = r.if_error()) return *err;
return static_cast<std::size_t>(*r);
};
auto stringify = [](const std::size_t& v) { return std::to_string(v); };
return get<std::size_t>(name, env_key, user_value, default_value, parse,
stringify, &logger);
}

double ConfigProvider::get_double(ConfigName name, StringView env_key,
const Optional<double>& user_value,
double default_value, Logger& logger) {
auto parse = [](const std::string& s) -> Expected<double> {
return parse_double(StringView(s));
};
auto stringify = [](const double& v) { return to_string(v, 1); };
return get<double>(name, env_key, user_value, default_value, parse, stringify,
&logger);
}

std::unordered_map<std::string, std::string> ConfigProvider::get_tags(
ConfigName name, StringView env_key,
const Optional<std::unordered_map<std::string, std::string>>& user_value,
std::unordered_map<std::string, std::string> default_value,
Logger& logger) {
using TagMap = std::unordered_map<std::string, std::string>;
auto parse = [](const std::string& s) -> Expected<TagMap> {
return parse_tags(StringView(s));
};
auto stringify = [](const TagMap& m) { return join_tags(m); };
return get<TagMap>(name, env_key, user_value, std::move(default_value), parse,
stringify, &logger);
}

std::vector<PropagationStyle> ConfigProvider::get_propagation_styles(
ConfigName name, StringView env_key,
const Optional<std::vector<PropagationStyle>>& user_value,
std::vector<PropagationStyle> default_value, Logger& logger) {
using StyleList = std::vector<PropagationStyle>;
auto parse = [](const std::string& s) -> Expected<StyleList> {
return parse_propagation_styles(StringView(s));
};
auto stringify = [](const StyleList& s) {
return join_propagation_styles(s);
};
return get<StyleList>(name, env_key, user_value, std::move(default_value),
parse, stringify, &logger);
}

std::vector<PropagationStyle>
ConfigProvider::get_propagation_styles_with_aliases(
ConfigName name, std::initializer_list<StringView> env_keys,
const Optional<std::vector<PropagationStyle>>& user_value,
std::vector<PropagationStyle> default_value, Logger& logger) {
// Try each env_key in order. Use the first that any source supplies.
// Behavior detail: each call records its own metadata entries; only
// the call that produced a non-default winner survives in the final
// map (the others contribute their DEFAULT entry, then overwritten).
// To avoid metadata noise, peek the env source first and skip empty
// keys before delegating.
for (auto env_key : env_keys) {
bool any_source_has_value = false;
if (fleet_ && fleet_->lookup(env_key))
any_source_has_value = true;
else if (env_ && env_->lookup(env_key))
any_source_has_value = true;
else if (local_ && local_->lookup(env_key))
any_source_has_value = true;
if (any_source_has_value) {
return get_propagation_styles(name, env_key, user_value,
std::move(default_value), logger);
}
}
// No source supplied any key. Fall through to user_value or default
// via the regular accessor using the first (canonical) key. This
// also records a DEFAULT entry for telemetry.
return get_propagation_styles(name, *env_keys.begin(), user_value,
std::move(default_value), logger);
}

} // namespace tracing
} // namespace datadog
Loading
Loading