Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Report host information in telemetry #118

Merged
merged 2 commits into from
May 20, 2024
Merged
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
45 changes: 35 additions & 10 deletions src/datadog/datadog_agent.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
#include "string_view.h"
#include "trace_sampler.h"
#include "tracer.h"
#include "version.h"

namespace datadog {
namespace tracing {
Expand Down Expand Up @@ -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_);

Expand Down Expand Up @@ -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()));
};

Expand Down Expand Up @@ -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
*/
dmehala marked this conversation as resolved.
Show resolved Hide resolved
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()) {
Expand All @@ -379,20 +402,22 @@ void DatadogAgent::send_telemetry(std::string payload) {

void DatadogAgent::send_app_started(
const std::unordered_map<ConfigName, ConfigMetadata>& 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<ConfigMetadata>& 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() {
Expand Down
4 changes: 3 additions & 1 deletion src/datadog/datadog_agent.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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();

Expand Down
111 changes: 92 additions & 19 deletions src/datadog/platform_util.cpp
Original file line number Diff line number Diff line change
@@ -1,41 +1,114 @@
#include "platform_util.h"

#ifdef _MSC_VER
#include <processthreadsapi.h>
#include <winsock.h>
#else
#include <pthread.h>
#include <sys/types.h>
#include <sys/utsname.h>
#include <unistd.h>

#include <fstream>

#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 <sys/sysctl.h>
#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<std::string> 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
Expand Down
19 changes: 16 additions & 3 deletions src/datadog/platform_util.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,25 @@

#include <string>

#include "optional.h"

namespace datadog {
namespace tracing {

Optional<std::string> 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();

Expand Down
4 changes: 3 additions & 1 deletion src/datadog/tracer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<std::shared_ptr<Collector>>(&config.collector)) {
collector_ = *collector;
Expand Down
16 changes: 11 additions & 5 deletions src/datadog/tracer_telemetry.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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_) {
Expand Down Expand Up @@ -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},
}},
});
}

Expand Down
8 changes: 5 additions & 3 deletions src/datadog/tracer_telemetry.h
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand All @@ -48,8 +49,8 @@ class TracerTelemetry {
bool debug_ = false;
Clock clock_;
std::shared_ptr<Logger> logger_;
HostInfo host_info_;
TracerSignature tracer_signature_;
std::string hostname_;
std::string integration_name_;
std::string integration_version_;
// Track sequence id per payload generated
Expand Down Expand Up @@ -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(
Expand Down
Loading