Skip to content

Commit

Permalink
[crashtracker] Add builder class for CrashInfo
Browse files Browse the repository at this point in the history
  • Loading branch information
danielsn committed Nov 21, 2024
1 parent c6ad4ff commit 175369a
Show file tree
Hide file tree
Showing 7 changed files with 266 additions and 8 deletions.
2 changes: 1 addition & 1 deletion crashtracker/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ mod collector;
mod crash_info;
#[cfg(all(unix, feature = "receiver"))]
mod receiver;
mod rfc5_crash_info;
pub mod rfc5_crash_info;
#[cfg(all(unix, any(feature = "collector", feature = "receiver")))]
mod shared;

Expand Down
221 changes: 221 additions & 0 deletions crashtracker/src/rfc5_crash_info/builder.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,221 @@
// Copyright 2024-Present Datadog, Inc. https://www.datadoghq.com/
// SPDX-License-Identifier: Apache-2.0

use chrono::{DateTime, Utc};
use error_data::ThreadData;
use stacktrace::StackTrace;
use unknown_value::UnknownValue;
use uuid::Uuid;

use super::*;

#[derive(Debug, Default)]
pub struct ErrorDataBuilder {
pub kind: Option<ErrorKind>,
pub message: Option<String>,
pub stack: Option<StackTrace>,
pub threads: Option<Vec<ThreadData>>,
}

impl ErrorDataBuilder {
pub fn build(self) -> anyhow::Result<(ErrorData, bool /* incomplete */)> {
let incomplete = self.stack.is_none();
let is_crash = true;
let kind = self.kind.context("required field 'kind' missing")?;
let message = self.message;
let source_type = SourceType::Crashtracking;
let stack = self.stack.unwrap_or(StackTrace {
format: "Missing Stacktrace".to_string(),
frames: vec![],
});
let threads = self.threads.unwrap_or_default();
Ok((
ErrorData {
is_crash,
kind,
message,
source_type,
stack,
threads,
},
incomplete,
))
}

pub fn new() -> Self {
Self::default()
}

pub fn with_kind(&mut self, kind: ErrorKind) -> &mut Self {
self.kind = Some(kind);
self
}

pub fn with_message(&mut self, message: String) -> &mut Self {
self.message = Some(message);
self
}

pub fn with_stack(&mut self, stack: StackTrace) -> &mut Self {
self.stack = Some(stack);
self
}

pub fn with_threads(&mut self, threads: Vec<ThreadData>) -> &mut Self {
self.threads = Some(threads);
self
}
}

#[derive(Debug, Default)]
pub struct CrashInfoBuilder {
pub counters: Option<HashMap<String, i64>>,
pub error: ErrorDataBuilder,
pub files: Option<HashMap<String, Vec<String>>>,
pub fingerprint: Option<String>,
pub incomplete: Option<bool>,
pub log_messages: Option<Vec<String>>,
pub metadata: Option<Metadata>,
pub os_info: Option<OsInfo>,
pub proc_info: Option<ProcInfo>,
pub sig_info: Option<SigInfo>,
pub span_ids: Option<Vec<Span>>,
pub timestamp: Option<DateTime<Utc>>,
pub trace_ids: Option<Vec<Span>>,
pub uuid: Option<String>,
}

impl CrashInfoBuilder {
pub fn build(self) -> anyhow::Result<CrashInfo> {
let counters = self.counters.unwrap_or_default();
let data_schema_version = CrashInfo::current_schema_version().to_string();
let (error, incomplete_error) = self.error.build()?;
let files = self.files.unwrap_or_default();
let fingerprint = self.fingerprint;
let incomplete = incomplete_error; // TODO
let log_messages = self.log_messages.unwrap_or_default();
let metadata = self.metadata.unwrap_or_else(Metadata::unknown_value);
let os_info = self.os_info.unwrap_or_else(OsInfo::unknown_value);
let proc_info = self.proc_info;
let sig_info = self.sig_info;
let span_ids = self.span_ids.unwrap_or_default();
let timestamp = self.timestamp.unwrap_or_else(Utc::now).to_string();
let trace_ids = self.trace_ids.unwrap_or_default();
let uuid = self.uuid.unwrap_or_else(|| Uuid::new_v4().to_string());
Ok(CrashInfo {
counters,
data_schema_version,
error,
files,
fingerprint,
incomplete,
log_messages,
metadata,
os_info,
proc_info,
sig_info,
span_ids,
timestamp,
trace_ids,
uuid,
})
}

pub fn new() -> Self {
Self::default()
}

pub fn with_counters(&mut self, counters: HashMap<String, i64>) -> &mut Self {
self.counters = Some(counters);
self
}

pub fn with_kind(&mut self, kind: ErrorKind) -> &mut Self {
self.error.with_kind(kind);
self
}

pub fn with_files(&mut self, files: HashMap<String, Vec<String>>) -> &mut Self {
self.files = Some(files);
self
}
pub fn with_fingerprint(&mut self, fingerprint: String) -> &mut Self {
self.fingerprint = Some(fingerprint);
self
}
pub fn with_incomplete(&mut self, incomplete: bool) -> &mut Self {
self.incomplete = Some(incomplete);
self
}
pub fn with_log_messages(&mut self, log_messages: Vec<String>) -> &mut Self {
self.log_messages = Some(log_messages);
self
}

pub fn with_message(&mut self, message: String) -> &mut Self {
self.error.with_message(message);
self
}

pub fn with_metadata(&mut self, metadata: Metadata) -> &mut Self {
self.metadata = Some(metadata);
self
}

pub fn with_os_info(&mut self, os_info: OsInfo) -> &mut Self {
self.os_info = Some(os_info);
self
}

pub fn with_os_info_this_machine(&mut self) -> &mut Self {
self.with_os_info(::os_info::get().into())
}

pub fn with_proc_info(&mut self, proc_info: ProcInfo) -> &mut Self {
self.proc_info = Some(proc_info);
self
}

pub fn with_sig_info(&mut self, sig_info: SigInfo) -> &mut Self {
self.sig_info = Some(sig_info);
self
}

pub fn with_span_ids(&mut self, span_ids: Vec<Span>) -> &mut Self {
self.span_ids = Some(span_ids);
self
}

pub fn with_stack(&mut self, stack: StackTrace) -> &mut Self {
self.error.with_stack(stack);
self
}

pub fn with_threads(&mut self, threads: Vec<ThreadData>) -> &mut Self {
self.error.with_threads(threads);
self
}

pub fn with_timestamp(&mut self, timestamp: DateTime<Utc>) -> &mut Self {
self.timestamp = Some(timestamp);
self
}

pub fn with_timestamp_now(&mut self) -> &mut Self {
self.with_timestamp(Utc::now())
}

pub fn with_trace_ids(&mut self, trace_ids: Vec<Span>) -> &mut Self {
self.trace_ids = Some(trace_ids);
self
}

pub fn with_uuid(&mut self, uuid: String) -> &mut Self {
self.uuid = Some(uuid);
self
}

pub fn with_uuid_random(&mut self) -> &mut Self {
self.with_uuid(Uuid::new_v4().to_string())
}
}
13 changes: 13 additions & 0 deletions crashtracker/src/rfc5_crash_info/metadata.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};

