Skip to content
Open
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
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions datadog-sidecar-ffi/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -515,6 +515,8 @@ pub unsafe extern "C" fn ddog_sidecar_telemetry_addDependency(
let dependency = TelemetryActions::AddDependency(Dependency {
name: dependency_name.to_utf8_lossy().into_owned(),
version,
hash: None,
metadata: None,
});

try_c!(blocking::enqueue_actions(
Expand Down
10 changes: 10 additions & 0 deletions libdd-data-pipeline/src/telemetry/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,16 @@ pub struct TelemetryClient {
worker: TelemetryWorkerHandle,
}

impl TelemetryClient {
/// Allow sharing a telemetry worker with data-pipeline
pub fn with_handle(handle: TelemetryWorkerHandle) -> Self {
TelemetryClient {
metrics: Metrics::new(&handle),
worker: handle,
}
}
}

/// Telemetry describing the sending of a trace payload
/// It can be produced from a [`SendWithRetryResult`] or from a [`SendDataResult`].
#[derive(PartialEq, Debug, Default)]
Expand Down
111 changes: 64 additions & 47 deletions libdd-data-pipeline/src/trace_exporter/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use crate::agentless::config::{AgentlessTraceConfig, DEFAULT_AGENTLESS_TIMEOUT};
use crate::otlp::config::{OtlpProtocol, DEFAULT_OTLP_TIMEOUT};
use crate::otlp::{OtlpMetricsConfig, OtlpResourceInfo, OtlpTraceConfig};
#[cfg(all(not(target_arch = "wasm32"), feature = "telemetry"))]
use crate::telemetry::TelemetryClientBuilder;
use crate::telemetry::{TelemetryClient, TelemetryClientBuilder};
use crate::trace_exporter::agent_response::AgentResponsePayloadVersion;
use crate::trace_exporter::error::BuilderErrorKind;
use crate::trace_exporter::log_writer::DEFAULT_LOG_MAX_LINE_SIZE;
Expand All @@ -26,6 +26,8 @@ use libdd_dogstatsd_client::new;
use libdd_shared_runtime::SharedRuntime;
#[cfg(not(target_arch = "wasm32"))]
use libdd_shared_runtime::{BlockingRuntime, ForkSafeRuntime};
#[cfg(all(not(target_arch = "wasm32"), feature = "telemetry"))]
use libdd_telemetry::worker::TelemetryWorkerHandle;
use libdd_trace_utils::trace_filter::TraceFilterer;
use std::sync::Arc;
use std::time::Duration;
Expand Down Expand Up @@ -80,6 +82,8 @@ pub struct TraceExporterBuilder<R: SharedRuntime> {
client_side_stats_obfuscation_enabled: bool,
#[cfg(feature = "telemetry")]
telemetry: Option<TelemetryConfig>,
#[cfg(feature = "telemetry")]
telemetry_handle: Option<TelemetryWorkerHandle>,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Initialize the telemetry handle field in the builder

With the default telemetry feature enabled, this newly added field is never initialized by TraceExporterBuilder::new(), so constructing the builder fails to compile (cargo check -p libdd-data-pipeline reports E0063 at the Self { ... } initializer). Please add telemetry_handle: None under the same cfg gate in new() so the default data-pipeline build remains usable.

Useful? React with 👍 / 👎.

telemetry_instrumentation_sessions: TelemetryInstrumentationSessions,
shared_runtime: Option<Arc<R>>,
health_metrics_enabled: bool,
Expand Down Expand Up @@ -148,6 +152,8 @@ impl<R: SharedRuntime> TraceExporterBuilder<R> {
client_side_stats_obfuscation_enabled: false,
#[cfg(feature = "telemetry")]
telemetry: None,
#[cfg(feature = "telemetry")]
telemetry_handle: None,
telemetry_instrumentation_sessions: TelemetryInstrumentationSessions::default(),
shared_runtime: None,
health_metrics_enabled: false,
Expand Down Expand Up @@ -356,6 +362,12 @@ impl<R: SharedRuntime> TraceExporterBuilder<R> {
self
}

#[cfg(feature = "telemetry")]
pub fn set_telemetry_handle(&mut self, handle: TelemetryWorkerHandle) -> &mut Self {
self.telemetry_handle = Some(handle);
self
}

/// Sets optional instrumentation session headers on telemetry requests (`dd-session-id`, etc.).
pub fn set_telemetry_instrumentation_sessions(
&mut self,
Expand Down Expand Up @@ -636,54 +648,59 @@ impl<R: SharedRuntime> TraceExporterBuilder<R> {
}

#[cfg(all(not(target_arch = "wasm32"), feature = "telemetry"))]
let (telemetry_client, telemetry_handle) = {
let sessions = self.telemetry_instrumentation_sessions;
let telemetry = self
.telemetry
.filter(|_| {
// no agent endpoint to talk to, so we skip the
// telemetry worker
!(agentless_enabled || self.output_to_log)
})
.map(|telemetry_config| {
let mut builder = TelemetryClientBuilder::default()
.set_language(&self.language)
.set_language_version(&self.language_version)
.set_service_name(&self.service)
.set_service_version(&self.app_version)
.set_env(&self.env)
.set_tracer_version(&self.tracer_version)
.set_heartbeat(telemetry_config.heartbeat)
.set_url(base_url)
.set_debug_enabled(telemetry_config.debug_enabled);
if let Some(id) = telemetry_config.runtime_id {
builder = builder.set_runtime_id(&id);
}
if let Some(ref id) = sessions.session_id {
builder = builder.set_session_id(id);
let (telemetry_client, telemetry_handle) =
if let Some(shared_handle) = self.telemetry_handle {
// Consolidated path: report health metrics through the externally-owned worker.
// We do not own it (no spawn, no start, no shutdown handle).
(Some(TelemetryClient::with_handle(shared_handle)), None)
} else {
let sessions = self.telemetry_instrumentation_sessions;
let telemetry = self
.telemetry
.filter(|_| {
// no agent endpoint to talk to, so we skip the
// telemetry worker
!(agentless_enabled || self.output_to_log)
})
.map(|telemetry_config| {
let mut builder = TelemetryClientBuilder::default()
.set_language(&self.language)
.set_language_version(&self.language_version)
.set_service_name(&self.service)
.set_service_version(&self.app_version)
.set_env(&self.env)
.set_tracer_version(&self.tracer_version)
.set_heartbeat(telemetry_config.heartbeat)
.set_url(base_url)
.set_debug_enabled(telemetry_config.debug_enabled);
if let Some(id) = telemetry_config.runtime_id {
builder = builder.set_runtime_id(&id);
}
if let Some(ref id) = sessions.session_id {
builder = builder.set_session_id(id);
}
if let Some(ref id) = sessions.root_session_id {
builder = builder.set_root_session_id(id);
}
if let Some(ref id) = sessions.parent_session_id {
builder = builder.set_parent_session_id(id);
}
Ok(builder.build())
});
match telemetry {
Some(Ok((client_tel, worker))) => {
let handle = shared_runtime.spawn_worker(worker, false).map_err(|e| {
TraceExporterError::Builder(BuilderErrorKind::InvalidConfiguration(
e.to_string(),
))
})?;
client_tel.start().await;
(Some(client_tel), Some(handle))
}
if let Some(ref id) = sessions.root_session_id {
builder = builder.set_root_session_id(id);
}
if let Some(ref id) = sessions.parent_session_id {
builder = builder.set_parent_session_id(id);
}
Ok(builder.build())
});
match telemetry {
Some(Ok((client_tel, worker))) => {
let handle = shared_runtime.spawn_worker(worker, false).map_err(|e| {
TraceExporterError::Builder(BuilderErrorKind::InvalidConfiguration(
e.to_string(),
))
})?;
client_tel.start().await;
(Some(client_tel), Some(handle))
Some(Err(e)) => return Err(e),
None => (None, None),
}
Some(Err(e)) => return Err(e),
None => (None, None),
}
};
};

// Transport selection: agentless is mutually exclusive with both OTLP and a
// user-supplied agent URL; OTLP and the agent URL may coexist. All exclusion
Expand Down
2 changes: 1 addition & 1 deletion libdd-telemetry-ffi/src/worker_handle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ pub unsafe extern "C" fn ddog_telemetry_handle_add_dependency(
) -> MaybeError {
let name = crate::try_c!(dependency_name.try_to_string());
let version = crate::try_c!(dependency_version.try_to_string_option());
crate::try_c!(handle.add_dependency(name, version));
crate::try_c!(handle.add_dependency(name, version, None));
MaybeError::None
}

Expand Down
2 changes: 2 additions & 0 deletions libdd-telemetry/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ http-body-util = "0.1"
http = "1"
serde = { version = "1.0", features = ["derive"] }
serde_json = { version = "1.0" }
strum = { version = "0.26", default-features = false }
strum_macros = "0.26"
tokio = { version = "1.23", features = ["sync", "io-util"] }
tokio-util = { version = "0.7", features = ["codec"] }
tracing = { version = "0.1", default-features = false }
Expand Down
3 changes: 3 additions & 0 deletions libdd-telemetry/examples/tm-ping.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ fn build_app_started_payload() -> AppStarted {
configuration: Vec::new(),
dependencies: Vec::new(),
integrations: Vec::new(),
install_signature: None,
products: Default::default(),
error: None,
}
}

Expand Down
11 changes: 11 additions & 0 deletions libdd-telemetry/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,15 @@ pub struct Config {
pub parent_session_id: Option<String>,
#[serde(default)]
pub root_session_id: Option<String>,

/// Whether to emit the `app-started`/`app-closing` lifecycle payloads.
/// Forked processes may not be required to emit these.
#[serde(default = "default_true")]
pub emit_app_lifecycle: bool,
}

fn default_true() -> bool {
true
}

fn endpoint_with_telemetry_path(
Expand Down Expand Up @@ -195,6 +204,7 @@ impl Default for Config {
session_id: None,
parent_session_id: None,
root_session_id: None,
emit_app_lifecycle: true,
}
}
}
Expand Down Expand Up @@ -324,6 +334,7 @@ impl Config {
session_id: None,
parent_session_id: None,
root_session_id: None,
emit_app_lifecycle: true,
};

_ = this.set_endpoint(TelemetryEndpoint {
Expand Down
32 changes: 30 additions & 2 deletions libdd-telemetry/src/data/metrics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,20 @@ pub enum SerializedSketch {
B64 { sketch_b64: String },
}

#[derive(Serialize, Deserialize, Debug, Clone, Copy)]
#[derive(
Serialize,
Deserialize,
Debug,
Clone,
Copy,
PartialEq,
Eq,
Hash,
strum_macros::Display,
strum_macros::EnumIter,
)]
#[serde(rename_all = "snake_case")]
#[strum(serialize_all = "snake_case")]
#[repr(C)]
pub enum MetricNamespace {
Tracers,
Expand All @@ -51,13 +63,29 @@ pub enum MetricNamespace {
Telemetry,
Apm,
Sidecar,
Civisibility,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Serialize CI Visibility metrics under the expected namespace

Because this enum uses #[serde(rename_all = "snake_case")], the new Civisibility variant serializes as "civisibility" rather than the CI Visibility namespace name ("ci_visibility"). When callers register CI Visibility telemetry metrics with this variant, the payload will carry an unknown/misspelled namespace and those metrics can be rejected or misclassified; use a variant/serde rename that emits ci_visibility.

Useful? React with 👍 / 👎.

Mlobs,
Ddtraceapi,
}

#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[derive(
Serialize,
Deserialize,
Debug,
Clone,
Copy,
PartialEq,
Eq,
Hash,
strum_macros::Display,
strum_macros::EnumIter,
)]
#[serde(rename_all = "snake_case")]
#[strum(serialize_all = "snake_case")]
#[repr(C)]
pub enum MetricType {
Gauge,
Count,
Rate,
Distribution,
}
Loading
Loading