Merge branch 'main' into experiment/name-parts

This commit is contained in:
Toby Lawrence 2020-11-13 13:26:42 -05:00 committed by GitHub
commit db02cd80da
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 80 additions and 10 deletions

View File

@ -35,6 +35,23 @@ jobs:
override: true override: true
- name: Run Tests - name: Run Tests
run: cargo test run: cargo test
docs:
runs-on: ubuntu-latest
env:
RUSTDOCFLAGS: -Dwarnings
steps:
- uses: actions/checkout@v2
- name: Install Rust Nightly
uses: actions-rs/toolchain@v1
with:
toolchain: nightly
override: true
components: rust-docs
- name: Check docs
uses: actions-rs/cargo@v1
with:
command: doc
args: --workspace --no-deps
bench: bench:
name: Bench ${{ matrix.os }} name: Bench ${{ matrix.os }}
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
@ -49,4 +66,7 @@ jobs:
toolchain: 'stable' toolchain: 'stable'
override: true override: true
- name: Run Benchmarks - name: Run Benchmarks
run: cargo bench --all-features uses: actions-rs/cargo@v1
with:
command: bench
args: --all-features

View File

@ -1,6 +1,6 @@
[package] [package]
name = "metrics-exporter-prometheus" name = "metrics-exporter-prometheus"
version = "0.1.0-alpha.6" version = "0.1.0-alpha.7"
authors = ["Toby Lawrence <toby@nuclearfurnace.com>"] authors = ["Toby Lawrence <toby@nuclearfurnace.com>"]
edition = "2018" edition = "2018"
@ -15,15 +15,21 @@ readme = "README.md"
categories = ["development-tools::debugging"] categories = ["development-tools::debugging"]
keywords = ["metrics", "telemetry", "prometheus"] keywords = ["metrics", "telemetry", "prometheus"]
[features]
default = ["tokio-exporter"]
tokio-exporter = ["hyper", "tokio"]
[dependencies] [dependencies]
metrics = { version = "0.13.0-alpha.1", path = "../metrics" } metrics = { version = "0.13.0-alpha.1", path = "../metrics" }
metrics-util = { version = "0.4.0-alpha.1", path = "../metrics-util"} metrics-util = { version = "0.4.0-alpha.1", path = "../metrics-util" }
hdrhistogram = "7.1" hdrhistogram = "7.1"
hyper = { version = "0.13", default-features = false, features = ["tcp"] }
tokio = { version = "0.2", features = ["rt-core", "tcp", "time", "macros"] }
parking_lot = "0.11" parking_lot = "0.11"
thiserror = "1.0" thiserror = "1.0"
# Optional
hyper = { version = "0.13", default-features = false, features = ["tcp"], optional = true }
tokio = { version = "0.2", features = ["rt-core", "tcp", "time", "macros"], optional = true }
[dev-dependencies] [dev-dependencies]
quanta = "0.6" quanta = "0.6"
tracing = "0.1" tracing = "0.1"

View File