use super::unknown_value::UnknownValue;

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
pub struct Metadata {
pub library_name: String,
Expand All @@ -13,6 +15,17 @@ pub struct Metadata {
pub tags: Vec<String>,
}

impl UnknownValue for Metadata {
fn unknown_value() -> Self {
Self {
library_name: "unknown".to_string(),
library_version: "unknown".to_string(),
family: "unknown".to_string(),
tags: vec![],
}
}
}

impl From<crate::crash_info::CrashtrackerMetadata> for Metadata {
fn from(value: crate::crash_info::CrashtrackerMetadata) -> Self {
let tags = value
Expand Down
20 changes: 15 additions & 5 deletions crashtracker/src/rfc5_crash_info/mod.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
// Copyright 2024-Present Datadog, Inc. https://www.datadoghq.com/
// SPDX-License-Identifier: Apache-2.0

mod builder;
mod error_data;
mod metadata;
mod os_info;
mod proc_info;
mod sig_info;
mod spans;
mod stacktrace;
mod unknown_value;

pub use builder::*;

use anyhow::Context;
use error_data::{thread_data_from_additional_stacktraces, ErrorData, ErrorKind, SourceType};
Expand Down Expand Up @@ -35,8 +39,8 @@ pub struct CrashInfo {
pub log_messages: Vec<String>,
pub metadata: Metadata,
pub os_info: OsInfo,
pub proc_info: ProcInfo,
pub sig_info: SigInfo,
pub proc_info: Option<ProcInfo>, //TODO, update the schema
pub sig_info: Option<SigInfo>, //TODO, update the schema
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub span_ids: Vec<Span>,
pub timestamp: String,
Expand All @@ -45,10 +49,16 @@ pub struct CrashInfo {
pub uuid: String,
}

impl CrashInfo {
pub fn current_schema_version() -> String {
"1.0".to_string()
}
}

impl From<crate::crash_info::CrashInfo> for CrashInfo {
fn from(value: crate::crash_info::CrashInfo) -> Self {
let counters = value.counters;
let data_schema_version = String::from("1.0");
let data_schema_version = CrashInfo::current_schema_version();
let error = {
let is_crash = true;
let kind = ErrorKind::UnixSignal;
Expand All @@ -71,8 +81,8 @@ impl From<crate::crash_info::CrashInfo> for CrashInfo {
let log_messages = vec![];
let metadata = value.metadata.unwrap().into();
let os_info = value.os_info.into();
let proc_info = value.proc_info.unwrap().into();
let sig_info = value.siginfo.unwrap().into();
let proc_info = value.proc_info.map(ProcInfo::from);
let sig_info = value.siginfo.map(SigInfo::from);
let span_ids = value
.span_ids
.into_iter()
Expand Down
8 changes: 8 additions & 0 deletions crashtracker/src/rfc5_crash_info/os_info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};

use super::unknown_value::UnknownValue;

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
pub struct OsInfo {
pub architecture: String,
Expand All @@ -11,6 +13,12 @@ pub struct OsInfo {
pub version: String,
}

impl UnknownValue for OsInfo {
fn unknown_value() -> Self {
os_info::Info::unknown().into()
}
}

impl From<os_info::Info> for OsInfo {
fn from(value: os_info::Info) -> Self {
let architecture = value.architecture().unwrap_or("unknown").to_string();
Expand Down
4 changes: 2 additions & 2 deletions crashtracker/src/rfc5_crash_info/stacktrace.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ use crate::NormalizedAddress;

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
pub struct StackTrace {
format: String,
frames: Vec<StackFrame>,
pub format: String,
pub frames: Vec<StackFrame>,
}

impl From<Vec<crate::StackFrame>> for StackTrace {
Expand Down
6 changes: 6 additions & 0 deletions crashtracker/src/rfc5_crash_info/unknown_value.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// Copyright 2024-Present Datadog, Inc. https://www.datadoghq.com/
// SPDX-License-Identifier: Apache-2.0

pub trait UnknownValue {
fn unknown_value() -> Self;
}

0 comments on commit 175369a

Please sign in to comment.