Extend the datapoint macro to support group-by operations. (#25385)
#### Problem Our existing datapoint macro syntax does directly not support group-by tags. The existing workaround is to embed the group-by tags into the metric name which does not scale well. #### Summary of Changes This PR extends the existing syntax to support group-by tags as follows. The new syntax is also compatible with the existing syntax: ``` datapoint_debug!( "metric_name", "tag" => "tag-value", "tag2" => "tag-value2", .... ("field1", 100, i64), // field syntax is the same as the current syntax. ("field2", "hello", String), ... ); ```
This commit is contained in:
parent
5b67960c76
commit
6de2884969
|
@ -12,6 +12,12 @@
|
|||
//! The matric macro consists of the following three main parts:
|
||||
//! - name: the name of the metric.
|
||||
//!
|
||||
//! - tags (optional): when a metric sample is reported with tags, you can use
|
||||
//! group-by when querying the reported samples. Each metric sample can be
|
||||
//! attached with zero to many tags. Each tag is of the format:
|
||||
//!
|
||||
//! - "tag-name" => "tag-value"
|
||||
//!
|
||||
//! - fields (optional): fields are the main content of a metric sample. The
|
||||
//! macro supports four different types of fields: bool, i64, f64, and String.
|
||||
//! Here're their syntax:
|
||||
|
@ -25,6 +31,8 @@
|
|||
//!
|
||||
//! datapoint_debug!(
|
||||
//! "name-of-the-metric",
|
||||
//! "tag" => "tag-value",
|
||||
//! "tag2" => "tag-value2",
|
||||
//! ("some-bool", false, bool),
|
||||
//! ("some-int", 100, i64),
|
||||
//! ("some-float", 1.05, f64),
|
||||
|
@ -37,6 +45,8 @@ use std::{fmt, time::SystemTime};
|
|||
pub struct DataPoint {
|
||||
pub name: &'static str,
|
||||
pub timestamp: SystemTime,
|
||||
/// tags are eligible for group-by operations.
|
||||
pub tags: Vec<(&'static str, &'static str)>,
|
||||
pub fields: Vec<(&'static str, String)>,
|
||||
}
|
||||
|
||||
|
@ -45,10 +55,16 @@ impl DataPoint {
|
|||
DataPoint {
|
||||
name,
|
||||
timestamp: SystemTime::now(),
|
||||
tags: vec![],
|
||||
fields: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_tag(&mut self, name: &'static str, value: &'static str) -> &mut Self {
|
||||
self.tags.push((name, value));
|
||||
self
|
||||
}
|
||||
|
||||
pub fn add_field_str(&mut self, name: &'static str, value: &str) -> &mut Self {
|
||||
self.fields
|
||||
.push((name, format!("\"{}\"", value.replace('\"', "\\\""))));
|
||||
|
@ -74,6 +90,9 @@ impl DataPoint {
|
|||
impl fmt::Display for DataPoint {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "datapoint: {}", self.name)?;
|
||||
for tag in &self.tags {
|
||||
write!(f, ",{}={}", tag.0, tag.1)?;
|
||||
}
|
||||
for field in &self.fields {
|
||||
write!(f, " {}={}", field.0, field.1)?;
|
||||
}
|
||||
|
@ -95,8 +114,22 @@ macro_rules! create_datapoint {
|
|||
(@field $point:ident $name:expr, $value:expr, bool) => {
|
||||
$point.add_field_bool($name, $value as bool);
|
||||
};
|
||||
(@tag $point:ident $tag_name:expr, $tag_value:expr) => {
|
||||
$point.add_tag($tag_name, &$tag_value);
|
||||
};
|
||||
|
||||
(@fields $point:ident) => {};
|
||||
|
||||
// process tags
|
||||
(@fields $point:ident $tag_name:expr => $tag_value:expr, $($rest:tt)*) => {
|
||||
$crate::create_datapoint!(@tag $point $tag_name, $tag_value);
|
||||
$crate::create_datapoint!(@fields $point $($rest)*);
|
||||
};
|
||||
(@fields $point:ident $tag_name:expr => $tag_value:expr) => {
|
||||
$crate::create_datapoint!(@tag $point $tag_name, $tag_value);
|
||||
};
|
||||
|
||||
// process fields
|
||||
(@fields $point:ident ($name:expr, $value:expr, $type:ident) , $($rest:tt)*) => {
|
||||
$crate::create_datapoint!(@field $point $name, $value, $type);
|
||||
$crate::create_datapoint!(@fields $point $($rest)*);
|
||||
|
@ -218,6 +251,7 @@ mod test {
|
|||
("bool", true, bool)
|
||||
);
|
||||
assert_eq!(point.name, "name");
|
||||
assert_eq!(point.tags.len(), 0);
|
||||
assert_eq!(point.fields[0], ("i64", "1i".to_string()));
|
||||
assert_eq!(
|
||||
point.fields[1],
|
||||
|
@ -226,4 +260,69 @@ mod test {
|
|||
assert_eq!(point.fields[2], ("f64", "12.34".to_string()));
|
||||
assert_eq!(point.fields[3], ("bool", "true".to_string()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_datapoint_with_tags() {
|
||||
datapoint_debug!("name", "tag" => "tag-value", ("field name", "test", String));
|
||||
datapoint_info!(
|
||||
"name",
|
||||
"tag" => "tag-value",
|
||||
"tag2" => "tag-value-2",
|
||||
("field name", 12.34_f64, f64)
|
||||
);
|
||||
datapoint_trace!(
|
||||
"name",
|
||||
"tag" => "tag-value",
|
||||
"tag2" => "tag-value-2",
|
||||
"tag3" => "tag-value-3",
|
||||
("field name", true, bool)
|
||||
);
|
||||
datapoint_warn!("name", "tag" => "tag-value");
|
||||
datapoint_error!("name", "tag" => "tag-value", ("field name", 1, i64),);
|
||||
datapoint!(
|
||||
log::Level::Warn,
|
||||
"name",
|
||||
"tag" => "tag-value",
|
||||
("field1 name", 2, i64),
|
||||
("field2 name", 2, i64)
|
||||
);
|
||||
datapoint_info!("name", ("field1 name", 2, i64), ("field2 name", 2, i64),);
|
||||
datapoint_trace!(
|
||||
"name",
|
||||
"tag" => "tag-value",
|
||||
("field1 name", 2, i64),
|
||||
("field2 name", 2, i64),
|
||||
("field3 name", 3, i64)
|
||||
);
|
||||
datapoint!(
|
||||
log::Level::Error,
|
||||
"name",
|
||||
"tag" => "tag-value",
|
||||
("field1 name", 2, i64),
|
||||
("field2 name", 2, i64),
|
||||
("field3 name", 3, i64),
|
||||
);
|
||||
|
||||
let point = create_datapoint!(
|
||||
@point "name",
|
||||
"tag1" => "tag-value-1",
|
||||
"tag2" => "tag-value-2",
|
||||
"tag3" => "tag-value-3",
|
||||
("i64", 1, i64),
|
||||
("String", "string space string", String),
|
||||
("f64", 12.34_f64, f64),
|
||||
("bool", true, bool)
|
||||
);
|
||||
assert_eq!(point.name, "name");
|
||||
assert_eq!(point.fields[0], ("i64", "1i".to_string()));
|
||||
assert_eq!(
|
||||
point.fields[1],
|
||||
("String", "\"string space string\"".to_string())
|
||||
);
|
||||
assert_eq!(point.fields[2], ("f64", "12.34".to_string()));
|
||||
assert_eq!(point.fields[3], ("bool", "true".to_string()));
|
||||
assert_eq!(point.tags[0], ("tag1", "tag-value-1"));
|
||||
assert_eq!(point.tags[1], ("tag2", "tag-value-2"));
|
||||
assert_eq!(point.tags[2], ("tag3", "tag-value-3"));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -87,6 +87,9 @@ pub fn serialize_points(points: &Vec<DataPoint>, host_id: &str) -> String {
|
|||
for (name, value) in &point.fields {
|
||||
len += name.len() + value.len() + EXTRA_LEN;
|
||||
}
|
||||
for (name, value) in &point.tags {
|
||||
len += name.len() + value.len() + EXTRA_LEN;
|
||||
}
|
||||
len += point.name.len();
|
||||
len += TIMESTAMP_LEN;
|
||||
len += host_id.len() + HOST_ID_LEN;
|
||||
|
@ -94,6 +97,9 @@ pub fn serialize_points(points: &Vec<DataPoint>, host_id: &str) -> String {
|
|||
let mut line = String::with_capacity(len);
|
||||
for point in points {
|
||||
let _ = write!(line, "{},host_id={}", &point.name, host_id);
|
||||
for (name, value) in point.tags.iter() {
|
||||
let _ = write!(line, ",{}={}", name, value);
|
||||
}
|
||||
|
||||
let mut first = true;
|
||||
for (name, value) in point.fields.iter() {
|
||||
|
|
Loading…
Reference in New Issue