From 689c42263a0e85e8a44001d473ad37f310c3b823 Mon Sep 17 00:00:00 2001 From: Oliver Gould Date: Thu, 19 Apr 2018 16:57:40 -0700 Subject: [PATCH] proxy: Add destination labels to TapEvents (#814) The Tap API supports key-value labels on endpoint metadata. The proxy was not setting these labels previously. In order to add these labels onto tap events, we store the original set of labels in an `Arc` on `DstLabels`. When tap events are emitted, the destination' labels are copied from the `DstLabels` into each event. --- proxy/src/control/pb.rs | 8 +++++- proxy/src/telemetry/metrics/labels.rs | 35 ++++++++++++++++++++++++--- 2 files changed, 38 insertions(+), 5 deletions(-) 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) } }