use chrono::{DateTime, SecondsFormat, Utc};
use crate::{
context::TelemetryContext,
contracts::{Base, Data, Envelope, EventData},
telemetry::{ContextTags, Measurements, Properties, Telemetry},
time,
};
#[derive(Debug)]
pub struct EventTelemetry {
name: String,
timestamp: DateTime<Utc>,
properties: Properties,
tags: ContextTags,
measurements: Measurements,
}
impl EventTelemetry {
pub fn new(name: impl Into<String>) -> Self {
Self {
name: name.into(),
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 EventTelemetry {
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, EventTelemetry)> for Envelope {
fn from((context, telemetry): (TelemetryContext, EventTelemetry)) -> Self {
Self {
name: "Microsoft.ApplicationInsights.Event".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::EventData(EventData {
name: telemetry.name,
properties: Some(Properties::combine(context.properties, telemetry.properties).into()),
measurements: Some(telemetry.measurements.into()),
..EventData::default()
}))),
..Envelope::default()
}
}
}
#[cfg(test)]
mod tests {
use std::collections::BTreeMap;
use chrono::TimeZone;
use super::*;
use crate::time;
#[test]
fn it_overrides_properties_from_context() {
time::set(Utc.ymd(2019, 1, 2).and_hms_milli(3, 4, 5, 600));
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 = EventTelemetry::new("test");
telemetry.properties_mut().insert("no-write".into(), "ok".into());
telemetry.measurements_mut().insert("value".into(), 5.0);
let envelop = Envelope::from((context, telemetry));
let expected = Envelope {
name: "Microsoft.ApplicationInsights.Event".into(),
time: "2019-01-02T03:04:05.600Z".into(),
i_key: Some("instrumentation".into()),
tags: Some(BTreeMap::default()),
data: Some(Base::Data(Data::EventData(EventData {
name: "test".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("value".into(), 5.0);
measurements
}),
..EventData::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 = EventTelemetry::new("test");
telemetry.tags_mut().insert("no-write".into(), "ok".into());
let envelop = Envelope::from((context, telemetry));
let expected = Envelope {
name: "Microsoft.ApplicationInsights.Event".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::EventData(EventData {
name: "test".into(),
properties: Some(BTreeMap::default()),
measurements: Some(BTreeMap::default()),
..EventData::default()
}))),
..Envelope::default()
};
assert_eq!(envelop, expected)
}
}