forked from metrics-rs/metrics
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feature: add a JSON observer (metrics-rs#38)
- Loading branch information
Showing
21 changed files
with
526 additions
and
226 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
/target | ||
**/*.rs.bk | ||
Cargo.lock |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
# The Code of Conduct | ||
|
||
This document is based on the [Rust Code of Conduct](https://www.rust-lang.org/conduct.html) and outlines the standard of conduct which is both expected and enforced as part of this project. | ||
|
||
## Conduct | ||
|
||
* We are committed to providing a friendly, safe and welcoming environment for all, regardless of level of experience, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, ethnicity, age, religion, nationality, or other similar characteristic. | ||
* Avoid using overtly sexual nicknames or other nicknames that might detract from a friendly, safe and welcoming environment for all. | ||
* Please be kind and courteous. There's no need to be mean or rude. | ||
* Respect that people have differences of opinion and that every design or implementation choice carries a trade-off and numerous costs. There is seldom a right answer. | ||
* Please keep unstructured critique to a minimum. If you have solid ideas you want to experiment with, make a fork and see how it works. | ||
* We will exclude you from interaction if you insult, demean or harass anyone. That is not welcome behaviour. We interpret the term "harassment" as including the definition in the [Citizen Code of Conduct](http://citizencodeofconduct.org/); if you have any lack of clarity about what might be included in that concept, please read their definition. In particular, we don't tolerate behavior that excludes people in socially marginalized groups. | ||
* Private harassment is also unacceptable. No matter who you are, if you feel you have been or are being harassed or made uncomfortable by a community member, please contact one of the repository Owners immediately. Whether you're a regular contributor or a newcomer, we care about making this community a safe place for you and we've got your back. | ||
* Likewise any spamming, trolling, flaming, baiting or other attention-stealing behaviour is not welcome. | ||
|
||
## Moderation | ||
|
||
These are the policies for upholding our community's standards of conduct. If you feel that a thread needs moderation, please use the contact information above, or mention @tobz or @LucioFranco in the thread. | ||
|
||
1. Remarks that violate this Code of Conduct, including hateful, hurtful, oppressive, or exclusionary remarks, are not allowed. (Cursing is allowed, but never targeting another user, and never in a hateful manner.) | ||
2. Remarks that moderators find inappropriate, whether listed in the code of conduct or not, are also not allowed. | ||
|
||
In the Rust community we strive to go the extra step to look out for each other. Don't just aim to be technically unimpeachable, try to be your best self. In particular, avoid flirting with offensive or sensitive issues, particularly if they're off-topic; this all too often leads to unnecessary fights, hurt feelings, and damaged trust; worse, it can drive people away from the community entirely. | ||
|
||
And if someone takes issue with something you said or did, resist the urge to be defensive. Just stop doing what it was they complained about and apologize. Even if you feel you were misinterpreted or unfairly accused, chances are good there was something you could've communicated better — remember that it's your responsibility to make your fellow Rustaceans comfortable. Everyone wants to get along and we are all here first and foremost because we want to talk about cool technology. You will find that people will be eager to assume good intent and forgive as long as you earn their trust. | ||
|
||
## Contacts: | ||
|
||
- Toby Lawrence ([[email protected]](mailto:[email protected])) | ||
- Lucio Franco ([[email protected]](mailto:[email protected])) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
[package] | ||
name = "metrics-observer-json" | ||
version = "0.1.0" | ||
authors = ["Toby Lawrence <[email protected]>"] | ||
edition = "2018" | ||
|
||
license = "MIT" | ||
|
||
description = "metric observer for JSON output" | ||
|
||
homepage = "https://github.com/metrics-rs/metrics" | ||
repository = "https://github.com/metrics-rs/metrics" | ||
documentation = "https://docs.rs/metrics-observer-text" | ||
readme = "README.md" | ||
|
||
keywords = ["metrics", "telemetry", "json"] | ||
|
||
[dependencies] | ||
metrics-core = { path = "../metrics-core", version = "^0.4" } | ||
metrics-util = { path = "../metrics-util", version = "^0.3" } | ||
hdrhistogram = "^6.1" | ||
serde_json = "^1.0" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
// Permission is hereby granted, free of charge, to any person obtaining a copy | ||
// of this software and associated documentation files (the "Software"), to deal | ||
// in the Software without restriction, including without limitation the rights | ||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
// copies of the Software, and to permit persons to whom the Software is | ||
// furnished to do so, subject to the following conditions: | ||
// | ||
// The above copyright notice and this permission notice shall be included in all | ||
// copies or substantial portions of the Software. | ||
// | ||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
// SOFTWARE. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
# metrics-observer-json | ||
|
||
[![conduct-badge][]][conduct] [![downloads-badge][] ![release-badge][]][crate] [![docs-badge][]][docs] [![license-badge][]](#license) | ||
|
||
[conduct-badge]: https://img.shields.io/badge/%E2%9D%A4-code%20of%20conduct-blue.svg | ||
[downloads-badge]: https://img.shields.io/crates/d/metrics-observer-json.svg | ||
[release-badge]: https://img.shields.io/crates/v/metrics-observer-json.svg | ||
[license-badge]: https://img.shields.io/crates/l/metrics-observer-json.svg | ||
[docs-badge]: https://docs.rs/metrics-observer-json/badge.svg | ||
[conduct]: https://github.com/metrics-rs/metrics-observer-json/blob/master/CODE_OF_CONDUCT.md | ||
[crate]: https://crates.io/crates/metrics-observer-json | ||
[docs]: https://docs.rs/metrics-observer-json | ||
|
||
__metrics-observer-json__ is a metric observer that outputs JSON. | ||
|
||
## code of conduct | ||
|
||
**NOTE**: All conversations and contributions to this project shall adhere to the [Code of Conduct][conduct]. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,187 @@ | ||
//! Observes metrics in a JSON format. | ||
//! | ||
//! Metric scopes are used to provide the hierarchy of metrics. As an example, for a | ||
//! snapshot with two metrics — `server.msgs_received` and `server.msgs_sent` — we would | ||
//! expect to see this output: | ||
//! | ||
//! ```c | ||
//! {"server":{"msgs_received":42,"msgs_sent":13}} | ||
//! ``` | ||
//! | ||
//! If we added another metric — `configuration_reloads` — we would expect to see: | ||
//! | ||
//! ```c | ||
//! {"configuration_reloads":2,"server":{"msgs_received":42,"msgs_sent":13}} | ||
//! ``` | ||
//! | ||
//! Metrics are sorted alphabetically. | ||
//! | ||
//! ## Histograms | ||
//! | ||
//! Histograms are rendered with a configurable set of quantiles that are provided when creating an | ||
//! instance of `JsonBuilder`. They are formatted using human-readable labels when displayed to | ||
//! the user. For example, 0.0 is rendered as "min", 1.0 as "max", and anything in between using | ||
//! the common "pXXX" format i.e. a quantile of 0.5 or percentile of 50 would be p50, a quantile of | ||
//! 0.999 or percentile of 99.9 would be p999, and so on. | ||
//! | ||
//! All histograms have the sample count of the histogram provided in the output. | ||
//! | ||
//! ```c | ||
//! {"connect_time count":15,"connect_time min":1334,"connect_time p50":1934,"connect_time | ||
//! p99":5330,"connect_time max":139389} | ||
//! ``` | ||
//! | ||
#![deny(missing_docs)] | ||
use hdrhistogram::Histogram; | ||
use metrics_core::{Builder, Drain, Key, Label, Observer}; | ||
use metrics_util::{parse_quantiles, MetricsTree, Quantile}; | ||
use std::collections::HashMap; | ||
|
||
/// Builder for [`JsonObserver`]. | ||
pub struct JsonBuilder { | ||
quantiles: Vec<Quantile>, | ||
pretty: bool, | ||
} | ||
|
||
impl JsonBuilder { | ||
/// Creates a new [`JsonBuilder`] with default values. | ||
pub fn new() -> Self { | ||
let quantiles = parse_quantiles(&[0.0, 0.5, 0.9, 0.95, 0.99, 0.999, 1.0]); | ||
|
||
Self { | ||
quantiles, | ||
pretty: false, | ||
} | ||
} | ||
|
||
/// Sets the quantiles to use when rendering histograms. | ||
/// | ||
/// Quantiles represent a scale of 0 to 1, where percentiles represent a scale of 1 to 100, so | ||
/// a quantile of 0.99 is the 99th percentile, and a quantile of 0.99 is the 99.9th percentile. | ||
/// | ||
/// By default, the quantiles will be set to: 0.0, 0.5, 0.9, 0.95, 0.99, 0.999, and 1.0. | ||
pub fn set_quantiles(mut self, quantiles: &[f64]) -> Self { | ||
self.quantiles = parse_quantiles(quantiles); | ||
self | ||
} | ||
|
||
/// Sets whether or not to render the JSON as "pretty." | ||
/// | ||
/// Pretty JSON refers to the formatting and identation, where different fields are on | ||
/// different lines, and depending on their depth from the root object, are indented. | ||
/// | ||
/// By default, pretty mode is not enabled. | ||
pub fn set_pretty_json(mut self, pretty: bool) -> Self { | ||
self.pretty = pretty; | ||
self | ||
} | ||
} | ||
|
||
impl Builder for JsonBuilder { | ||
type Output = JsonObserver; | ||
|
||
fn build(&self) -> Self::Output { | ||
JsonObserver { | ||
quantiles: self.quantiles.clone(), | ||
pretty: self.pretty, | ||
tree: MetricsTree::default(), | ||
histos: HashMap::new(), | ||
} | ||
} | ||
} | ||
|
||
impl Default for JsonBuilder { | ||
fn default() -> Self { | ||
Self::new() | ||
} | ||
} | ||
|
||
/// Records metrics in a hierarchical, text-based format. | ||
pub struct JsonObserver { | ||
pub(crate) quantiles: Vec<Quantile>, | ||
pub(crate) pretty: bool, | ||
pub(crate) tree: MetricsTree, | ||
pub(crate) histos: HashMap<Key, Histogram<u64>>, | ||
} | ||
|
||
impl Observer for JsonObserver { | ||
fn observe_counter(&mut self, key: Key, value: u64) { | ||
let (levels, name) = key_to_parts(key); | ||
self.tree.insert_value(levels, name, value); | ||
} | ||
|
||
fn observe_gauge(&mut self, key: Key, value: i64) { | ||
let (levels, name) = key_to_parts(key); | ||
self.tree.insert_value(levels, name, value); | ||
} | ||
|
||
fn observe_histogram(&mut self, key: Key, values: &[u64]) { | ||
let entry = self | ||
.histos | ||
.entry(key) | ||
.or_insert_with(|| Histogram::<u64>::new(3).expect("failed to create histogram")); | ||
|
||
for value in values { | ||
entry | ||
.record(*value) | ||
.expect("failed to observe histogram value"); | ||
} | ||
} | ||
} | ||
|
||
impl Drain<String> for JsonObserver { | ||
fn drain(&mut self) -> String { | ||
for (key, h) in self.histos.drain() { | ||
let (levels, name) = key_to_parts(key); | ||
let values = hist_to_values(name, h.clone(), &self.quantiles); | ||
self.tree.insert_values(levels, values); | ||
} | ||
|
||
let result = if self.pretty { | ||
serde_json::to_string_pretty(&self.tree) | ||
} else { | ||
serde_json::to_string(&self.tree) | ||
}; | ||
let rendered = result.expect("failed to render json output"); | ||
self.tree.clear(); | ||
rendered | ||
} | ||
} | ||
|
||
fn key_to_parts(key: Key) -> (Vec<String>, String) { | ||
let (name, labels) = key.into_parts(); | ||
let mut parts = name.split('.').map(ToOwned::to_owned).collect::<Vec<_>>(); | ||
let name = parts.pop().expect("name didn't have a single part"); | ||
|
||
let labels = labels | ||
.into_iter() | ||
.map(Label::into_parts) | ||
.map(|(k, v)| format!("{}=\"{}\"", k, v)) | ||
.collect::<Vec<_>>() | ||
.join(","); | ||
let label = if labels.is_empty() { | ||
String::new() | ||
} else { | ||
format!("{{{}}}", labels) | ||
}; | ||
|
||
let fname = format!("{}{}", name, label); | ||
|
||
(parts, fname) | ||
} | ||
|
||
fn hist_to_values( | ||
name: String, | ||
hist: Histogram<u64>, | ||
quantiles: &[Quantile], | ||
) -> Vec<(String, u64)> { | ||
let mut values = Vec::new(); | ||
|
||
values.push((format!("{} count", name), hist.len())); | ||
for quantile in quantiles { | ||
let value = hist.value_at_quantile(quantile.value()); | ||
values.push((format!("{} {}", name, quantile.label()), value)); | ||
} | ||
|
||
values | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,18 +1,21 @@ | ||
[package] | ||
name = "metrics-observer-prometheus" | ||
version = "0.2.2" | ||
version = "0.3.0" | ||
authors = ["Toby Lawrence <[email protected]>"] | ||
edition = "2018" | ||
|
||
license = "MIT" | ||
|
||
description = "metric recorder for Prometheus exposition output" | ||
description = "metric observer for Prometheus exposition output" | ||
|
||
homepage = "https://github.com/metrics-rs/metrics" | ||
repository = "https://github.com/metrics-rs/metrics" | ||
documentation = "https://docs.rs/metrics-recorder-prometheus" | ||
documentation = "https://docs.rs/metrics-observer-prometheus" | ||
readme = "README.md" | ||
|
||
keywords = ["metrics", "telemetry", "prometheus"] | ||
|
||
[dependencies] | ||
metrics-core = { path = "../metrics-core", version = "^0.4" } | ||
metrics-util = { path = "../metrics-util", version = "^0.2" } | ||
metrics-util = { path = "../metrics-util", version = "^0.3" } | ||
hdrhistogram = "^6.1" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.