13 releases
0.2.5 | May 11, 2024 |
---|---|
0.2.4 | Sep 8, 2023 |
0.2.3 | Jun 22, 2023 |
0.2.2 | Mar 20, 2023 |
0.1.4 | Jul 16, 2020 |
#280 in Encoding
53,112 downloads per month
Used in 2 crates
78KB
2K
SLoC
Serde Prometheus
A serde implementation for Prometheus' text-based exposition format.
lib.rs
:
A serde implementation for Prometheus' text-based exposition format.
Currently this library only supports serialisation to Prometheus' format for exporting metrics but this might be extended to deserialisation later on down the line.
serde_prometheus
will work with most metric libraries' structs out of the
box, however some work may be required to get them into a format expected
by Prometheus.
Metric names exposed in the format are derived from the value's name in the struct or map that contains it.
Basic Usage
#[derive(Serialize)]
struct HitCount(u64);
#[derive(Serialize)]
struct MetricRegistry {
my_struct: MyStructMetrics
}
#[derive(Serialize)]
struct MyStructMetrics {
hit_count: HitCount
}
let metrics = MetricRegistry {
my_struct: MyStructMetrics {
hit_count: HitCount(30)
}
};
assert_eq!(
serde_prometheus::to_string(&metrics, None, HashMap::new())?,
"hit_count{path = \"my_struct\"} 30\n"
);
Global Labels
Global labels can be added to all metrics exported by serde_prometheus
using
the HashMap
(or any type resolving to IntoIterator<Borrow<(&str, &str)>>
)
passed into serde_prometheus::to_string
for example:
#
#
#
let mut labels = HashMap::new();
labels.insert("my_key", "my_value");
let serialised = serde_prometheus::to_string(&metrics, None, &[("my_key", "my_value")])?;
assert_eq!(serialised, "hit_count{my_key = \"my_value\", path = \"my_struct\"} 30\n");
Global Prefix
And a global prefix can be added to all metrics:
#
#
#
assert_eq!(
serde_prometheus::to_string(&metrics, Some("my_prefix"), HashMap::new())?,
"my_prefix_hit_count{path = \"my_struct\"} 30\n"
);
Metadata/key manipulation
Serde's newtype implementation is (ab)used by serde_prometheus
to add metadata
to serialised fields without breaking backwards compatibility with serde_json
and such.
For example, serde_prometheus
support has been added to metered-rs's
histograms whilst still keeping the same JSON schema, it does this by using
a call to serialize_newtype_struct
in a struct's Serialize trait impl, the
format for the type names is as follows:
keymodifiers|key=value,key2=value2
Modifiers can also be used in labels using a ==
like so:
|key3==modifiers
The path
stack is reset for each label value, however after applying the key
modifiers, it is retained when writing the path
label itself.
The modifiers that can be used are:
Modifier | Description |
---|---|
< | Pops a value off of the path stack and appends it to the name |
! | Pops the last value off of the path stack and drops it |
- | Moves the stack cursor back a position, when a value is popped off the stack, the position is reset back to the top of the stack |
. | The default behaviour of serde_prometheus 0.1 is to append the collected stack to the next value in path (as if an extra < was added to your modifiers), to prevent this use this modifier. This has no effect in labels. |
These can be combined and are read from left to right, for example:
#
#
#
struct HitCount(u64);
impl Serialize for HitCount {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
// metric name: include our key (hit_count), ignore the second (my_method), and include the third (my_struct)
// metric meta: for the method_name, ignore our key (hit_count), include the second (my_method)
serializer.serialize_newtype_struct("<!<|my_key=my_value,method_name==!<", &self.0)
}
}
let metrics = MetricRegistry {
my_struct: MyStructMetrics {
my_method: MyMethodMetrics {
hit_count: HitCount(30)
}
}
};
let serialised = serde_prometheus::to_string(&metrics, None, HashMap::new())?;
assert_eq!(
serialised,
// would be `hit_count{path = "my_struct/my_method"}` without the Serialize impl
"my_struct_hit_count{my_key = \"my_value\", method_name = \"my_method\"} 30\n"
);
Internal overrides
serialize_newtype_struct
supports an extra section to allow overriding of internal operations,
as follows:
keymodifiers|key=value,key2=value2|:internal=abc
Internal overrides are always prefixed with a :
to make it plainly obvious they're not labels,
a list of internal overrides available for use are as follows:
Override | Description |
---|---|
:namespace | Overrides the global namespace with a new value for that struct and any leaves under it |
Label concatenation
By default, when added via a serialize_newtype_struct
call, a new label added by a
"deeper" value will override a previously set one set further up the stack. This can
be overridden by the "deeper" value by appending [::]
to the label name, this will
concatenate the previously set label and the new label with a ::
. The ::
can be
anything valid in a Prometheus label value except =
.
fn add_my_cool_key<S: serde::Serializer, T: serde::Serialize>(
value: &T,
serializer: S,
) -> Result<S::Ok, S::Error> {
serializer.serialize_newtype_struct("!|my_cool_key[::]==<", value)
}
#[derive(Serialize)]
struct MetricRegistry {
#[serde(serialize_with = "add_my_cool_key")]
my_struct: MyStructMetrics
}
#[derive(Serialize)]
struct MyStructMetrics {
#[serde(serialize_with = "add_my_cool_key")]
my_method: MyMethodMetrics
}
#[derive(Serialize)]
struct MyMethodMetrics {
hit_count: HitCount
}
#
let metrics = MetricRegistry {
my_struct: MyStructMetrics {
my_method: MyMethodMetrics {
hit_count: HitCount(30)
}
}
};
let serialised = serde_prometheus::to_string(&metrics, None, HashMap::new())?;
assert_eq!(
serialised,
"hit_count{my_cool_key = \"my_struct::my_method\"} 30\n"
);
Dependencies
~2.1–3MB
~61K SLoC