Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
Next Next commit
[WIP]
  • Loading branch information
emschwartz committed Apr 21, 2023
commit b255f8c1c82d4cfbe88f71d1e9d3e055c47bd258
4 changes: 4 additions & 0 deletions autometrics/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ default = ["opentelemetry"]
metrics = ["dep:metrics"]
opentelemetry = ["opentelemetry_api"]
prometheus = ["const_format", "dep:prometheus", "once_cell"]
prometheus-client = ["dep:prometheus-client", "once_cell"]
prometheus-exporter = [
"metrics-exporter-prometheus",
"once_cell",
Expand Down Expand Up @@ -45,6 +46,9 @@ prometheus = { version = "0.13", default-features = false, optional = true }
# Used for prometheus feature
const_format = { version = "0.2", features = ["rust_1_51"], optional = true }

# Used for prometheus-client feature
prometheus-client = { version = "0.20", optional = true }

[dev-dependencies]
regex = "1.7"
http = "0.2"
Expand Down
62 changes: 54 additions & 8 deletions autometrics/src/labels.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,19 @@
use crate::{constants::*, objectives::*};
use prometheus_client::encoding::EncodeLabelKey;
#[cfg(feature = "prometheus-client")]
use prometheus_client::encoding::{
EncodeLabelSet, EncodeLabelValue, LabelSetEncoder, LabelValueEncoder,
};
use std::ops::Deref;

pub(crate) type Label = (&'static str, &'static str);
type ResultAndReturnTypeLabels = (&'static str, Option<&'static str>);

/// These are the labels used for the `build_info` metric.
#[cfg_attr(
feature = "prometheus-client",
derive(EncodeLabelSet, Debug, Clone, PartialEq, Eq, Hash)
)]
pub struct BuildInfoLabels {
pub(crate) version: &'static str,
pub(crate) commit: &'static str,
Expand All @@ -21,12 +30,38 @@ impl BuildInfoLabels {
}

/// These are the labels used for the `function.calls.count` metric.
#[cfg_attr(
feature = "prometheus-client",
derive(EncodeLabelSet, Debug, Clone, PartialEq, Eq, Hash)
)]
pub struct CounterLabels {
pub(crate) function: &'static str,
pub(crate) module: &'static str,
pub(crate) caller: &'static str,
pub(crate) result: Option<ResultAndReturnTypeLabels>,
pub(crate) objective: Option<(&'static str, ObjectivePercentile)>,
pub(crate) result: Option<ResultLabel>,
pub(crate) ok: Option<&'static str>,
pub(crate) error: Option<&'static str>,
pub(crate) objective_name: Option<&'static str>,
pub(crate) objective_percentile: Option<ObjectivePercentile>,
}

#[cfg_attr(
feature = "prometheus-client",
derive(Debug, Clone, PartialEq, Eq, Hash)
)]
pub(crate) enum ResultLabel {
Ok,
Error,
}

#[cfg(feature = "prometheus-client")]
impl EncodeLabelValue for ResultLabel {
fn encode(&self, encoder: &mut LabelValueEncoder) -> Result<(), std::fmt::Error> {
match self {
ResultLabel::Ok => OK_KEY.encode(encoder),
ResultLabel::Error => ERROR_KEY.encode(encoder),
}
}
}

impl CounterLabels {
Expand All @@ -37,21 +72,22 @@ impl CounterLabels {
result: Option<ResultAndReturnTypeLabels>,
objective: Option<Objective>,
) -> Self {
let objective = if let Some(objective) = objective {
let (objective_name, objective_percentile) = if let Some(objective) = objective {
if let Some(success_rate) = objective.success_rate {
Some((objective.name, success_rate))
(objective.name, success_rate)
} else {
None
("", "")
}
} else {
None
("", "")
};
Self {
function,
module,
caller,
objective_name,
objective_percentile,
result,
objective,
}
}

Expand All @@ -77,11 +113,17 @@ impl CounterLabels {
}

/// These are the labels used for the `function.calls.duration` metric.
#[cfg_attr(
feature = "prometheus-client",
derive(EncodeLabelSet, Debug, Clone, PartialEq, Eq, Hash)
)]
pub struct HistogramLabels {
pub function: &'static str,
pub module: &'static str,
/// The SLO name, objective percentile, and latency threshold
pub objective: Option<(&'static str, ObjectivePercentile, ObjectiveLatency)>,
pub objective_name: &'static str,
pub objective_percentile: ObjectivePercentile,
pub objective_latency_threshold: ObjectiveLatency,
}

impl HistogramLabels {
Expand Down Expand Up @@ -117,6 +159,10 @@ impl HistogramLabels {
}

/// These are the labels used for the `function.calls.concurrent` metric.
#[cfg_attr(
feature = "prometheus-client",
derive(EncodeLabelSet, Debug, Clone, PartialEq, Eq, Hash)
)]
pub struct GaugeLabels {
pub function: &'static str,
pub module: &'static str,
Expand Down
6 changes: 5 additions & 1 deletion autometrics/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,11 @@ pub use self::prometheus_exporter::*;

/// We use the histogram buckets recommended by the OpenTelemetry specification
/// https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/metrics/sdk.md#explicit-bucket-histogram-aggregation
#[cfg(any(feature = "prometheus", feature = "prometheus-exporter"))]
#[cfg(any(
feature = "prometheus",
feature = "prometheus-client",
feature = "prometheus-exporter"
))]
pub(crate) const HISTOGRAM_BUCKETS: [f64; 14] = [
0.005, 0.01, 0.025, 0.05, 0.075, 0.1, 0.25, 0.5, 0.75, 1.0, 2.5, 5.0, 7.5, 10.0,
];
Expand Down
25 changes: 25 additions & 0 deletions autometrics/src/objectives.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
#[cfg(feature = "prometheus-client")]
use prometheus_client::encoding::{EncodeLabelValue, LabelValueEncoder};

