diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml new file mode 100644 index 000000000..a32684085 --- /dev/null +++ b/.github/workflows/stale.yml @@ -0,0 +1,27 @@ +name: "Mark stale pull requests" + +on: + schedule: + - cron: "0 0 * * *" + workflow_dispatch: + +permissions: + issues: write + pull-requests: write + +jobs: + stale: + runs-on: ubuntu-latest + steps: + - uses: actions/stale@v8 + with: + stale-pr-message: > + This pull request has been automatically marked as stale because it has not had recent activity. + It will be closed if no further activity occurs. To override this behavior, add the keep-open + label or update the PR. + days-before-issue-stale: -1 + days-before-issue-close: -1 + days-before-pr-stale: 90 + days-before-pr-close: 14 + stale-pr-label: "stale" + exempt-pr-labels: "keep-open" diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index e317b89fc..5d76ce3aa 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -25,7 +25,7 @@ jobs: android: true dotnet: true haskell: true - large-packages: false + large-packages: true docker-images: false swap-storage: true diff --git a/bin_tests/tests/crashtracker_bin_test.rs b/bin_tests/tests/crashtracker_bin_test.rs index 128e8f625..c7e858c3e 100644 --- a/bin_tests/tests/crashtracker_bin_test.rs +++ b/bin_tests/tests/crashtracker_bin_test.rs @@ -104,7 +104,8 @@ fn test_crash_tracking_bin( assert_eq!( serde_json::json!({ "signum": 11, - "signame": "SIGSEGV" + "signame": "SIGSEGV", + "faulting_address": 0, }), crash_payload["siginfo"] ); @@ -146,7 +147,8 @@ fn assert_telemetry_message(crash_telemetry: &[u8]) { "profiler_collecting_sample:1", "profiler_inactive:0", "profiler_serializing:0", - "signame:SIGSEGV" + "signame:SIGSEGV", + "faulting_address:0x0000000000000000", ]), tags ); diff --git a/crashtracker-ffi/src/crash_info/datatypes.rs b/crashtracker-ffi/src/crash_info/datatypes.rs index 4bf936d93..1b01ec2d4 100644 --- a/crashtracker-ffi/src/crash_info/datatypes.rs +++ b/crashtracker-ffi/src/crash_info/datatypes.rs @@ -237,7 +237,12 @@ impl<'a> TryFrom> for datadog_crashtracker::SigInfo { fn try_from(value: SigInfo<'a>) -> Result { let signum = value.signum; let signame = option_from_char_slice(value.signame)?; - Ok(Self { signum, signame }) + let faulting_address = None; // TODO: Expose this to FFI + Ok(Self { + signum, + signame, + faulting_address, + }) } } diff --git a/crashtracker/src/collector/crash_handler.rs b/crashtracker/src/collector/crash_handler.rs index ce4f71256..493b47815 100644 --- a/crashtracker/src/collector/crash_handler.rs +++ b/crashtracker/src/collector/crash_handler.rs @@ -254,7 +254,7 @@ pub fn shutdown_receiver() -> anyhow::Result<()> { extern "C" fn handle_posix_sigaction(signum: i32, sig_info: *mut siginfo_t, ucontext: *mut c_void) { // Handle the signal. Note this has a guard to ensure that we only generate // one crash report per process. - let _ = handle_posix_signal_impl(signum); + let _ = handle_posix_signal_impl(signum, sig_info); // Once we've handled the signal, chain to any previous handlers. // SAFETY: This was created by [register_crash_handlers]. There is a tiny @@ -303,7 +303,7 @@ extern "C" fn handle_posix_sigaction(signum: i32, sig_info: *mut siginfo_t, ucon }; } -fn handle_posix_signal_impl(signum: i32) -> anyhow::Result<()> { +fn handle_posix_signal_impl(signum: i32, sig_info: *mut siginfo_t) -> anyhow::Result<()> { static NUM_TIMES_CALLED: AtomicU64 = AtomicU64::new(0); if NUM_TIMES_CALLED.fetch_add(1, SeqCst) > 0 { // In the case where some lower-level signal handler recovered the error @@ -324,13 +324,27 @@ fn handle_posix_signal_impl(signum: i32) -> anyhow::Result<()> { anyhow::ensure!(!metadata_ptr.is_null(), "No crashtracking metadata"); let (_metadata, metadata_string) = unsafe { metadata_ptr.as_ref().context("metadata ptr")? }; + let faulting_address: Option = + if !sig_info.is_null() && (signum == libc::SIGSEGV || signum == libc::SIGBUS) { + unsafe { Some((*sig_info).si_addr() as usize) } + } else { + None + }; + match unsafe { receiver.as_mut().context("No crashtracking receiver")? } { ReceiverType::ForkedProcess(child) => { let pipe = child .stdin .as_mut() .context("Crashtracker: Can't get pipe")?; - let res = emit_crashreport(pipe, config, config_str, metadata_string, signum); + let res = emit_crashreport( + pipe, + config, + config_str, + metadata_string, + signum, + faulting_address, + ); let _ = pipe.flush(); if config.wait_for_receiver { // https://doc.rust-lang.org/std/process/struct.Child.html#method.wait @@ -362,6 +376,7 @@ fn handle_posix_signal_impl(signum: i32) -> anyhow::Result<()> { config_str, metadata_string, signum, + faulting_address, ); let _ = unix_stream.flush(); unix_stream @@ -464,10 +479,15 @@ unsafe fn set_alt_stack() -> anyhow::Result<()> { return Ok(()); } + // Ensure that the altstack size is the greater of 16 pages or SIGSTKSZ. This is necessary + // because the default SIGSTKSZ is 8KB, which we're starting to run into. This new size is + // arbitrary, but at least it's large enough for our purposes, and yet a small enough part of + // the process RSS that it shouldn't be a problem. let page_size = page_size::get(); + let sigalstack_base_size = std::cmp::max(SIGSTKSZ, 16 * page_size); let stackp = mmap( ptr::null_mut(), - SIGSTKSZ + page_size::get(), + sigalstack_base_size + page_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, @@ -487,7 +507,7 @@ unsafe fn set_alt_stack() -> anyhow::Result<()> { let stack = libc::stack_t { ss_sp: stackp, ss_flags: 0, - ss_size: SIGSTKSZ, + ss_size: sigalstack_base_size, }; let rval = sigaltstack(&stack, ptr::null_mut()); anyhow::ensure!(rval == 0, "sigaltstack failed {rval}"); diff --git a/crashtracker/src/collector/emitters.rs b/crashtracker/src/collector/emitters.rs index 07b1a0bdd..83e39702d 100644 --- a/crashtracker/src/collector/emitters.rs +++ b/crashtracker/src/collector/emitters.rs @@ -101,10 +101,11 @@ pub(crate) fn emit_crashreport( config_str: &str, metadata_string: &str, signum: i32, + faulting_address: Option, ) -> anyhow::Result<()> { emit_metadata(pipe, metadata_string)?; emit_config(pipe, config_str)?; - emit_siginfo(pipe, signum)?; + emit_siginfo(pipe, signum, faulting_address)?; emit_procinfo(pipe)?; pipe.flush()?; emit_counters(pipe)?; @@ -163,7 +164,11 @@ fn emit_proc_self_maps(w: &mut impl Write) -> anyhow::Result<()> { Ok(()) } -fn emit_siginfo(w: &mut impl Write, signum: i32) -> anyhow::Result<()> { +fn emit_siginfo( + w: &mut impl Write, + signum: i32, + faulting_address: Option, +) -> anyhow::Result<()> { let signame = if signum == libc::SIGSEGV { "SIGSEGV" } else if signum == libc::SIGBUS { @@ -173,7 +178,14 @@ fn emit_siginfo(w: &mut impl Write, signum: i32) -> anyhow::Result<()> { }; writeln!(w, "{DD_CRASHTRACK_BEGIN_SIGINFO}")?; - writeln!(w, "{{\"signum\": {signum}, \"signame\": \"{signame}\"}}")?; + if let Some(addr) = faulting_address { + writeln!( + w, + "{{\"signum\": {signum}, \"signame\": \"{signame}\", \"faulting_address\": {addr}}}" + )?; + } else { + writeln!(w, "{{\"signum\": {signum}, \"signame\": \"{signame}\"}}")?; + }; writeln!(w, "{DD_CRASHTRACK_END_SIGINFO}")?; Ok(()) } diff --git a/crashtracker/src/crash_info/mod.rs b/crashtracker/src/crash_info/mod.rs index 57ceb0993..ac13af7f9 100644 --- a/crashtracker/src/crash_info/mod.rs +++ b/crashtracker/src/crash_info/mod.rs @@ -23,6 +23,9 @@ pub struct SigInfo { #[serde(skip_serializing_if = "Option::is_none")] #[serde(default)] pub signame: Option, + #[serde(skip_serializing_if = "Option::is_none")] + #[serde(default)] + pub faulting_address: Option, } #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] diff --git a/crashtracker/src/crash_info/telemetry.rs b/crashtracker/src/crash_info/telemetry.rs index 3c3a8fc9d..87a97f2e6 100644 --- a/crashtracker/src/crash_info/telemetry.rs +++ b/crashtracker/src/crash_info/telemetry.rs @@ -191,6 +191,9 @@ fn extract_crash_info_tags(crash_info: &CrashInfo) -> anyhow::Result { if let Some(signame) = &siginfo.signame { write!(&mut tags, ",signame:{}", signame)?; } + if let Some(faulting_address) = &siginfo.faulting_address { + write!(&mut tags, ",faulting_address:{:#018x}", faulting_address)?; + } } for (counter, value) in &crash_info.counters { write!(&mut tags, ",{}:{}", counter, value)?; @@ -276,6 +279,7 @@ mod tests { siginfo: Some(SigInfo { signum: 11, signame: Some("SIGSEGV".to_owned()), + faulting_address: Some(0x1234), }), proc_info: None, stacktrace: vec![], @@ -308,7 +312,8 @@ mod tests { "signum:11", "signame:SIGSEGV", "collecting_sample:1", - "not_profiling:0" + "not_profiling:0", + "faulting_address:0x0000000000001234", ]), tags ); diff --git a/data-pipeline/src/trace_exporter.rs b/data-pipeline/src/trace_exporter.rs index 49b092545..97a46b0c4 100644 --- a/data-pipeline/src/trace_exporter.rs +++ b/data-pipeline/src/trace_exporter.rs @@ -32,6 +32,11 @@ const DEFAULT_STATS_ELIGIBLE_SPAN_KINDS: [&str; 4] = ["client", "server", "produ const STATS_ENDPOINT: &str = "/v0.6/stats"; const INFO_ENDPOINT: &str = "/info"; +// Keys used for sampling +const SAMPLING_PRIORITY_KEY: &str = "_sampling_priority_v1"; +const SAMPLING_SINGLE_SPAN_MECHANISM: &str = "_dd.span_sampling.mechanism"; +const SAMPLING_ANALYTICS_RATE_KEY: &str = "_dd1.sr.eausr"; + /// TraceExporterInputFormat represents the format of the input traces. /// The input format can be either Proxy or V0.4, where V0.4 is the default. #[derive(Copy, Clone, Debug, Default, PartialEq)] @@ -95,33 +100,45 @@ fn add_path(url: &Uri, path: &str) -> Uri { Uri::from_parts(parts).unwrap() } +struct DroppedP0Counts { + pub dropped_p0_traces: usize, + pub dropped_p0_spans: usize, +} + /// Remove spans and chunks only keeping the ones that may be sampled by the agent -fn drop_chunks(traces: &mut Vec>) { +fn drop_chunks(traces: &mut Vec>) -> DroppedP0Counts { + let mut dropped_p0_traces = 0; + let mut dropped_p0_spans = 0; traces.retain_mut(|chunk| { + // List of spans to keep even if the chunk is dropped let mut sampled_indexes = Vec::new(); for (index, span) in chunk.iter().enumerate() { + // ErrorSampler if span.error == 1 { // We send chunks containing an error return true; } - let priority = span.metrics.get("_sampling_priority_v1"); - if priority.is_some_and(|p| *p > 0.0) { - if has_top_level(span) { - // We send chunks with positive priority - return true; - } - // We send single spans with positive priority - sampled_indexes.push(index); - } else if priority.is_none() && has_top_level(span) { - // We send chunks with no priority + // PrioritySampler and NoPrioritySampler + let priority = span.metrics.get(SAMPLING_PRIORITY_KEY); + if has_top_level(span) && (priority.is_none() || priority.is_some_and(|p| *p > 0.0)) { + // We send chunks with positive priority or no priority return true; - } else if span.metrics.contains_key("_dd.sr.eausr") { - // We send analyzed spans + } + // SingleSpanSampler and AnalyzedSpansSampler + else if span + .metrics + .get(SAMPLING_SINGLE_SPAN_MECHANISM) + .is_some_and(|m| *m == 8.0) + || span.metrics.contains_key(SAMPLING_ANALYTICS_RATE_KEY) + { + // We send spans sampled by single-span sampling or analyzed spans sampled_indexes.push(index); } } + dropped_p0_spans += chunk.len() - sampled_indexes.len(); if sampled_indexes.is_empty() { // If no spans were sampled we can drop the whole chunk + dropped_p0_traces += 1; return false; } let sampled_spans = sampled_indexes @@ -130,7 +147,11 @@ fn drop_chunks(traces: &mut Vec>) { .collect(); *chunk = sampled_spans; true - }) + }); + DroppedP0Counts { + dropped_p0_traces, + dropped_p0_spans, + } } #[derive(Clone, Default)] @@ -429,15 +450,6 @@ impl TraceExporter { ) } - fn get_headers(&self) -> TracerHeaderTags<'_> { - let mut headers: TracerHeaderTags = self.metadata.borrow().into(); - if let StatsComputationStatus::Enabled { .. } = &**self.client_side_stats.load() { - headers.client_computed_top_level = true; - headers.client_computed_stats = true; - }; - headers - } - fn send_data_to_url( &self, data: &[u8], @@ -454,7 +466,7 @@ impl TraceExporter { ) .method(Method::POST); - let headers: HashMap<&'static str, String> = self.get_headers().into(); + let headers: HashMap<&'static str, String> = self.metadata.borrow().into(); for (key, value) in &headers { req_builder = req_builder.header(*key, value); @@ -589,6 +601,8 @@ impl TraceExporter { None, ); + let mut header_tags: TracerHeaderTags = self.metadata.borrow().into(); + // Stats computation if let StatsComputationStatus::Enabled { .. } = &**self.client_side_stats.load() { if !self.client_computed_top_level { @@ -599,11 +613,13 @@ impl TraceExporter { self.add_spans_to_stats(traces.iter().flat_map(|trace| trace.iter())); // Once stats have been computed we can drop all chunks that are not going to be // sampled by the agent - drop_chunks(&mut traces); + let dropped_counts = drop_chunks(&mut traces); + header_tags.client_computed_top_level = true; + header_tags.client_computed_stats = true; + header_tags.dropped_p0_traces = dropped_counts.dropped_p0_traces; + header_tags.dropped_p0_spans = dropped_counts.dropped_p0_spans; } - let header_tags: TracerHeaderTags<'_> = self.get_headers(); - match self.output_format { TraceExporterOutputFormat::V04 => rmp_serde::to_vec_named(&traces) .map_err(|err| { @@ -990,7 +1006,7 @@ mod tests { pb::Span { span_id: 1, metrics: HashMap::from([ - ("_sampling_priority_v1".to_string(), 1.0), + (SAMPLING_PRIORITY_KEY.to_string(), 1.0), ("_dd.top_level".to_string(), 1.0), ]), ..Default::default() @@ -1005,7 +1021,7 @@ mod tests { pb::Span { span_id: 1, metrics: HashMap::from([ - ("_sampling_priority_v1".to_string(), 0.0), + (SAMPLING_PRIORITY_KEY.to_string(), 0.0), ("_dd.top_level".to_string(), 1.0), ]), ..Default::default() @@ -1033,7 +1049,7 @@ mod tests { span_id: 1, error: 1, metrics: HashMap::from([ - ("_sampling_priority_v1".to_string(), 0.0), + (SAMPLING_PRIORITY_KEY.to_string(), 0.0), ("_dd.top_level".to_string(), 1.0), ]), ..Default::default() @@ -1048,7 +1064,7 @@ mod tests { pb::Span { span_id: 1, metrics: HashMap::from([ - ("_sampling_priority_v1".to_string(), 0.0), + (SAMPLING_PRIORITY_KEY.to_string(), 0.0), ("_dd.top_level".to_string(), 1.0), ]), ..Default::default() @@ -1056,7 +1072,7 @@ mod tests { pb::Span { span_id: 2, parent_id: 1, - metrics: HashMap::from([("_sampling_priority_v1".to_string(), 1.0)]), + metrics: HashMap::from([(SAMPLING_SINGLE_SPAN_MECHANISM.to_string(), 8.0)]), ..Default::default() }, ]; @@ -1064,7 +1080,7 @@ mod tests { pb::Span { span_id: 1, metrics: HashMap::from([ - ("_sampling_priority_v1".to_string(), 0.0), + (SAMPLING_PRIORITY_KEY.to_string(), 0.0), ("_dd.top_level".to_string(), 1.0), ]), ..Default::default() @@ -1072,7 +1088,7 @@ mod tests { pb::Span { span_id: 2, parent_id: 1, - metrics: HashMap::from([("_dd.sr.eausr".to_string(), 1.0)]), + metrics: HashMap::from([(SAMPLING_ANALYTICS_RATE_KEY.to_string(), 1.0)]), ..Default::default() }, ]; diff --git a/sidecar-ffi/src/lib.rs b/sidecar-ffi/src/lib.rs index 3d535ac61..aaee3a43a 100644 --- a/sidecar-ffi/src/lib.rs +++ b/sidecar-ffi/src/lib.rs @@ -595,6 +595,7 @@ impl<'a> TryInto for &'a TracerHeaderTags<'a> { container_id: &self.container_id.to_utf8_lossy(), client_computed_top_level: self.client_computed_top_level, client_computed_stats: self.client_computed_stats, + ..Default::default() }; tags.try_into().map_err(|_| { diff --git a/sidecar/src/service/serialized_tracer_header_tags.rs b/sidecar/src/service/serialized_tracer_header_tags.rs index 6a4d6d1e8..3261e65ba 100644 --- a/sidecar/src/service/serialized_tracer_header_tags.rs +++ b/sidecar/src/service/serialized_tracer_header_tags.rs @@ -34,6 +34,7 @@ pub struct SerializedTracerHeaderTags { /// container_id: "1234567890", /// client_computed_top_level: true, /// client_computed_stats: false, +/// ..Default::default() /// }; /// /// let serialized: SerializedTracerHeaderTags = tracer_header_tags.try_into().unwrap(); @@ -73,6 +74,7 @@ impl<'a> TryFrom<&'a SerializedTracerHeaderTags> for TracerHeaderTags<'a> { /// container_id: "1234567890", /// client_computed_top_level: true, /// client_computed_stats: false, +/// ..Default::default() /// }; /// /// let serialized: Result = tracer_header_tags.try_into(); @@ -106,6 +108,7 @@ mod tests { container_id: "1234567890", client_computed_top_level: true, client_computed_stats: false, + ..Default::default() }; let serialized: Result = tracer_header_tags.try_into(); @@ -124,6 +127,7 @@ mod tests { container_id: "1234567890", client_computed_top_level: true, client_computed_stats: false, + ..Default::default() }; let data = bincode::serialize(&tracer_header_tags).unwrap(); diff --git a/tools/sidecar_mockgen/src/lib.rs b/tools/sidecar_mockgen/src/lib.rs index 0884816b7..5d15e00f4 100644 --- a/tools/sidecar_mockgen/src/lib.rs +++ b/tools/sidecar_mockgen/src/lib.rs @@ -62,7 +62,13 @@ pub fn generate_mock_symbols(binary: &Path, objects: &[&Path]) -> Result writeln!(generated, "void {}() {{}}", name), + SymbolKind::Text => { + if !sym.is_weak() { + writeln!(generated, "void {}() {{}}", name) + } else { + Ok(()) + } + } // Ignore symbols of size 0, like _GLOBAL_OFFSET_TABLE_ on alpine SymbolKind::Data | SymbolKind::Unknown => { if sym.size() > 0 { diff --git a/trace-utils/src/send_data/mod.rs b/trace-utils/src/send_data/mod.rs index 4b7296a9c..d6de84bb9 100644 --- a/trace-utils/src/send_data/mod.rs +++ b/trace-utils/src/send_data/mod.rs @@ -524,6 +524,8 @@ mod tests { container_id: "id", client_computed_top_level: false, client_computed_stats: false, + dropped_p0_traces: 0, + dropped_p0_spans: 0, }; fn setup_payload(header_tags: &TracerHeaderTags) -> TracerPayload { @@ -754,7 +756,10 @@ mod tests { header_tags.lang_interpreter, ) .header("datadog-meta-lang-version", header_tags.lang_version) - .header("datadog-meta-lang-vendor", header_tags.lang_vendor) + .header( + "datadog-meta-lang-interpreter-vendor", + header_tags.lang_vendor, + ) .header("datadog-meta-tracer-version", header_tags.tracer_version) .header("datadog-container-id", header_tags.container_id) .path("/"); @@ -810,7 +815,10 @@ mod tests { header_tags.lang_interpreter, ) .header("datadog-meta-lang-version", header_tags.lang_version) - .header("datadog-meta-lang-vendor", header_tags.lang_vendor) + .header( + "datadog-meta-lang-interpreter-vendor", + header_tags.lang_vendor, + ) .header("datadog-meta-tracer-version", header_tags.tracer_version) .header("datadog-container-id", header_tags.container_id) .path("/"); @@ -995,7 +1003,10 @@ mod tests { header_tags.lang_interpreter, ) .header("datadog-meta-lang-version", header_tags.lang_version) - .header("datadog-meta-lang-vendor", header_tags.lang_vendor) + .header( + "datadog-meta-lang-interpreter-vendor", + header_tags.lang_vendor, + ) .header("datadog-meta-tracer-version", header_tags.tracer_version) .header("datadog-container-id", header_tags.container_id) .path("/"); diff --git a/trace-utils/src/tracer_header_tags.rs b/trace-utils/src/tracer_header_tags.rs index e1bca47cc..0dec96745 100644 --- a/trace-utils/src/tracer_header_tags.rs +++ b/trace-utils/src/tracer_header_tags.rs @@ -35,6 +35,10 @@ pub struct TracerHeaderTags<'a> { // specifies whether the client has computed stats so that the agent doesn't have to. Any // non-empty value will mean 'yes'. pub client_computed_stats: bool, + // number of trace chunks dropped in the tracer + pub dropped_p0_traces: usize, + // number of spans dropped in the tracer + pub dropped_p0_spans: usize, } impl<'a> From> for HashMap<&'static str, String> { @@ -46,7 +50,10 @@ impl<'a> From> for HashMap<&'static str, String> { "datadog-meta-lang-interpreter", tags.lang_interpreter.to_string(), ), - ("datadog-meta-lang-vendor", tags.lang_vendor.to_string()), + ( + "datadog-meta-lang-interpreter-vendor", + tags.lang_vendor.to_string(), + ), ( "datadog-meta-tracer-version", tags.tracer_version.to_string(), @@ -68,6 +75,22 @@ impl<'a> From> for HashMap<&'static str, String> { String::new() }, ), + ( + "datadog-client-dropped-p0-traces", + if tags.dropped_p0_traces > 0 { + tags.dropped_p0_traces.to_string() + } else { + String::new() + }, + ), + ( + "datadog-client-dropped-p0-spans", + if tags.dropped_p0_spans > 0 { + tags.dropped_p0_spans.to_string() + } else { + String::new() + }, + ), ]); headers.retain(|_, v| !v.is_empty()); headers @@ -83,7 +106,7 @@ impl<'a> From<&'a HeaderMap> for TracerHeaderTags<'a> { "datadog-meta-lang" => tags.lang, "datadog-meta-lang-version" => tags.lang_version, "datadog-meta-lang-interpreter" => tags.lang_interpreter, - "datadog-meta-lang-vendor" => tags.lang_vendor, + "datadog-meta-lang-interpreter-vendor" => tags.lang_vendor, "datadog-meta-tracer-version" => tags.tracer_version, "datadog-container-id" => tags.container_id, } @@ -94,6 +117,16 @@ impl<'a> From<&'a HeaderMap> for TracerHeaderTags<'a> { if headers.get("datadog-client-computed-stats").is_some() { tags.client_computed_stats = true; } + if let Some(count) = headers.get("datadog-client-dropped-p0-traces") { + tags.dropped_p0_traces = count + .to_str() + .unwrap_or_default() + .parse() + .unwrap_or_default(); + } + if let Some(count) = headers.get("datadog-client-dropped-p0-spans") { + tags.dropped_p0_spans = count.to_str().map_or(0, |c| c.parse().unwrap_or(0)); + } tags } } @@ -114,18 +147,23 @@ mod tests { container_id: "id", client_computed_top_level: true, client_computed_stats: true, + dropped_p0_traces: 12, + dropped_p0_spans: 120, }; let map: HashMap<&'static str, String> = header_tags.into(); - assert_eq!(map.len(), 8); + assert_eq!(map.len(), 10); assert_eq!(map.get("datadog-meta-lang").unwrap(), "test-lang"); assert_eq!(map.get("datadog-meta-lang-version").unwrap(), "2.0"); assert_eq!( map.get("datadog-meta-lang-interpreter").unwrap(), "interpreter" ); - assert_eq!(map.get("datadog-meta-lang-vendor").unwrap(), "vendor"); + assert_eq!( + map.get("datadog-meta-lang-interpreter-vendor").unwrap(), + "vendor" + ); assert_eq!(map.get("datadog-meta-tracer-version").unwrap(), "1.0"); assert_eq!(map.get("datadog-container-id").unwrap(), "id"); assert_eq!( @@ -133,6 +171,8 @@ mod tests { "true" ); assert_eq!(map.get("datadog-client-computed-stats").unwrap(), "true"); + assert_eq!(map.get("datadog-client-dropped-p0-traces").unwrap(), "12"); + assert_eq!(map.get("datadog-client-dropped-p0-spans").unwrap(), "120"); } #[test] fn tags_to_hashmap_empty_value() { @@ -145,6 +185,8 @@ mod tests { container_id: "", client_computed_top_level: false, client_computed_stats: false, + dropped_p0_spans: 0, + dropped_p0_traces: 0, }; let map: HashMap<&'static str, String> = header_tags.into(); @@ -156,11 +198,16 @@ mod tests { map.get("datadog-meta-lang-interpreter").unwrap(), "interpreter" ); - assert_eq!(map.get("datadog-meta-lang-vendor").unwrap(), "vendor"); + assert_eq!( + map.get("datadog-meta-lang-interpreter-vendor").unwrap(), + "vendor" + ); assert_eq!(map.get("datadog-meta-tracer-version").unwrap(), "1.0"); assert_eq!(map.get("datadog-container-id"), None); assert_eq!(map.get("datadog-client-computed-top-level"), None); assert_eq!(map.get("datadog-client-computed-stats"), None); + assert_eq!(map.get("datadog-client-dropped-p0-traces"), None); + assert_eq!(map.get("datadog-client-dropped-p0-spans"), None); } #[test] @@ -173,10 +220,14 @@ mod tests { "datadog-meta-lang-interpreter", "interpreter".parse().unwrap(), ); - header_map.insert("datadog-meta-lang-vendor", "vendor".parse().unwrap()); + header_map.insert( + "datadog-meta-lang-interpreter-vendor", + "vendor".parse().unwrap(), + ); header_map.insert("datadog-meta-tracer-version", "1.0".parse().unwrap()); header_map.insert("datadog-container-id", "id".parse().unwrap()); header_map.insert("datadog-client-computed-stats", "true".parse().unwrap()); + header_map.insert("datadog-client-dropped-p0-traces", "12".parse().unwrap()); let tags: TracerHeaderTags = (&header_map).into(); @@ -188,5 +239,7 @@ mod tests { assert_eq!(tags.container_id, "id"); assert!(tags.client_computed_stats); assert!(!tags.client_computed_top_level); + assert_eq!(tags.dropped_p0_traces, 12); + assert_eq!(tags.dropped_p0_spans, 0); } } diff --git a/trace-utils/tests/test_send_data.rs b/trace-utils/tests/test_send_data.rs index 1c0c42852..1265378f4 100644 --- a/trace-utils/tests/test_send_data.rs +++ b/trace-utils/tests/test_send_data.rs @@ -24,8 +24,7 @@ mod tracing_integration_tests { lang_vendor: "vendor", tracer_version: "1.0", container_id: "id", - client_computed_top_level: false, - client_computed_stats: false, + ..Default::default() }; let endpoint = Endpoint::from_url(