Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@
**/*.rs.bk
Cargo.lock
.DS_Store
.idea
.idea
52 changes: 48 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,24 +69,37 @@ struct MyMetric {

As with any Telegraf point, tags are optional but at least one field is required.

Timestamps are optional and can be set via the `timestamp` attribute:

```rust
use telegraf::*;

#[derive(Metric)]
struct MyMetric {
#[telegraf(timestamp)]
ts: u64,
field1: i32,
}
```

## Use the `point` macro to do ad-hoc metrics

```rust
use telegraf::*;

let mut client = Client::new("tcp://localhost:8094").unwrap();

let p = point!("measurement", ("tag1", "tag1Val"), ("field1", "val") ("field2", 10));
let p = point!("measurement", ("tag1", "tag1Val"), ("field1", "val") ("field2", 10); 100);
client.write_point(&p);
```

The macro syntax is the following format:

```
(<measurement>, [(<tagName>, <tagVal>)], [(<fieldName>, <fieldVal>)])
(<measurement>, [(<tagName>, <tagVal>)], [(<fieldName>, <fieldVal>)]; <timestamp>)
```

Measurement name, tag set, and field set are comma separated. Tag and field tuples are space separated. The tag set is optional.
Measurement name, tag set, and field set are comma separated. Tag and field tuples are space separated. Timestamp is semicolon separated. The tag set and timestamp are optional.

## Manual `Point` initialization

Expand All @@ -104,7 +117,8 @@ let p = Point::new(
(String::from("field1"), Box::new(10)),
(String::from("field2"), Box::new(20.5)),
(String::from("field3"), Box::new("anything!"))
]
],
Some(100),
);

c.write_point(p)
Expand All @@ -121,3 +135,33 @@ pub trait IntoFieldData {
```

Out of the box implementations are provided for many common data types, but manual implementation is possible for other data types.

### Timestamps

Timestamps are optional. If not present, the Telegraf daemon will set the timestamp using the current time.
Timestamps are specified in nanosecond-precision Unix time, therefore `u64` must implement the `From<T>` trait for the field type, if the implementation is not already present:

```rust
use telegraf::*;

#[derive(Copy, Clone)]
struct MyType {
// ...
}

impl From<MyType> for u64 {
fn from(my_type: MyType) -> Self {
todo!()
}
}

#[derive(Metric)]
struct MyMetric {
#[telegraf(timestamp)]
ts: MyType,
field1: i32,
}

```

More information about timestamps can be found [here](https://docs.influxdata.com/influxdb/v1.8/write_protocols/line_protocol_tutorial/#timestamp).
112 changes: 105 additions & 7 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@
//! client.write(&point);
//! ```
//!
//! As with any Telegraf point, tags are optional but at least one field
//! is required.
//!
//! By default the measurement name will be the same as the struct. You can
//! override this via derive attributes:
//!
Expand All @@ -44,8 +47,18 @@
//! }
//! ```
//!
//! As with any Telegraf point, tags are optional but at least one field
//! is required.
//! Timestamps are optional and can be set via the `timestamp` attribute:
//!
//! ```rust
//! use telegraf::*;
//!
//! #[derive(Metric)]
//! struct MyMetric {
//! #[telegraf(timestamp)]
//! ts: u64,
//! field1: i32,
//! }
//! ```
//!
//! ## Use the [crate::point] macro to do ad-hoc metrics.
//!
Expand All @@ -60,10 +73,11 @@
//!
//! The macro syntax is the following format:
//!
//! `(<measurement>, [(<tagName>, <tagVal>)], [(<fieldName>, <fieldVal>)])`
//! `(<measurement>, [(<tagName>, <tagVal>)], [(<fieldName>, <fieldVal>)]; <timestamp>)`
//!
//! Measurement name, tag set, and field set are space separated. Tag and field sets are space
//! separated. The tag set is optional.
//! Measurement name, tag set, and field set are comma separated. Tag and field
//! tuples are space separated. Timestamp is semicolon separated. The tag set and
//! timestamp are optional.
//!
//! ## Manual [crate::Point] initialization.
//!
Expand All @@ -81,7 +95,8 @@
//! (String::from("field1"), Box::new(10)),
//! (String::from("field2"), Box::new(20.5)),
//! (String::from("field3"), Box::new("anything!"))
//! ]
//! ],
//! Some(100),
//! );
//!
//! c.write_point(&p);
Expand All @@ -100,6 +115,36 @@
//! ```
//!
//! Out of the box implementations are provided for many common data types, but manual implementation is possible for other data types.
//!
//! ### Timestamps
//!
//! Timestamps are an optional filed, if not present the Telegraf daemon will set the timestamp using the current time.
//! Timestamps are specified in nanosecond-precision Unix time, therefore `u64` must implement the `From<T>` trait for the field type, if the implementation is not already present:
//!
//! ```rust
//! use telegraf::*;
//!
//! #[derive(Copy, Clone)]
//! struct MyType {
//! // ...
//! }
//!
//! impl From<MyType> for u64 {
//! fn from(my_type: MyType) -> Self {
//! todo!()
//! }
//! }
//!
//! #[derive(Metric)]
//! struct MyMetric {
//! #[telegraf(timestamp)]
//! ts: MyType,
//! field1: i32,
//! }
//!
//! ```
//!
//! More information about timestamps can be found [here](https://docs.influxdata.com/influxdb/v1.8/write_protocols/line_protocol_tutorial/#timestamp).

pub mod macros;
pub mod protocol;
Expand Down Expand Up @@ -144,6 +189,8 @@ pub type TelegrafResult = Result<(), TelegrafError>;
/// #[telegraf(tag)]
/// tag1: String,
/// field2: f32,
/// #[telegraf(timestamp)]
/// ts: u64,
/// }
/// ```
pub trait Metric {
Expand Down Expand Up @@ -176,6 +223,7 @@ pub struct Point {
pub measurement: String,
pub tags: Vec<Tag>,
pub fields: Vec<Field>,
pub timestamp: Option<Timestamp>,
}

/// Connection client used to handle socket connection management
Expand All @@ -200,6 +248,7 @@ impl Point {
measurement: String,
tags: Vec<(String, String)>,
fields: Vec<(String, Box<dyn IntoFieldData>)>,
timestamp: Option<u64>,
) -> Self {
let t = tags
.into_iter()
Expand All @@ -212,23 +261,36 @@ impl Point {
value: v.field_data(),
})
.collect();
let ts = timestamp.map(|t| Timestamp { value: t });
Self {
measurement,
tags: t,
fields: f,
timestamp: ts,
}
}

fn to_lp(&self) -> LineProtocol {
let tag_attrs: Vec<Attr> = self.tags.iter().cloned().map(Attr::Tag).collect();
let field_attrs: Vec<Attr> = self.fields.iter().cloned().map(Attr::Field).collect();
let timestamp_attr: Vec<Attr> = self
.timestamp
.iter()
.cloned()
.map(Attr::Timestamp)
.collect();
let tag_str = if tag_attrs.is_empty() {
None
} else {
Some(format_attr(tag_attrs))
};
let field_str = format_attr(field_attrs);
LineProtocol::new(self.measurement.clone(), tag_str, field_str)
let timestamp_str = if timestamp_attr.is_empty() {
None
} else {
Some(format_attr(timestamp_attr))
};
LineProtocol::new(self.measurement.clone(), tag_str, field_str, timestamp_str)
}
}

Expand Down Expand Up @@ -391,6 +453,39 @@ impl<T> TelegrafUnwrap<T> for Option<T> {
mod tests {
use super::*;

#[test]
fn can_create_point_lp_ts_no_tags() {
let p = Point::new(
String::from("Foo"),
vec![],
vec![
("f1".to_owned(), Box::new(10)),
("f2".to_owned(), Box::new(10.3)),
],
Some(10),
);

let lp = p.to_lp();
assert_eq!(lp.to_str(), "Foo f1=10i,f2=10.3 10\n");
}

#[test]
fn can_create_point_lp_ts() {
let p = Point::new(
String::from("Foo"),
vec![("t1".to_owned(), "v".to_owned())],
vec![
("f1".to_owned(), Box::new(10)),
("f2".to_owned(), Box::new(10.3)),
("f3".to_owned(), Box::new("b")),
],
Some(10),
);

let lp = p.to_lp();
assert_eq!(lp.to_str(), "Foo,t1=v f1=10i,f2=10.3,f3=\"b\" 10\n");
}

#[test]
fn can_create_point_lp() {
let p = Point::new(
Expand All @@ -401,6 +496,7 @@ mod tests {
("f2".to_owned(), Box::new(10.3)),
("f3".to_owned(), Box::new("b")),
],
None,
);

let lp = p.to_lp();
Expand All @@ -416,7 +512,9 @@ mod tests {
("f1".to_owned(), Box::new(10)),
("f2".to_owned(), Box::new(10.3)),
],
None,
);

let lp = p.to_lp();
assert_eq!(lp.to_str(), "Foo f1=10i,f2=10.3\n");
}
Expand Down
Loading