@ -3,6 +3,7 @@
#![cfg_attr(docsrs, feature(doc_cfg), deny(broken_intra_doc_links))] #![cfg_attr(docsrs, feature(doc_cfg), deny(broken_intra_doc_links))]
use std::future::Future; use std::future::Future;
#[cfg(feature = "tokio-exporter")]
use hyper::{ use hyper::{
service::{make_service_fn, service_fn}, service::{make_service_fn, service_fn},
{Body, Error as HyperError, Response, Server}, {Body, Error as HyperError, Response, Server},
@ -16,9 +17,11 @@ use std::io;
use std::iter::FromIterator; use std::iter::FromIterator;
use std::net::{IpAddr, Ipv4Addr, SocketAddr}; use std::net::{IpAddr, Ipv4Addr, SocketAddr};
use std::sync::Arc; use std::sync::Arc;
#[cfg(feature = "tokio-exporter")]
use std::thread; use std::thread;
use std::{collections::HashMap, time::SystemTime}; use std::{collections::HashMap, time::SystemTime};
use thiserror::Error as ThisError; use thiserror::Error as ThisError;
#[cfg(feature = "tokio-exporter")]
use tokio::{pin, runtime, select}; use tokio::{pin, runtime, select};
type PrometheusRegistry = Registry<CompositeKey, Handle>; type PrometheusRegistry = Registry<CompositeKey, Handle>;
@ -32,6 +35,7 @@ pub enum Error {
Io(#[from] io::Error), Io(#[from] io::Error),
/// Binding/listening to the given address did not succeed. /// Binding/listening to the given address did not succeed.
#[cfg(feature = "tokio-exporter")]
#[error("failed to bind to given listen address: {0}")] #[error("failed to bind to given listen address: {0}")]
Hyper(#[from] HyperError), Hyper(#[from] HyperError),
@ -331,6 +335,13 @@ pub struct PrometheusRecorder {
} }
impl PrometheusRecorder { impl PrometheusRecorder {
/// Gets a [`PrometheusHandle`] to this recorder.
pub fn handle(&self) -> PrometheusHandle {
PrometheusHandle {
inner: self.inner.clone(),
}
}
fn add_description_if_missing(&self, key: &Key, description: Option<&'static str>) { fn add_description_if_missing(&self, key: &Key, description: Option<&'static str>) {
if let Some(description) = description { if let Some(description) = description {
let mut descriptions = self.inner.descriptions.write(); let mut descriptions = self.inner.descriptions.write();
@ -341,6 +352,20 @@ impl PrometheusRecorder {
} }
} }
/// Handle to [`PrometheusRecorder`].
///
/// Useful for exposing a scrape endpoint on an existing HTTP/HTTPS server.
pub struct PrometheusHandle {
inner: Arc<Inner>,
}
impl PrometheusHandle {
/// Returns the metrics in Prometheus accepted String format.
pub fn render(&self) -> String {
self.inner.render()
}
}
/// Builder for creating and installing a Prometheus recorder/exporter. /// Builder for creating and installing a Prometheus recorder/exporter.
pub struct PrometheusBuilder { pub struct PrometheusBuilder {
listen_address: SocketAddr, listen_address: SocketAddr,
@ -416,15 +441,16 @@ impl PrometheusBuilder {
/// ///
/// An error will be returned if there's an issue with creating the HTTP server or with /// An error will be returned if there's an issue with creating the HTTP server or with
/// installing the recorder as the global recorder. /// installing the recorder as the global recorder.
#[cfg(feature = "tokio-exporter")]
pub fn install(self) -> Result<(), Error> { pub fn install(self) -> Result<(), Error> {
let (recorder, exporter) = self.build()?;
metrics::set_boxed_recorder(Box::new(recorder))?;
let mut runtime = runtime::Builder::new() let mut runtime = runtime::Builder::new()
.basic_scheduler() .basic_scheduler()
.enable_all() .enable_all()
.build()?; .build()?;
let (recorder, exporter) = runtime.enter(|| self.build_with_exporter())?;
metrics::set_boxed_recorder(Box::new(recorder))?;
thread::Builder::new() thread::Builder::new()
.name("metrics-exporter-prometheus-http".to_string()) .name("metrics-exporter-prometheus-http".to_string())
.spawn(move || { .spawn(move || {
@ -441,13 +467,31 @@ impl PrometheusBuilder {
Ok(()) Ok(())
} }
/// Builds the recorder and returns it.
/// This function is only enabled when default features are not set.
pub fn build(self) -> Result<PrometheusRecorder, Error> {
let inner = Arc::new(Inner {
registry: Registry::new(),
distributions: RwLock::new(HashMap::new()),
quantiles: self.quantiles.clone(),
buckets: self.buckets.clone(),
buckets_by_name: self.buckets_by_name,
descriptions: RwLock::new(HashMap::new()),
});
let recorder = PrometheusRecorder { inner };
Ok(recorder)
}
/// Builds the recorder and exporter and returns them both. /// Builds the recorder and exporter and returns them both.
/// ///
/// In most cases, users should prefer to use [`PrometheusBuilder::install`] to create and /// In most cases, users should prefer to use [`PrometheusBuilder::install`] to create and
/// install the recorder and exporter automatically for them. If a caller is combining /// install the recorder and exporter automatically for them. If a caller is combining
/// recorders, or needs to schedule the exporter to run in a particular way, this method /// recorders, or needs to schedule the exporter to run in a particular way, this method
/// provides the flexibility to do so. /// provides the flexibility to do so.
pub fn build( #[cfg(feature = "tokio-exporter")]
pub fn build_with_exporter(
self, self,
) -> Result< ) -> Result<
( (

View File

@ -87,7 +87,7 @@ impl fmt::Display for NameParts {
/// Inner representation of [`Key`]. /// Inner representation of [`Key`].
/// ///
/// While [`Key`] is the type that users will interact with via [`Recorder`][crate::Recorder`, /// While [`Key`] is the type that users will interact with via [`Recorder`][crate::Recorder],
/// [`KeyData`] is responsible for the actual storage of the name and label data. /// [`KeyData`] is responsible for the actual storage of the name and label data.
#[derive(PartialEq, Eq, Hash, Clone, Debug)] #[derive(PartialEq, Eq, Hash, Clone, Debug)]
pub struct KeyData { pub struct KeyData {