Skip to content
This repository has been archived by the owner on Jun 8, 2024. It is now read-only.

Commit

Permalink
document the rest of metrics
Browse files Browse the repository at this point in the history
  • Loading branch information
KodrAus committed May 24, 2024
1 parent a93687d commit 8ca905b
Show file tree
Hide file tree
Showing 3 changed files with 144 additions and 12 deletions.
142 changes: 136 additions & 6 deletions src/metric.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*!
Sampling metrics.
The [`Metric`] type.
Metrics are an effective approach to monitoring applications at scale. They can be cheap to collect, making them suitable for performance sensitive operations. They can also be compact to report, making them suitable for high-volume scenarios. `emit` doesn't provide much infrastructure for collecting or sampling metrics. What it does provide is a standard way to report metric samples as events.
Expand Down Expand Up @@ -59,6 +59,8 @@ emit::emit!(
);
```
# Data model
The data model of metrics is an extension of `emit`'s events. Metric events are points or buckets in a time-series. They don't model the underlying instruments collecting metrics like counters or gauges. They instead model the aggregation of readings from those instruments over their lifetime. Metric events include the following well-known properties:
- `event_kind`: with a value of `"metric"` to indicate that the event is a metric sample.
Expand Down Expand Up @@ -300,6 +302,10 @@ pub use self::{sampler::Sampler, source::Source};

/**
A diagnostic event that represents a metric sample.
Metrics are an extension of [`Event`]s that explicitly take the well-known properties that signal an event as being a metric sample. See the [`crate::metric`] module for details.
A `Metric` can be converted into an [`Event`] through its [`ToEvent`] implemenation, or passed directly to an [`Emitter`] to emit it.
*/
pub struct Metric<'a, P> {
module: Path<'a>,
Expand All @@ -311,6 +317,18 @@ pub struct Metric<'a, P> {
}

impl<'a, P> Metric<'a, P> {
/**
Create a new metric from its properties.
Each metric consists of:
- `module`: The module that owns the underlying data source.
- `extent`: The [`Extent`] that the sample covers.
- `name`: The name of the underlying data source.
- `agg`: The aggregation applied to the underlying data source to produce the sample. See the [`crate::metric`] module for details.
- `value`: The value of the sample itself.
- `props`: Additional [`Props`] to associate with the sample.
*/
pub fn new(
module: impl Into<Path<'a>>,
extent: impl ToExtent,
Expand All @@ -329,55 +347,95 @@ impl<'a, P> Metric<'a, P> {
}
}

/**
Get a reference to the module that owns the underlying data source.
*/
pub fn module(&self) -> &Path<'a> {
&self.module
}

/**
Set the module of the underlying data source to a new value.
*/
pub fn with_module(mut self, module: impl Into<Path<'a>>) -> Self {
self.module = module.into();
self
}

/**
Get the name of the underlying data source.
*/
pub fn name(&self) -> &Str<'a> {
&self.name
}

/**
Set the name of the underlying data source to a new value.
*/
pub fn with_name(mut self, name: impl Into<Str<'a>>) -> Self {
self.name = name.into();
self
}

/**
Get the aggregation applied to the underlying data source to produce the sample.
The value of the aggregation should be one of the [`crate::well_known`] aggregation types.
*/
pub fn agg(&self) -> &Str<'a> {
&self.agg
}

/**
Set the aggregation to a new value.
The value of the aggregation should be one of the [`crate::well_known`] aggregation types.
*/
pub fn with_agg(mut self, agg: impl Into<Str<'a>>) -> Self {
self.agg = agg.into();
self
}

/**
Get the value of the sample itself.
*/
pub fn value(&self) -> &Value<'a> {
&self.value
}

/**
Set the sample to a new value.
*/
pub fn with_value(mut self, value: impl Into<Value<'a>>) -> Self {
self.value = value.into();
self
}

/**
Get the extent for which the sample was generated.
*/
pub fn extent(&self) -> &Option<Extent> {
&self.extent
}

/**
Set the extent of the sample to a new value.
*/
pub fn with_extent(mut self, extent: impl ToExtent) -> Self {
self.extent = extent.to_extent();
self
}

pub fn extent(&self) -> &Option<Extent> {
&self.extent
}

/**
Get the additional properties associated with the sample.
*/
pub fn props(&self) -> &P {
&self.props
}

/**
Set the additional properties associated with the sample to a new value.
*/
pub fn with_props<U>(self, props: U) -> Metric<'a, U> {
Metric {
module: self.module,
Expand Down Expand Up @@ -413,6 +471,9 @@ impl<'a, P: Props> ToEvent for Metric<'a, P> {
}

impl<'a, P: Props> Metric<'a, P> {
/**
Get a new metric sample, borrowing data from this one.
*/
pub fn by_ref<'b>(&'b self) -> Metric<'b, &'b P> {
Metric {
module: self.module.by_ref(),
Expand All @@ -424,6 +485,9 @@ impl<'a, P: Props> Metric<'a, P> {
}
}

/**
Get a type-erased metric sample, borrowing data from this one.
*/
pub fn erase<'b>(&'b self) -> Metric<'b, &'b dyn ErasedProps> {
Metric {
module: self.module.by_ref(),
Expand Down Expand Up @@ -457,6 +521,12 @@ impl<'a, P: Props> Props for Metric<'a, P> {
}

pub mod source {
/*!
The [`Source`] type.
[`Source`]s produce [`Metric`]s on-demand. They can be sampled directly, or combined with a [`crate::metric::Reporter`] and sampled together.
*/

use self::sampler::ErasedSampler;

use super::*;
Expand All @@ -465,8 +535,14 @@ pub mod source {
A source of [`Metric`]s.
*/
pub trait Source {
/**
Produce a current sample for all metrics in the source.
*/
fn sample_metrics<S: sampler::Sampler>(&self, sampler: S);

/**
Produce a current sample for all metrics in the source, emitting them as diagnostic events to the given [`Emitter`].
*/
fn emit_metrics<E: Emitter>(&self, emitter: E) {
struct FromEmitter<E>(E);

Expand All @@ -479,6 +555,9 @@ pub mod source {
self.sample_metrics(FromEmitter(emitter))
}

/**
Chain this source to `other`, sampling metrics from both.
*/
fn and_sample<U>(self, other: U) -> And<Self, U>
where
Self: Sized,
Expand Down Expand Up @@ -557,13 +636,24 @@ pub mod source {
}
}

/**
A [`Source`] from a function.
This type can be created directly, or via [`from_fn`].
*/
pub struct FromFn<F>(F);

/**
Create a [`Source`] from a function.
*/
pub fn from_fn<F: Fn(&mut dyn ErasedSampler)>(source: F) -> FromFn<F> {
FromFn::new(source)
}

impl<F> FromFn<F> {
/**
Wrap the given source function.
*/
pub const fn new(source: F) -> Self {
FromFn(source)
}
Expand Down Expand Up @@ -591,6 +681,11 @@ pub mod source {
}
}

/**
An object-safe [`Source`].
A `dyn ErasedSource` can be treated as `impl Source`.
*/
pub trait ErasedSource: internal::SealedSource {}

impl<T: Source> ErasedSource for T {}
Expand Down Expand Up @@ -642,14 +737,22 @@ mod alloc_support {

/**
A set of [`Source`]s that are all sampled together.
The reporter can be sampled like any other source through its own [`Source`] implementation.
*/
pub struct Reporter(Vec<Box<dyn ErasedSource + Send + Sync>>);

impl Reporter {
/**
Create a new empty reporter.
*/
pub const fn new() -> Self {
Reporter(Vec::new())
}

/**
Add a [`Source`] to the reporter.
*/
pub fn add_source(&mut self, source: impl Source + Send + Sync + 'static) -> &mut Self {
self.0.push(Box::new(source));

Expand Down Expand Up @@ -678,6 +781,8 @@ pub use self::alloc_support::*;
pub mod sampler {
/*!
The [`Sampler`] type.
A [`Sampler`] is a visitor for a [`Source`] that receives [`Metric`]s when the source is sampled.
*/

use emit_core::empty::Empty;
Expand All @@ -688,6 +793,9 @@ pub mod sampler {
A receiver of [`Metric`]s as produced by a [`Source`].
*/
pub trait Sampler {
/**
Receive a metric sample.
*/
fn metric<P: Props>(&self, metric: Metric<P>);
}

Expand All @@ -701,11 +809,28 @@ pub mod sampler {
fn metric<P: Props>(&self, _: Metric<P>) {}
}

/**
A [`Sampler`] from a function.
This type can be created directly, or via [`from_fn`].
*/
pub struct FromFn<F>(F);

/**
Create a [`Sampler`] from a function.
*/
pub fn from_fn<F: Fn(&Metric<&dyn ErasedProps>)>(f: F) -> FromFn<F> {
FromFn(f)
}

pub struct FromFn<F>(F);
impl<F> FromFn<F> {
/**
Wrap the given sampler function.
*/
pub const fn new(sampler: F) -> FromFn<F> {
FromFn(sampler)
}
}

impl<F: Fn(&Metric<&dyn ErasedProps>)> Sampler for FromFn<F> {
fn metric<P: Props>(&self, metric: Metric<P>) {
Expand All @@ -725,6 +850,11 @@ pub mod sampler {
}
}

/**
An object-safe [`Sampler`].
A `dyn ErasedSampler` can be treated as `impl Sampler`.
*/
pub trait ErasedSampler: internal::SealedSampler {}

impl<T: Sampler> ErasedSampler for T {}
Expand Down
2 changes: 1 addition & 1 deletion src/setup.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/*!
The [`Setup`] type.
All functionality in `emit` is based on a [`runtime::Runtime`]. When you call [`Setup::init`], it initializes the [`runtime::shared`] runtime for you, which is also what macros use by default.
All functionality in `emit` is based on a [`crate::runtime::Runtime`]. When you call [`Setup::init`], it initializes the [`crate::runtime::shared`] runtime for you, which is also what macros use by default.
You can implement your own runtime, providing your own implementations of the ambient clock, randomness, and global context. First, disable the default features of `emit` in your `Cargo.toml`:
Expand Down
12 changes: 7 additions & 5 deletions src/span.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*!
Distributed tracing.
The [`Span`] type.
When your application executes key operations, you can emit span events that dover the time they were active. Any other operations involved in that execution, or any other events emitted during it, will be correlated through identifiers to form a hierarchical call tree. Together, these events form a trace, which in distributed systems can involve operations executed by other services. Traces are a useful way to build a picture of service dependencies in distributed applications, and to identify performance problems across them.
Expand Down Expand Up @@ -161,6 +161,8 @@ frame.call(|| {
});
```
# Data model
The data model of spans is an extension of `emit`'s events. Span events include the following well-known properties:
- `event_kind`: with a value of `"span"` to indicate that the event is a span.
Expand All @@ -169,7 +171,7 @@ The data model of spans is an extension of `emit`'s events. Span events include
- `parent_id`: the `span_id` of the operation that invoked this one.
- `trace_id`: an identifier shared by all events in a distributed trace. A `trace_id` is assigned by the first operation.
### Contextual properties
# Contextual properties
Properties added to the span macros are added to an ambient context and automatically included on any events emitted within that operation:
Expand Down Expand Up @@ -265,7 +267,7 @@ Event {
Notice the `span_parent` of `inner_span` is the same as the `span_id` of `outer_span`. That's because `inner_span` was called within the execution of `outer_span`.
### Propagating span context across threads
# Propagating span context across threads
Ambient span properties are not shared across threads by default. This context needs to be fetched and sent across threads manually:
Expand Down Expand Up @@ -294,7 +296,7 @@ tokio::spawn(
Async functions that simply migrate across threads in work-stealing runtimes don't need any manual work to keep their context across those threads.
### Propagating span context across services
# Propagating span context across services
`emit` doesn't implement any distributed trace propagation itself. This is the responsibility of end-users through their web framework and clients to manage.
Expand Down Expand Up @@ -356,7 +358,7 @@ if let (Some(trace_id), Some(span_id)) = (trace_id, span_id) {
}
```
### Completing spans manually
# Completing spans manually
The `arg` control parameter can be applied to span macros to bind an identifier in the body of the annotated function for the [`Span`] that's created for it. This span can be completed manually, changing properties of the span along the way:
Expand Down

0 comments on commit 8ca905b

Please sign in to comment.