diff --git a/src/datadog/datadog_agent.cpp b/src/datadog/datadog_agent.cpp index 312d60a2..507f69dd 100644 --- a/src/datadog/datadog_agent.cpp +++ b/src/datadog/datadog_agent.cpp @@ -18,7 +18,6 @@ #include "string_view.h" #include "trace_sampler.h" #include "tracer.h" -#include "version.h" namespace datadog { namespace tracing { @@ -163,7 +162,8 @@ DatadogAgent::DatadogAgent( flush_interval_(config.flush_interval), request_timeout_(config.request_timeout), shutdown_timeout_(config.shutdown_timeout), - remote_config_(tracer_signature, config_manager) { + remote_config_(tracer_signature, config_manager), + tracer_signature_(tracer_signature) { assert(logger_); assert(tracer_telemetry_); @@ -290,8 +290,10 @@ void DatadogAgent::flush() { auto set_request_headers = [&](DictWriter& headers) { headers.set("Content-Type", "application/msgpack"); headers.set("Datadog-Meta-Lang", "cpp"); - headers.set("Datadog-Meta-Lang-Version", std::to_string(__cplusplus)); - headers.set("Datadog-Meta-Tracer-Version", tracer_version); + headers.set("Datadog-Meta-Lang-Version", + tracer_signature_.library_language_version); + headers.set("Datadog-Meta-Tracer-Version", + tracer_signature_.library_version); headers.set("X-Datadog-Trace-Count", std::to_string(trace_chunks.size())); }; @@ -366,9 +368,30 @@ void DatadogAgent::flush() { } } -void DatadogAgent::send_telemetry(std::string payload) { +void DatadogAgent::send_telemetry(StringView request_type, + std::string payload) { + auto set_telemetry_headers = [request_type, payload_size = payload.size(), + debug_enabled = tracer_telemetry_->debug(), + tracer_signature = + &tracer_signature_](DictWriter& headers) { + /* + TODO: + Datadog-Container-ID + */ + headers.set("Content-Type", "application/json"); + headers.set("Content-Length", std::to_string(payload_size)); + headers.set("DD-Telemetry-API-Version", "v2"); + headers.set("DD-Client-Library-Language", "cpp"); + headers.set("DD-Client-Library-Version", tracer_signature->library_version); + headers.set("DD-Telemetry-Request-Type", request_type); + + if (debug_enabled) { + headers.set("DD-Telemetry-Debug-Enabled", "true"); + } + }; + auto post_result = - http_client_->post(telemetry_endpoint_, set_content_type_json, + http_client_->post(telemetry_endpoint_, set_telemetry_headers, std::move(payload), telemetry_on_response_, telemetry_on_error_, clock_().tick + request_timeout_); if (auto* error = post_result.if_error()) { @@ -379,20 +402,22 @@ void DatadogAgent::send_telemetry(std::string payload) { void DatadogAgent::send_app_started( const std::unordered_map& config_metadata) { - send_telemetry(tracer_telemetry_->app_started(config_metadata)); + send_telemetry("app-started", + tracer_telemetry_->app_started(config_metadata)); } void DatadogAgent::send_heartbeat_and_telemetry() { - send_telemetry(tracer_telemetry_->heartbeat_and_telemetry()); + send_telemetry("app-heartbeat", tracer_telemetry_->heartbeat_and_telemetry()); } void DatadogAgent::send_app_closing() { - send_telemetry(tracer_telemetry_->app_closing()); + send_telemetry("app-closing", tracer_telemetry_->app_closing()); } void DatadogAgent::send_configuration_change( const std::vector& config) { - send_telemetry(tracer_telemetry_->configuration_change(config)); + send_telemetry("app-client-configuration-change", + tracer_telemetry_->configuration_change(config)); } void DatadogAgent::get_and_apply_remote_configuration_updates() { diff --git a/src/datadog/datadog_agent.h b/src/datadog/datadog_agent.h index a9687146..592dc71b 100644 --- a/src/datadog/datadog_agent.h +++ b/src/datadog/datadog_agent.h @@ -17,6 +17,7 @@ #include "http_client.h" #include "metrics.h" #include "remote_config.h" +#include "tracer_signature.h" #include "tracer_telemetry.h" namespace datadog { @@ -55,9 +56,10 @@ class DatadogAgent : public Collector { std::chrono::steady_clock::duration shutdown_timeout_; RemoteConfigurationManager remote_config_; + TracerSignature tracer_signature_; void flush(); - void send_telemetry(std::string); + void send_telemetry(StringView, std::string); void send_heartbeat_and_telemetry(); void send_app_closing(); diff --git a/src/datadog/platform_util.cpp b/src/datadog/platform_util.cpp index c7c2c8b1..f074fbe0 100644 --- a/src/datadog/platform_util.cpp +++ b/src/datadog/platform_util.cpp @@ -1,41 +1,114 @@ #include "platform_util.h" -#ifdef _MSC_VER -#include -#include -#else #include +#include +#include #include + +#include + +#include "string_util.h" + +#if defined(__x86_64__) || defined(_M_X64) +#define DD_SDK_CPU_ARCH "x86_64" +#elif defined(i386) || defined(__i386__) || defined(__i386) || defined(_M_IX86) +#define DD_SDK_CPU_ARCH "x86" +#elif defined(__aarch64__) || defined(_M_ARM64) +#define DD_SDK_CPU_ARCH "arm64" +#else +#define DD_SDK_CPU_ARCH "unknown" +#endif + +#if defined(__APPLE__) +#include +#define DD_SDK_OS "Darwin" +#define DD_SDK_KERNEL "Darwin" +#elif defined(__linux__) || defined(__unix__) +#define DD_SDK_OS "GNU/Linux" +#define DD_SDK_KERNEL "Linux" +#else +#define DD_SDK_OS "unknown" #endif namespace datadog { namespace tracing { +namespace { -Optional get_hostname() { - char buffer[256]; - if (::gethostname(buffer, sizeof buffer)) { - return nullopt; - } - return buffer; +#if defined(__APPLE__) +std::string get_os_version() { + char os_version[20] = ""; + size_t len = sizeof(os_version); + + sysctlbyname("kern.osproductversion", os_version, &len, NULL, 0); + return os_version; } +#elif defined(__linux__) +std::string get_os_version() { + std::ifstream os_release_file("/etc/os-release"); + if (!os_release_file.is_open()) { + return ""; + } + + std::string line; -int get_process_id() { -#ifdef _MSC_VER - return GetCurrentProcessId(); + while (std::getline(os_release_file, line)) { + size_t pos = line.find('='); + if (pos == std::string::npos) { + continue; + } + + std::string key = line.substr(0, pos); + to_lower(key); + if (key == "version") { + std::string value = line.substr(pos + 1); + return value; + } + } + + return ""; +} #else - return ::getpid(); +std::string get_os_version() { return ""; } #endif + +#if defined(__APPLE__) || defined(__linux__) + +HostInfo _get_host_info() { + HostInfo res; + + struct utsname buffer; + if (uname(&buffer) != 0) { + return res; + } + + res.os = DD_SDK_OS; + res.os_version = get_os_version(); + res.hostname = buffer.nodename; + res.cpu_architecture = DD_SDK_CPU_ARCH; + res.kernel_name = DD_SDK_KERNEL; + res.kernel_version = buffer.version; + res.kernel_release = buffer.release; + + return res; } +#endif + +} // namespace + +HostInfo get_host_info() { + static const HostInfo host_info = _get_host_info(); + return host_info; +} + +std::string get_hostname() { return get_host_info().hostname; } + +int get_process_id() { return ::getpid(); } + int at_fork_in_child(void (*on_fork)()) { -// Windows does not have `fork`, and so this is not relevant there. -#ifdef _MSC_VER - return 0; -#else // https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_atfork.html return pthread_atfork(/*before fork*/ nullptr, /*in parent*/ nullptr, /*in child*/ on_fork); -#endif } } // namespace tracing diff --git a/src/datadog/platform_util.h b/src/datadog/platform_util.h index 37ce3923..7e1cfb75 100644 --- a/src/datadog/platform_util.h +++ b/src/datadog/platform_util.h @@ -4,12 +4,25 @@ #include -#include "optional.h" - namespace datadog { namespace tracing { -Optional get_hostname(); +// Hold host information mainly used for telemetry purposes +// and for identifying a tracer. +struct HostInfo final { + std::string os; + std::string os_version; + std::string hostname; + std::string cpu_architecture; + std::string kernel_name; + std::string kernel_version; + std::string kernel_release; +}; + +// Returns host information. Lazy. +HostInfo get_host_info(); + +std::string get_hostname(); int get_process_id(); diff --git a/src/datadog/tracer.cpp b/src/datadog/tracer.cpp index 583ecd74..2df6d4ba 100644 --- a/src/datadog/tracer.cpp +++ b/src/datadog/tracer.cpp @@ -51,9 +51,11 @@ Tracer::Tracer(const FinalizedTracerConfig& config, clock_(config.clock), injection_styles_(config.injection_styles), extraction_styles_(config.extraction_styles), - hostname_(config.report_hostname ? get_hostname() : nullopt), tags_header_max_size_(config.tags_header_size), sampling_delegation_enabled_(config.delegate_trace_sampling) { + if (config.report_hostname) { + hostname_ = get_hostname(); + } if (auto* collector = std::get_if>(&config.collector)) { collector_ = *collector; diff --git a/src/datadog/tracer_telemetry.cpp b/src/datadog/tracer_telemetry.cpp index d58ab82e..2e3adaad 100644 --- a/src/datadog/tracer_telemetry.cpp +++ b/src/datadog/tracer_telemetry.cpp @@ -60,8 +60,8 @@ TracerTelemetry::TracerTelemetry(bool enabled, const Clock& clock, : enabled_(enabled), clock_(clock), logger_(logger), + host_info_(get_host_info()), tracer_signature_(tracer_signature), - hostname_(get_hostname().value_or("hostname-unavailable")), integration_name_(integration_name), integration_version_(integration_version) { if (enabled_) { @@ -120,10 +120,16 @@ nlohmann::json TracerTelemetry::generate_telemetry_body( {"language_name", tracer_signature_.library_language}, {"language_version", tracer_signature_.library_language_version}, })}, - // TODO: host information (os, os_version, kernel, etc) - {"host", nlohmann::json::object({ - {"hostname", hostname_}, - })}, + {"host", + { + {"hostname", host_info_.hostname}, + {"os", host_info_.os}, + {"os_version", host_info_.os_version}, + {"architecture", host_info_.cpu_architecture}, + {"kernel_name", host_info_.kernel_name}, + {"kernel_version", host_info_.kernel_version}, + {"kernel_release", host_info_.kernel_release}, + }}, }); } diff --git a/src/datadog/tracer_telemetry.h b/src/datadog/tracer_telemetry.h index 6a6ffb33..3911ba5d 100644 --- a/src/datadog/tracer_telemetry.h +++ b/src/datadog/tracer_telemetry.h @@ -34,6 +34,7 @@ #include "config.h" #include "json.hpp" #include "metrics.h" +#include "platform_util.h" #include "runtime_id.h" #include "tracer_signature.h" @@ -48,8 +49,8 @@ class TracerTelemetry { bool debug_ = false; Clock clock_; std::shared_ptr logger_; + HostInfo host_info_; TracerSignature tracer_signature_; - std::string hostname_; std::string integration_name_; std::string integration_version_; // Track sequence id per payload generated @@ -115,10 +116,11 @@ class TracerTelemetry { const TracerSignature& tracer_signature, const std::string& integration_name, const std::string& integration_version); - bool enabled() { return enabled_; }; + inline bool enabled() { return enabled_; } + inline bool debug() { return debug_; } // Provides access to the telemetry metrics for updating the values. // This value should not be stored. - auto& metrics() { return metrics_; }; + auto& metrics() { return metrics_; } // Constructs an `app-started` message using information provided when // constructed and the tracer_config value passed in. std::string app_started(