From 4259d73d5c983cfd95ba684ea699eb55f290a924 Mon Sep 17 00:00:00 2001 From: Ruben Nijveld Date: Thu, 8 Sep 2022 09:16:19 +0200 Subject: [PATCH 1/2] Add encode implementations for SocketAddr and IpAddr Signed-off-by: Ruben Nijveld --- src/encoding/text.rs | 89 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 89 insertions(+) diff --git a/src/encoding/text.rs b/src/encoding/text.rs index 754b08a7..e9989d99 100644 --- a/src/encoding/text.rs +++ b/src/encoding/text.rs @@ -36,6 +36,7 @@ use crate::registry::{Registry, Unit}; use std::borrow::Cow; use std::collections::HashMap; use std::io::Write; +use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6}; use std::ops::Deref; /// Encode the metrics registered with the provided [`Registry`] into the @@ -206,6 +207,52 @@ impl Encode for () { } } +impl Encode for Ipv4Addr { + fn encode(&self, writer: &mut dyn Write) -> Result<(), std::io::Error> { + writer.write_all(self.to_string().as_bytes())?; + Ok(()) + } +} + +impl Encode for Ipv6Addr { + fn encode(&self, writer: &mut dyn Write) -> Result<(), std::io::Error> { + writer.write_all(self.to_string().as_bytes())?; + Ok(()) + } +} + +impl Encode for IpAddr { + fn encode(&self, writer: &mut dyn Write) -> Result<(), std::io::Error> { + match self { + IpAddr::V4(v4) => v4.encode(writer), + IpAddr::V6(v6) => v6.encode(writer), + } + } +} + +impl Encode for SocketAddrV4 { + fn encode(&self, writer: &mut dyn Write) -> Result<(), std::io::Error> { + writer.write_all(self.to_string().as_bytes())?; + Ok(()) + } +} + +impl Encode for SocketAddrV6 { + fn encode(&self, writer: &mut dyn Write) -> Result<(), std::io::Error> { + writer.write_all(self.to_string().as_bytes())?; + Ok(()) + } +} + +impl Encode for SocketAddr { + fn encode(&self, writer: &mut dyn Write) -> Result<(), std::io::Error> { + match self { + SocketAddr::V4(v4) => v4.encode(writer), + SocketAddr::V6(v6) => v6.encode(writer), + } + } +} + /// Helper type for [`EncodeMetric`], see [`EncodeMetric::encode`]. /// // `Encoder` does not take a trait parameter for `writer` and `labels` because @@ -705,6 +752,48 @@ mod tests { parse_with_python_client(String::from_utf8(encoded).unwrap()); } + #[test] + fn encode_socketaddr() { + let mut registry = Registry::default(); + let family = Family::, Counter>::default(); + registry.register("my_addr", "My socket address", family.clone()); + + let addr: SocketAddr = "127.0.0.1:80".parse().unwrap(); + family.get_or_create(&vec![("address", addr)]).inc(); + + let mut encoded = Vec::new(); + encode(&mut encoded, ®istry).unwrap(); + + let expected = "# HELP my_addr My socket address.\n".to_owned() + + "# TYPE my_addr counter\n" + + "my_addr_total{address=\"127.0.0.1:80\"} 1\n" + + "# EOF\n"; + assert_eq!(expected, String::from_utf8(encoded.clone()).unwrap()); + + parse_with_python_client(String::from_utf8(encoded).unwrap()); + } + + #[test] + fn encode_ipaddr() { + let mut registry = Registry::default(); + let family = Family::, Counter>::default(); + registry.register("my_addr", "My IP address", family.clone()); + + let addr: IpAddr = "::1".parse().unwrap(); + family.get_or_create(&vec![("address", addr)]).inc(); + + let mut encoded = Vec::new(); + encode(&mut encoded, ®istry).unwrap(); + + let expected = "# HELP my_addr My IP address.\n".to_owned() + + "# TYPE my_addr counter\n" + + "my_addr_total{address=\"::1\"} 1\n" + + "# EOF\n"; + assert_eq!(expected, String::from_utf8(encoded.clone()).unwrap()); + + parse_with_python_client(String::from_utf8(encoded).unwrap()); + } + #[test] fn encode_counter_family_with_prefix_with_label() { let mut registry = Registry::default(); From f8caf4166ba4a4195fd97e8ffffaac39fe233892 Mon Sep 17 00:00:00 2001 From: Ruben Nijveld Date: Fri, 14 Oct 2022 23:41:10 +0200 Subject: [PATCH 2/2] Added cardinality warning to IPAddr/SocketAddr implementations --- src/encoding/text.rs | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/src/encoding/text.rs b/src/encoding/text.rs index e9989d99..221234ec 100644 --- a/src/encoding/text.rs +++ b/src/encoding/text.rs @@ -207,6 +207,12 @@ impl Encode for () { } } +/// Warning: Using an IP address as a label is only useful when the number of +/// distinct values is low (i.e. low cardinality). In all other cases you should +/// combine your metrics into a single metric instead. Especially bad examples +/// are: storing separate metrics for each client connecting to your public +/// service or having a large fleet of servers and storing individual binding +/// addresses. impl Encode for Ipv4Addr { fn encode(&self, writer: &mut dyn Write) -> Result<(), std::io::Error> { writer.write_all(self.to_string().as_bytes())?; @@ -214,6 +220,12 @@ impl Encode for Ipv4Addr { } } +/// Warning: Using an IP address as a label is only useful when the number of +/// distinct values is low (i.e. low cardinality). In all other cases you should +/// combine your metrics into a single metric instead. Especially bad examples +/// are: storing separate metrics for each client connecting to your public +/// service or having a large fleet of servers and storing individual binding +/// addresses. impl Encode for Ipv6Addr { fn encode(&self, writer: &mut dyn Write) -> Result<(), std::io::Error> { writer.write_all(self.to_string().as_bytes())?; @@ -221,6 +233,12 @@ impl Encode for Ipv6Addr { } } +/// Warning: Using an IP address as a label is only useful when the number of +/// distinct values is low (i.e. low cardinality). In all other cases you should +/// combine your metrics into a single metric instead. Especially bad examples +/// are: storing separate metrics for each client connecting to your public +/// service or having a large fleet of servers and storing individual binding +/// addresses. impl Encode for IpAddr { fn encode(&self, writer: &mut dyn Write) -> Result<(), std::io::Error> { match self { @@ -230,6 +248,12 @@ impl Encode for IpAddr { } } +/// Warning: Using a socket address as a label is only useful when the number of +/// distinct values is low (i.e. low cardinality). In all other cases you should +/// combine your metrics into a single metric instead. Especially bad examples +/// are: storing separate metrics for each client connecting to your public +/// service or having a large fleet of servers and storing individual binding +/// addresses. impl Encode for SocketAddrV4 { fn encode(&self, writer: &mut dyn Write) -> Result<(), std::io::Error> { writer.write_all(self.to_string().as_bytes())?; @@ -237,6 +261,12 @@ impl Encode for SocketAddrV4 { } } +/// Warning: Using a socket address as a label is only useful when the number of +/// distinct values is low (i.e. low cardinality). In all other cases you should +/// combine your metrics into a single metric instead. Especially bad examples +/// are: storing separate metrics for each client connecting to your public +/// service or having a large fleet of servers and storing individual binding +/// addresses. impl Encode for SocketAddrV6 { fn encode(&self, writer: &mut dyn Write) -> Result<(), std::io::Error> { writer.write_all(self.to_string().as_bytes())?; @@ -244,6 +274,12 @@ impl Encode for SocketAddrV6 { } } +/// Warning: Using a socket address as a label is only useful when the number of +/// distinct values is low (i.e. low cardinality). In all other cases you should +/// combine your metrics into a single metric instead. Especially bad examples +/// are: storing separate metrics for each client connecting to your public +/// service or having a large fleet of servers and storing individual binding +/// addresses. impl Encode for SocketAddr { fn encode(&self, writer: &mut dyn Write) -> Result<(), std::io::Error> { match self {