Skip to content

Commit

Permalink
[Serverless Mini Agent] Add Default Values for peer_tags and is_trace…
Browse files Browse the repository at this point in the history
…_root when Generating Trace Metrics (#493)

* add default values for peer_tags and is_trace_root when generating trace metrics

* adds unit tests for trace stat deserialization to ClientStatsPayload

* apply formatting

* remove unused dependencies from trace-protobuf

* fix merge conflict

* add field attributes for trace-protobuf
  • Loading branch information
duncanpharvey authored Jun 24, 2024
1 parent 7aeb446 commit d4d96ba
Show file tree
Hide file tree
Showing 5 changed files with 213 additions and 3 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

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

7 changes: 4 additions & 3 deletions trace-protobuf/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,13 @@ prost = "0.11.6"
serde = { version = "1.0.145", features = ["derive"] }
serde_bytes = "0.11.9"

[dev-dependencies]
serde_json = "1.0.117"

[build-dependencies]
prost-build = { version = "0.11.9", optional = true }
protoc-bin-vendored = { version = "3.0.0", optional = true }

[features]
generate-protobuf = ["dep:prost-build", "dep:protoc-bin-vendored"]

[dev-dependencies]
serde_json = "1.0.117"
tokio = { version = "1", features = ["macros", "rt-multi-thread"] }
2 changes: 2 additions & 0 deletions trace-protobuf/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,8 @@ fn generate_protobuf() {
config.field_attribute("ClientGroupedStats.type", "#[serde(default)]");
config.field_attribute("ClientGroupedStats.peer_service", "#[serde(default)]");
config.field_attribute("ClientGroupedStats.span_kind", "#[serde(default)]");
config.field_attribute("ClientGroupedStats.peer_tags", "#[serde(default)]");
config.field_attribute("ClientGroupedStats.is_trace_root", "#[serde(default)]");

config.field_attribute(
"ClientGroupedStats.okSummary",
Expand Down
93 changes: 93 additions & 0 deletions trace-protobuf/src/pb.rs
Original file line number Diff line number Diff line change
Expand Up @@ -429,9 +429,11 @@ pub struct ClientGroupedStats {
/// peer_tags are supplementary tags that further describe a peer entity
/// E.g., `grpc.target` to describe the name of a gRPC peer, or `db.hostname` to describe the name of peer DB
#[prost(string, repeated, tag = "16")]
#[serde(default)]
pub peer_tags: ::prost::alloc::vec::Vec<::prost::alloc::string::String>,
/// this field's value is equal to span's ParentID == 0.
#[prost(enumeration = "Trilean", tag = "17")]
#[serde(default)]
pub is_trace_root: i32,
}
/// Trilean is an expanded boolean type that is meant to differentiate between being unset and false.
Expand Down Expand Up @@ -493,3 +495,94 @@ impl TraceRootFlag {
}
}
}

#[cfg(test)]
mod tests {
use crate::pb::{ClientGroupedStats, ClientStatsBucket, ClientStatsPayload, Trilean::NotSet};

#[tokio::test]
async fn test_deserialize_client_stats_payload() {
let stats_json = r#"{
"Hostname": "TestHost",
"Env": "test",
"Version": "1.0.0",
"Stats": [
{
"Start": 0,
"Duration": 10000000000,
"Stats": [
{
"Name": "test-span",
"Service": "test-service",
"Resource": "test-span",
"Type": "",
"HTTPStatusCode": 0,
"Synthetics": false,
"Hits": 1,
"TopLevelHits": 1,
"Errors": 0,
"Duration": 10000000,
"OkSummary": [
0,
0,
0
],
"ErrorSummary": [
0,
0,
0
]
}
]
}
],
"Lang": "javascript",
"TracerVersion": "1.0.0",
"RuntimeID": "00000000-0000-0000-0000-000000000000",
"Sequence": 1
}"#;

let deserialized_stats_json: ClientStatsPayload = serde_json::from_str(stats_json).unwrap();

let client_stats_payload = ClientStatsPayload {
hostname: "TestHost".to_string(),
env: "test".to_string(),
version: "1.0.0".to_string(),
stats: vec![ClientStatsBucket {
start: 0,
duration: 10000000000,
stats: vec![ClientGroupedStats {
service: "test-service".to_string(),
name: "test-span".to_string(),
resource: "test-span".to_string(),
http_status_code: 0,
r#type: "".to_string(),
db_type: "".to_string(),
hits: 1,
errors: 0,
duration: 10000000,
ok_summary: vec![0, 0, 0],
error_summary: vec![0, 0, 0],
synthetics: false,
top_level_hits: 1,
span_kind: "".to_string(),
peer_tags: vec![],
is_trace_root: NotSet.into(),
}],
agent_time_shift: 0,
}],
lang: "javascript".to_string(),
tracer_version: "1.0.0".to_string(),
runtime_id: "00000000-0000-0000-0000-000000000000".to_string(),
sequence: 1,
agent_aggregation: "".to_string(),
service: "".to_string(),
container_id: "".to_string(),
tags: vec![],
git_commit_sha: "".to_string(),
image_tag: "".to_string(),
};

assert_eq!(deserialized_stats_json, client_stats_payload)
}
}
113 changes: 113 additions & 0 deletions trace-utils/src/stats_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,3 +79,116 @@ pub async fn send_stats_payload(
Err(e) => anyhow::bail!("Failed to send trace stats: {e}"),
}
}

