use std::time::Duration as StdDuration;
use chrono::{DateTime, SecondsFormat, Utc};
use crate::context::TelemetryContext;
use crate::contracts::*;
use crate::telemetry::{ContextTags, Measurements, Properties, Telemetry};
use crate::time::{self, Duration};
use crate::uuid::Uuid;
pub struct RemoteDependencyTelemetry {
id: Option<Uuid>,
name: String,
duration: Duration,
result_code: Option<String>,
success: bool,
data: Option<String>,
dependency_type: String,
target: String,
timestamp: DateTime<Utc>,
properties: Properties,
tags: ContextTags,
measurements: Measurements,
}
impl RemoteDependencyTelemetry {
pub fn new(name: String, dependency_type: String, duration: StdDuration, target: String, success: bool) -> Self {
Self {
id: Option::default(),
name,
duration: duration.into(),
result_code: Option::default(),
success,
data: Option::default(),
dependency_type,
target,
timestamp: time::now(),
properties: Properties::default(),
tags: ContextTags::default(),
measurements: Measurements::default(),
}
}
pub fn measurements(&self) -> &Measurements {
&self.measurements
}
pub fn measurements_mut(&mut self) -> &mut Measurements {
&mut self.measurements
}
}
impl Telemetry for RemoteDependencyTelemetry {
fn timestamp(&self) -> DateTime<Utc> {
self.timestamp
}
fn properties(&self) -> &Properties {
&self.properties
}
fn properties_mut(&mut self) -> &mut Properties {
&mut self.properties
}
fn tags(&self) -> &ContextTags {
&self.tags
}
fn tags_mut(&mut self) -> &mut ContextTags {
&mut self.tags
}
}
impl From<(TelemetryContext, RemoteDependencyTelemetry)> for Envelope {
fn from((context, telemetry): (TelemetryContext, RemoteDependencyTelemetry)) -> Self {
Self {
name: "Microsoft.ApplicationInsights.RemoteDependency".into(),
time: telemetry.timestamp.to_rfc3339_opts(SecondsFormat::Millis, true),
i_key: Some(context.i_key),
tags: Some(ContextTags::combine(context.tags, telemetry.tags).into()),
data: Some(Base::Data(Data::RemoteDependencyData(RemoteDependencyData {
name: telemetry.name,
id: telemetry.id.map(|id| id.to_hyphenated().to_string()),
result_code: telemetry.result_code,
duration: telemetry.duration.to_string(),
success: Some(telemetry.success),
data: telemetry.data,
target: Some(telemetry.target),
type_: Some(telemetry.dependency_type),
properties: Some(Properties::combine(context.properties, telemetry.properties).into()),
measurements: Some(telemetry.measurements.into()),
..RemoteDependencyData::default()
}))),
..Envelope::default()
}
}
}
#[cfg(test)]
mod tests {
use std::collections::BTreeMap;
use chrono::TimeZone;
use super::*;
#[test]
fn it_overrides_properties_from_context() {
time::set(Utc.ymd(2019, 1, 2).and_hms_milli(3, 4, 5, 800));
let mut context =
TelemetryContext::new("instrumentation".into(), ContextTags::default(), Properties::default());
context.properties_mut().insert("test".into(), "ok".into());
context.properties_mut().insert("no-write".into(), "fail".into());
let mut telemetry = RemoteDependencyTelemetry::new(
"GET https://example.com/main.html".into(),
"HTTP".into(),
StdDuration::from_secs(2),
"example.com".into(),
true,
);
telemetry.properties_mut().insert("no-write".into(), "ok".into());
telemetry.measurements_mut().insert("latency".into(), 200.0);
let envelop = Envelope::from((context, telemetry));
let expected = Envelope {
name: "Microsoft.ApplicationInsights.RemoteDependency".into(),
time: "2019-01-02T03:04:05.800Z".into(),
i_key: Some("instrumentation".into()),
tags: Some(BTreeMap::default()),
data: Some(Base::Data(Data::RemoteDependencyData(RemoteDependencyData {
name: "GET https://example.com/main.html".into(),
duration: "0.00:00:02.0000000".into(),
success: Some(true),
target: Some("example.com".into()),
type_: Some("HTTP".into()),
properties: Some({
let mut properties = BTreeMap::default();
properties.insert("test".into(), "ok".into());
properties.insert("no-write".into(), "ok".into());
properties
}),
measurements: Some({
let mut measurements = BTreeMap::default();
measurements.insert("latency".into(), 200.0);
measurements
}),
..RemoteDependencyData::default()
}))),
..Envelope::default()
};
assert_eq!(envelop, expected)
}
#[test]
fn it_overrides_tags_from_context() {
time::set(Utc.ymd(2019, 1, 2).and_hms_milli(3, 4, 5, 700));
let mut context =
TelemetryContext::new("instrumentation".into(), ContextTags::default(), Properties::default());
context.tags_mut().insert("test".into(), "ok".into());
context.tags_mut().insert("no-write".into(), "fail".into());
let mut telemetry = RemoteDependencyTelemetry::new(
"GET https://example.com/main.html".into(),
"HTTP".into(),
StdDuration::from_secs(2),
"example.com".into(),
true,
);
telemetry.tags_mut().insert("no-write".into(), "ok".into());
let envelop = Envelope::from((context, telemetry));
let expected = Envelope {
name: "Microsoft.ApplicationInsights.RemoteDependency".into(),
time: "2019-01-02T03:04:05.700Z".into(),
i_key: Some("instrumentation".into()),
tags: Some({
let mut tags = BTreeMap::default();
tags.insert("test".into(), "ok".into());
tags.insert("no-write".into(), "ok".into());
tags
}),
data: Some(Base::Data(Data::RemoteDependencyData(RemoteDependencyData {
name: "GET https://example.com/main.html".into(),
duration: "0.00:00:02.0000000".into(),
success: Some(true),
target: Some("example.com".into()),
type_: Some("HTTP".into()),
properties: Some(BTreeMap::default()),
measurements: Some(BTreeMap::default()),
..RemoteDependencyData::default()
}))),
..Envelope::default()
};
assert_eq!(envelop, expected)
}
}