Skip to content

Commit 36834dd

Browse files
authored
refactor: next-generation metrics (#80)
1 parent a796126 commit 36834dd

File tree

126 files changed

+4829
-5794
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

126 files changed

+4829
-5794
lines changed

.editorconfig

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# http://editorconfig.org
2+
root = true
3+
4+
[*]
5+
indent_style = space
6+
indent_size = 2
7+
end_of_line = lf
8+
charset = utf-8
9+
trim_trailing_whitespace = true
10+
insert_final_newline = true
11+
12+
[*.rs]
13+
indent_size = 4

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
/target
22
**/*.rs.bk
33
Cargo.lock
4+
/.vscode

Cargo.toml

+5-7
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,10 @@
11
[workspace]
22
members = [
3-
"metrics-core",
43
"metrics",
5-
"metrics-runtime",
4+
"metrics-macros",
65
"metrics-util",
7-
"metrics-exporter-log",
8-
"metrics-exporter-http",
9-
"metrics-observer-yaml",
10-
"metrics-observer-prometheus",
11-
"metrics-observer-json",
6+
"metrics-benchmark",
7+
"metrics-exporter-tcp",
8+
"metrics-exporter-prometheus",
9+
"metrics-tracing-context",
1210
]

README.md

+17-32
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ The Metrics project: a metrics ecosystem for Rust.
2121

2222
Running applications in production can be hard when you don't have insight into what the application is doing. We're lucky to have so many good system monitoring programs and services to show us how our servers are performing, but we still have to do the work of instrumenting our applications to gain deep insight into their behavior and performance.
2323

24-
_Metrics_ makes it easy to instrument your application to provide real-time insight into what's happening. It provides a number of practical features that make it easy for library and application authors to start collecting and exporting metrics from their codebase.
24+
`metrics` makes it easy to instrument your application to provide real-time insight into what's happening. It provides a number of practical features that make it easy for library and application authors to start collecting and exporting metrics from their codebase.
2525

2626
# why would I collect metrics?
2727

@@ -32,50 +32,35 @@ Some of the most common scenarios for collecting metrics from an application:
3232

3333
Importantly, this works for both library authors and application authors. If the libraries you use are instrumented, you unlock the power of being able to collect those metrics in your application for free, without any extra configuration. Everyone wins, and learns more about their application performance at the end of the day.
3434

35-
# project goals
36-
37-
Firstly, we want to establish standardized interfaces by which everyone can interoperate: this is the goal of the `metrics` and `metrics-core` crates.
38-
39-
`metrics` provides macros similar to `log`, which are essentially zero cost and invisible when not in use, but automatically funnel their data when a user opts in and installs a metrics recorder. This allows library authors to instrument their libraries without needing to care which metrics system end users will be utilizing.
40-
41-
`metrics-core` provides foundational traits for core components of the metrics ecosystem, primarily the output side. There are a large number of output formats and transports that application authors may consider or want to use. By focusing on the API boundary between the systems that collect metrics and the systems they're exported to, these pieces can be easily swapped around depending on the needs of the end user.
42-
43-
Secondly, we want to provide a best-in-class reference runtime: this is the goal of the `metrics-runtime` crate.
44-
45-
Unfortunately, a great interface is no good without a suitable implementation, and we want to make sure that for users looking to instrument their applications for the first time, that they have a batteries-included option that gets them off to the races quickly. The `metrics-runtime` crate provides a best-in-class implementation of a metrics collection system, including support for the core metric types -- counters, gauges, and histograms -- as well as support for important features such as scoping, labels, flexible approaches to recording, and more.
46-
47-
On top of that, collecting metrics isn't terribly useful unless you can export those values, and so `metrics-runtime` pulls in a small set of default observers and exporters to allow users to quickly set up their application to be observable by their existing downstream metrics aggregation/storage.
48-
4935
# project layout
5036

5137
The Metrics project provides a number of crates for both library and application authors.
5238

53-
If you're a library author, you'll only care about using [`metrics`] to instrument your library. If you're an application author, you'll primarily care about [`metrics-runtime`], but you may also want to use [`metrics`] to make instrumenting your own code even easier.
39+
If you're a library author, you'll only care about using [`metrics`] to instrument your library. If
40+
you're an application author, you'll likely also want to instrument your application, but you'll
41+
care about "exporters" as a means to take those metrics and ship them somewhere for analysis.
5442

5543
Overall, this repository is home to the following crates:
5644

5745
* [`metrics`][metrics]: A lightweight metrics facade, similar to [`log`](https://docs.rs/log).
58-
* [`metrics-core`][metrics-core]: Foundational traits for interoperable metrics libraries.
59-
* [`metrics-runtime`][metrics-runtime]: A batteries-included metrics library.
60-
* [`metrics-exporter-http`][metrics-exporter-http]: A metrics-core compatible exporter for serving metrics over HTTP.
61-
* [`metrics-exporter-log`][metrics-exporter-log]: A metrics-core compatible exporter for forwarding metrics to logs.
62-
* [`metrics-observer-json`][metrics-observer-json]: A metrics-core compatible observer that outputs JSON.
63-
* [`metrics-observer-yaml`][metrics-observer-yaml]: A metrics-core compatible observer that outputs YAML.
64-
* [`metrics-observer-prometheus`][metrics-observer-prometheus]: A metrics-core compatible observer that outputs the Prometheus exposition format.
65-
* [`metrics-util`][metrics-util]: Helper types/functions used by the metrics ecosystem.
46+
* [`metrics-macros`][metrics-macros]: Procedural macros that power `metrics`.
47+
* [`metrics-tracing-context`][metrics-tracing-context]: Allow capturing [`tracing`][tracing] span
48+
fields as metric labels.
49+
* [`metrics-exporter-tcp`][metrics-exporter-tcp]: A `metrics`-compatible exporter for serving metrics over TCP.
50+
* [`metrics-exporter-prometheus`][metrics-exporter-prometheus]: A `metrics`-compatible exporter for
51+
serving a Prometheus scrape endpoint.
52+
* [`metrics-util`][metrics-util]: Helper types/functions used by the `metrics` ecosystem.
6653

6754
# contributing
6855

69-
We're always looking for users who have thoughts on how to make metrics better, or users with interesting use cases. Of course, we're also happy to accept code contributions for outstanding feature requests! 😀
56+
We're always looking for users who have thoughts on how to make `metrics` better, or users with interesting use cases. Of course, we're also happy to accept code contributions for outstanding feature requests! 😀
7057

7158
We'd love to chat about any of the above, or anything else, really! You can find us over on [Discord](https://discord.gg/eTwKyY9).
7259

7360
[metrics]: https://github.com/metrics-rs/metrics/tree/master/metrics
74-
[metrics-core]: https://github.com/metrics-rs/metrics/tree/master/metrics-core
75-
[metrics-runtime]: https://github.com/metrics-rs/metrics/tree/master/metrics-runtime
76-
[metrics-exporter-http]: https://github.com/metrics-rs/metrics/tree/master/metrics-exporter-http
77-
[metrics-exporter-log]: https://github.com/metrics-rs/metrics/tree/master/metrics-exporter-log
78-
[metrics-observer-json]: https://github.com/metrics-rs/metrics/tree/master/metrics-observer-json
79-
[metrics-observer-yaml]: https://github.com/metrics-rs/metrics/tree/master/metrics-observer-yaml
80-
[metrics-observer-prometheus]: https://github.com/metrics-rs/metrics/tree/master/metrics-observer-prometheus
61+
[metrics-macros]: https://github.com/metrics-rs/metrics/tree/master/metrics-macros
62+
[metrics-tracing-context]: https://github.com/metrics-rs/metrics/tree/master/metrics-tracing-context
63+
[metrics-exporter-tcp]: https://github.com/metrics-rs/metrics/tree/master/metrics-exporter-tcp
64+
[metrics-exporter-prometheus]: https://github.com/metrics-rs/metrics/tree/master/metrics-exporter-prometheus
8165
[metrics-util]: https://github.com/metrics-rs/metrics/tree/master/metrics-util
66+
[tracing]: https://tracing.rs

ci/azure-test-minimum.yaml

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,6 @@ jobs:
1515
steps:
1616
- template: azure-install-rust.yml
1717
parameters:
18-
rust_version: 1.39.0
18+
rust_version: 1.40.0
1919
- script: cargo test
2020
displayName: cargo test

metrics-benchmark/Cargo.toml

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
[package]
2+
name = "metrics-benchmark"
3+
version = "0.1.1-alpha.1"
4+
authors = ["Toby Lawrence <[email protected]>"]
5+
edition = "2018"
6+
7+
[dependencies]
8+
log = "0.4"
9+
env_logger = "0.7"
10+
getopts = "0.2"
11+
hdrhistogram = "7.0"
12+
quanta = "0.6"
13+
atomic-shim = "0.1"
14+
metrics = { version = "0.13.0-alpha.0", path = "../metrics" }
15+
metrics-util = { version = "0.4.0-alpha.0", path = "../metrics-util" }
File renamed without changes.

metrics-runtime/examples/facade.rs renamed to metrics-benchmark/src/main.rs

+25-50
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,12 @@
1-
#[macro_use]
2-
extern crate log;
3-
extern crate env_logger;
4-
extern crate getopts;
5-
extern crate hdrhistogram;
6-
extern crate metrics_core;
7-
extern crate metrics_runtime;
8-
extern crate tokio;
9-
10-
#[macro_use]
11-
extern crate metrics;
12-
131
use atomic_shim::AtomicU64;
142
use getopts::Options;
153
use hdrhistogram::Histogram;
16-
use metrics_runtime::{exporters::HttpExporter, observers::JsonBuilder, Receiver};
17-
use quanta::Clock;
4+
use log::{error, info};
5+
use metrics::{gauge, histogram, increment};
6+
use metrics_util::DebuggingRecorder;
187
use std::{
198
env,
9+
ops::Sub,
2010
sync::{
2111
atomic::{AtomicBool, Ordering},
2212
Arc,
@@ -28,23 +18,21 @@ use std::{
2818
const LOOP_SAMPLE: u64 = 1000;
2919

3020
struct Generator {
31-
t0: Option<u64>,
21+
t0: Option<Instant>,
3222
gauge: i64,
3323
hist: Histogram<u64>,
3424
done: Arc<AtomicBool>,
3525
rate_counter: Arc<AtomicU64>,
36-
clock: Clock,
3726
}
3827

3928
impl Generator {
40-
fn new(done: Arc<AtomicBool>, rate_counter: Arc<AtomicU64>, clock: Clock) -> Generator {
29+
fn new(done: Arc<AtomicBool>, rate_counter: Arc<AtomicU64>) -> Generator {
4130
Generator {
4231
t0: None,
4332
gauge: 0,
4433
hist: Histogram::<u64>::new_with_bounds(1, u64::max_value(), 3).unwrap(),
4534
done,
4635
rate_counter,
47-
clock,
4836
}
4937
}
5038

@@ -59,22 +47,22 @@ impl Generator {
5947

6048
self.gauge += 1;
6149

62-
let t1 = self.clock.now();
50+
let t1 = Instant::now();
6351

6452
if let Some(t0) = self.t0 {
65-
let start = if counter % LOOP_SAMPLE == 0 {
66-
self.clock.now()
53+
let start = if counter % 1000 == 0 {
54+
Some(Instant::now())
6755
} else {
68-
0
56+
None
6957
};
7058

71-
counter!("ok.gotem", 1);
72-
timing!("ok.gotem", t0, t1);
73-
gauge!("total", self.gauge);
59+
increment!("ok");
60+
gauge!("total", self.gauge as f64);
61+
histogram!("ok", t1.sub(t0));
7462

75-
if start != 0 {
76-
let delta = self.clock.now() - start;
77-
self.hist.saturating_record(delta);
63+
if let Some(val) = start {
64+
let delta = Instant::now() - val;
65+
self.hist.saturating_record(delta.as_nanos() as u64);
7866

7967
// We also increment our global counter for the sample rate here.
8068
self.rate_counter
@@ -121,8 +109,7 @@ pub fn opts() -> Options {
121109
opts
122110
}
123111

124-
#[tokio::main]
125-
async fn main() {
112+
fn main() {
126113
env_logger::init();
127114

128115
let args: Vec<String> = env::args().collect();
@@ -159,35 +146,23 @@ async fn main() {
159146
info!("duration: {}s", seconds);
160147
info!("producers: {}", producers);
161148

162-
let receiver = Receiver::builder()
163-
.histogram(Duration::from_secs(5), Duration::from_millis(100))
164-
.build()
165-
.expect("failed to build receiver");
166-
167-
let controller = receiver.controller();
168-
169-
let addr = "0.0.0.0:23432"
170-
.parse()
171-
.expect("failed to parse http listen address");
172-
let builder = JsonBuilder::new().set_pretty_json(true);
173-
let exporter = HttpExporter::new(controller.clone(), builder, addr);
174-
tokio::spawn(exporter.async_run());
149+
let recorder = DebuggingRecorder::new();
150+
let snapshotter = recorder.snapshotter();
151+
recorder.install().expect("failed to install recorder");
175152

176-
receiver.install();
177-
info!("receiver configured");
153+
info!("sink configured");
178154

179155
// Spin up our sample producers.
180156
let done = Arc::new(AtomicBool::new(false));
181157
let rate_counter = Arc::new(AtomicU64::new(0));
182158
let mut handles = Vec::new();
183-
let clock = Clock::new();
184159

185160
for _ in 0..producers {
186161
let d = done.clone();
187162
let r = rate_counter.clone();
188-
let c = clock.clone();
189163
let handle = thread::spawn(move || {
190-
Generator::new(d, r, c).run();
164+
let mut gen = Generator::new(d, r);
165+
gen.run();
191166
});
192167

193168
handles.push(handle);
@@ -202,7 +177,7 @@ async fn main() {
202177
let t1 = Instant::now();
203178

204179
let start = Instant::now();
205-
let _snapshot = controller.snapshot();
180+
let _snapshot = snapshotter.snapshot();
206181
let end = Instant::now();
207182
snapshot_hist.saturating_record(duration_as_nanos(end - start) as u64);
208183

@@ -219,7 +194,7 @@ async fn main() {
219194
info!("--------------------------------------------------------------------------------");
220195
info!(" ingested samples total: {}", total);
221196
info!(
222-
"snapshot end-to-end: min: {:9} p50: {:9} p95: {:9} p99: {:9} p999: {:9} max: {:9}",
197+
"snapshot retrieval: min: {:9} p50: {:9} p95: {:9} p99: {:9} p999: {:9} max: {:9}",
223198
nanos_to_readable(snapshot_hist.min()),
224199
nanos_to_readable(snapshot_hist.value_at_percentile(50.0)),
225200
nanos_to_readable(snapshot_hist.value_at_percentile(95.0)),

metrics-core/CHANGELOG.md

-47
This file was deleted.

metrics-core/CODE_OF_CONDUCT.md

-30
This file was deleted.

metrics-core/Cargo.toml

-16
This file was deleted.

0 commit comments

Comments
 (0)