#[cfg(test)]
mod tests {
use crate::stats_utils;
use datadog_trace_protobuf::pb::{
ClientGroupedStats, ClientStatsBucket, ClientStatsPayload, Trilean::NotSet,
};
use hyper::Request;
use serde_json::Value;

#[tokio::test]
async fn test_get_stats_from_request_body() {
let stats_json = r#"{
"Hostname": "TestHost",
"Env": "test",
"Version": "1.0.0",
"Stats": [
{
"Start": 0,
"Duration": 10000000000,
"Stats": [
{
"Name": "test-span",
"Service": "test-service",
"Resource": "test-span",
"Type": "",
"HTTPStatusCode": 0,
"Synthetics": false,
"Hits": 1,
"TopLevelHits": 1,
"Errors": 0,
"Duration": 10000000,
"OkSummary": [
0,
0,
0
],
"ErrorSummary": [
0,
0,
0
]
}
]
}
],
"Lang": "javascript",
"TracerVersion": "1.0.0",
"RuntimeID": "00000000-0000-0000-0000-000000000000",
"Sequence": 1
}"#;

let v: Value = match serde_json::from_str(stats_json) {
Ok(value) => value,
Err(err) => {
panic!("Failed to parse stats JSON: {}", err);
}
};

let bytes = rmp_serde::to_vec(&v).unwrap();
let request = Request::builder()
.body(hyper::body::Body::from(bytes))
.unwrap();

let res = stats_utils::get_stats_from_request_body(request.into_body()).await;

let client_stats_payload = ClientStatsPayload {
hostname: "TestHost".to_string(),
env: "test".to_string(),
version: "1.0.0".to_string(),
stats: vec![ClientStatsBucket {
start: 0,
duration: 10000000000,
stats: vec![ClientGroupedStats {
service: "test-service".to_string(),
name: "test-span".to_string(),
resource: "test-span".to_string(),
http_status_code: 0,
r#type: "".to_string(),
db_type: "".to_string(),
hits: 1,
errors: 0,
duration: 10000000,
ok_summary: vec![0, 0, 0],
error_summary: vec![0, 0, 0],
synthetics: false,
top_level_hits: 1,
span_kind: "".to_string(),
peer_tags: vec![],
is_trace_root: NotSet.into(),
}],
agent_time_shift: 0,
}],
lang: "javascript".to_string(),
tracer_version: "1.0.0".to_string(),
runtime_id: "00000000-0000-0000-0000-000000000000".to_string(),
sequence: 1,
agent_aggregation: "".to_string(),
service: "".to_string(),
container_id: "".to_string(),
tags: vec![],
git_commit_sha: "".to_string(),
image_tag: "".to_string(),
};

assert!(
res.is_ok(),
"Expected Ok result, but got Err: {}",
res.unwrap_err()
);
assert_eq!(res.unwrap(), client_stats_payload)
}
}

0 comments on commit d4d96ba

Please sign in to comment.