/// This represents a Service-Level Objective (SLO) for a function or group of functions.
/// The objective should be given a descriptive name and can represent
/// a success rate and/or latency objective.
Expand Down Expand Up @@ -90,6 +93,10 @@ impl Objective {
}

/// The percentage of requests that must meet the given criteria (success rate or latency).
#[cfg_attr(
feature = "prometheus-client",
derive(Clone, Debug, PartialEq, Eq, Hash)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can also be Copy, which would make it easier to use

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, true. Do you think we should derive all of those traits all of the time (not just when using prometheus-client)? I'm somewhat hesitant to do that proactively because it technically expands the surface area of the API and (maybe?) increases compile times. But maybe it's fine to just do it for this type of enum

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

saving Copy makes it way nicer to use. just be sure that u dont plan to add more in the future which may not be Copy, as removing Copy is a breaking change and requires a major version bump (unlike adding it)

)]
#[non_exhaustive]
pub enum ObjectivePercentile {
/// 90%
Expand Down Expand Up @@ -123,7 +130,18 @@ impl ObjectivePercentile {
}
}

#[cfg(feature = "prometheus-client")]
impl EncodeLabelValue for ObjectivePercentile {
fn encode(&self, encoder: &mut LabelValueEncoder) -> Result<(), std::fmt::Error> {
self.as_str().encode(encoder)
}
}

/// The latency threshold, in milliseoncds, for a given objective.
#[cfg_attr(
feature = "prometheus-client",
derive(Clone, Debug, PartialEq, Eq, Hash)
)]
#[non_exhaustive]
pub enum ObjectiveLatency {
/// 5 milliseconds
Expand Down Expand Up @@ -203,3 +221,10 @@ impl ObjectiveLatency {
}
}
}

#[cfg(feature = "prometheus-client")]
impl EncodeLabelValue for ObjectiveLatency {
fn encode(&self, encoder: &mut LabelValueEncoder) -> Result<(), std::fmt::Error> {
self.as_str().encode(encoder)
}
}
2 changes: 2 additions & 0 deletions autometrics/src/tracker/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ mod metrics;
mod opentelemetry;
#[cfg(feature = "prometheus")]
mod prometheus;
#[cfg(feature = "prometheus-client")]
mod prometheus_client;

// By default, use the opentelemetry crate
#[cfg(all(
Expand Down
60 changes: 60 additions & 0 deletions autometrics/src/tracker/prometheus_client.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
use super::TrackMetrics;
use crate::labels::{BuildInfoLabels, CounterLabels, GaugeLabels, HistogramLabels};
use crate::{constants::*, HISTOGRAM_BUCKETS};
use once_cell::sync::Lazy;
use prometheus_client::metrics::{
counter::Counter, family::Family, gauge::Gauge, histogram::Histogram,
};
use prometheus_client::registry::Registry;

pub static PROMETHEUS_CLIENT_REGISTRY: Lazy<Registry> = Lazy::new(|| <Registry>::default());

static METRICS: Lazy<Metrics> = Lazy::new(|| {
let counter = Family::<CounterLabels, Counter>::default();
PROMETHEUS_CLIENT_REGISTRY.register(COUNTER_NAME, COUNTER_DESCRIPTION, counter.clone());

let histogram = Family::<HistogramLabels, Histogram>::new_with_constructor(|| {
Histogram::new(HISTOGRAM_BUCKETS.into_iter())
});
PROMETHEUS_CLIENT_REGISTRY.register(HISTOGRAM_NAME, HISTOGRAM_DESCRIPTION, histogram.clone());

let gauge = Family::<GaugeLabels, Gauge>::default();
PROMETHEUS_CLIENT_REGISTRY.register(GAUGE_NAME, GAUGE_DESCRIPTION, gauge.clone());

let build_info = Family::<BuildInfoLabels, Gauge>::default();
PROMETHEUS_CLIENT_REGISTRY.register(
BUILD_INFO_NAME,
BUILD_INFO_DESCRIPTION,
build_info.clone(),
);

Metrics {
counter,
histogram,
gauge,
build_info,
}
});

struct Metrics {
counter: Family<CounterLabels, Counter>,
histogram: Family<HistogramLabels, Histogram>,
gauge: Family<GaugeLabels, Gauge>,
build_info: Family<BuildInfoLabels, Gauge>,
}

struct PrometheusClientTracker {}

impl TrackMetrics for PrometheusClientTracker {
fn set_build_info(build_info_labels: &BuildInfoLabels) {
todo!()
}

fn start(gauge_labels: Option<&GaugeLabels>) -> Self {
todo!()
}

fn finish(self, counter_labels: &CounterLabels, histogram_labels: &HistogramLabels) {
todo!()
}
}