diff --git a/proxy/src/control/pb.rs b/proxy/src/control/pb.rs index 00eb4f94c..79f87c216 100644 --- a/proxy/src/control/pb.rs +++ b/proxy/src/control/pb.rs @@ -45,10 +45,16 @@ impl event::StreamResponseEnd { eos, }; + let destination_meta = ctx.dst_labels() + .and_then(|b| b.borrow().clone()) + .map(|d| tap_event::EndpointMeta { + labels: d.as_map().clone(), + }); + common::TapEvent { source: Some((&ctx.server.remote).into()), destination: Some((&ctx.client.remote).into()), - destination_meta: None, + destination_meta, event: Some(tap_event::Event::Http(tap_event::Http { event: Some(tap_event::http::Event::ResponseEnd(end)), })), diff --git a/proxy/src/telemetry/metrics/labels.rs b/proxy/src/telemetry/metrics/labels.rs index bc580bde5..4e6821753 100644 --- a/proxy/src/telemetry/metrics/labels.rs +++ b/proxy/src/telemetry/metrics/labels.rs @@ -1,4 +1,6 @@ +use std::collections::HashMap; use std::fmt::{self, Write}; +use std::hash; use std::sync::Arc; use http; @@ -48,8 +50,11 @@ enum Direction { Outbound, } -#[derive(Clone, Debug, Hash, Eq, PartialEq)] -pub struct DstLabels(Arc); +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct DstLabels { + formatted: Arc, + original: Arc>, +} // ===== impl RequestLabels ===== @@ -204,27 +209,49 @@ impl DstLabels { let mut labels = labels.into_iter(); if let Some((k, v)) = labels.next() { + let mut original = HashMap::new(); + // Format the first label pair without a leading comma, since we // don't know where it is in the output labels at this point. let mut s = format!("dst_{}=\"{}\"", k, v); + original.insert(format!("{}", k), format!("{}", v)); // Format subsequent label pairs with leading commas, since // we know that we already formatted the first label pair. for (k, v) in labels { write!(s, ",dst_{}=\"{}\"", k, v) .expect("writing to string should not fail"); + original.insert(format!("{}", k), format!("{}", v)); } - Some(DstLabels(Arc::from(s))) + Some(DstLabels { + formatted: Arc::from(s), + original: Arc::new(original), + }) } else { // The iterator is empty; return None None } } + + pub fn as_map(&self) -> &HashMap { + &self.original + } + + pub fn as_str(&self) -> &str { + &self.formatted + } +} + +// Simply hash the formatted string and no other fields on `DstLabels`. +impl hash::Hash for DstLabels { + fn hash(&self, state: &mut H) { + self.formatted.hash(state) + } } impl fmt::Display for DstLabels { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", self.0) + self.formatted.fmt(f) } }