use chrono::{DateTime, SecondsFormat, Utc};
use crate::{
context::TelemetryContext,
contracts::{SeverityLevel as ContractsSeverityLevel, *},
telemetry::{ContextTags, Measurements, Properties, Telemetry},
time,
};
#[derive(Debug)]
pub struct TraceTelemetry {
message: String,
severity: SeverityLevel,
timestamp: DateTime<Utc>,
properties: Properties,
tags: ContextTags,
measurements: Measurements,
}
impl TraceTelemetry {
pub fn new(message: impl Into<String>, severity: SeverityLevel) -> Self {
Self {
message: message.into(),
severity,
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 TraceTelemetry {
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, TraceTelemetry)> for Envelope {
fn from((context, telemetry): (TelemetryContext, TraceTelemetry)) -> Self {
Self {
name: "Microsoft.ApplicationInsights.Message".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::MessageData(MessageData {
message: telemetry.message,
severity_level: Some(telemetry.severity.into()),
properties: Some(Properties::combine(context.properties, telemetry.properties).into()),
measurements: Some(telemetry.measurements.into()),
..MessageData::default()
}))),
..Envelope::default()
}
}
}
#[derive(Debug)]
pub enum SeverityLevel {
Verbose,
Information,
Warning,
Error,
Critical,
}
impl From<SeverityLevel> for ContractsSeverityLevel {
fn from(severity: SeverityLevel) -> Self {
match severity {
SeverityLevel::Verbose => ContractsSeverityLevel::Verbose,
SeverityLevel::Information => ContractsSeverityLevel::Information,
SeverityLevel::Warning => ContractsSeverityLevel::Warning,
SeverityLevel::Error => ContractsSeverityLevel::Error,
SeverityLevel::Critical => ContractsSeverityLevel::Critical,
}
}
}
#[cfg(test)]
mod tests {
use std::collections::BTreeMap;
use chrono::{TimeZone, Utc};
use super::{SeverityLevel, TraceTelemetry};
use crate::{
contracts::{Base, Data, Envelope, MessageData},
telemetry::{ContextTags, Properties, Telemetry},
time, TelemetryContext,
};
#[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 = TraceTelemetry::new("message", SeverityLevel::Information);
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.Message".into(),
time: "2019-01-02T03:04:05.800Z".into(),
i_key: Some("instrumentation".into()),
tags: Some(BTreeMap::default()),
data: Some(Base::Data(Data::MessageData(MessageData {
message: "message".into(),
severity_level: Some(crate::contracts::SeverityLevel::Information),
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
}),
..MessageData::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 = TraceTelemetry::new("message", SeverityLevel::Information);
telemetry.tags_mut().insert("no-write".into(), "ok".into());
let envelop = Envelope::from((context, telemetry));
let expected = Envelope {
name: "Microsoft.ApplicationInsights.Message".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::MessageData(MessageData {
message: "message".into(),
severity_level: Some(crate::contracts::SeverityLevel::Information),
properties: Some(BTreeMap::default()),
measurements: Some(BTreeMap::default()),
..MessageData::default()
}))),
..Envelope::default()
};
assert_eq!(envelop, expected)
}
}