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

Commit

Permalink
document platform and setup
Browse files Browse the repository at this point in the history
  • Loading branch information
KodrAus committed May 22, 2024
1 parent de82230 commit be36fd9
Show file tree
Hide file tree
Showing 8 changed files with 254 additions and 104 deletions.
52 changes: 1 addition & 51 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -467,57 +467,7 @@ Hello, World!
## Custom runtimes
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.
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`:
```toml
[dependencies.emit]
version = "*"
default-features = false
features = ["std"]
```
This will ensure the `rt` control parameter is always passed to macros so that your custom runtime will always be used. Next, define your runtime itself and use it in macros:
```
// Define a static runtime to use
// In this example, we use the default implementations of most things,
// but you can also bring-your-own
static RUNTIME: emit::runtime::Runtime<
MyEmitter,
emit::Empty,
emit::platform::thread_local_ctxt::ThreadLocalCtxt,
emit::platform::system_clock::SystemClock,
emit::platform::rand_rng::RandRng,
> = emit::runtime::Runtime::build(
MyEmitter,
emit::Empty,
emit::platform::thread_local_ctxt::ThreadLocalCtxt::shared(),
emit::platform::system_clock::SystemClock::new(),
emit::platform::rand_rng::RandRng::new(),
);
struct MyEmitter;
impl emit::Emitter for MyEmitter {
fn emit<E: emit::event::ToEvent>(&self, evt: E) {
println!("{}", evt.to_event().msg());
}
fn blocking_flush(&self, _: std::time::Duration) -> bool {
// Nothing to flush
true
}
}
// Use your runtime with the `rt` control parameter
emit::emit!(rt: &RUNTIME, "emitted through a custom runtime");
```
```text
emitted through a custom runtime
```
Everything in `emit` is based on a [`runtime::Runtime`]; a fully isolated set of components that provide capabilities like clocks and randomness, as well as your configured emitters and filters. When a runtime isn't specified, it's [`runtime::shared`]. You can define your own runtimes too. The [`mod@setup`] module has more details.
## Troubleshooting
Expand Down
20 changes: 18 additions & 2 deletions src/metric.rs
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,9 @@ use emit_core::{

use crate::kind::Kind;

/**
A diagnostic event that represents a metric sample.
*/
pub struct Metric<'a, P> {
module: Path<'a>,
extent: Option<Extent>,
Expand Down Expand Up @@ -402,6 +405,9 @@ impl<'a, P: Props> Props for Metric<'a, P> {
}
}

/**
A source of [`Metric`]s.
*/
pub trait Source {
fn sample_metrics<S: sampler::Sampler>(&self, sampler: S);

Expand Down Expand Up @@ -429,7 +435,7 @@ pub trait Source {
where
Self: Sized + Clone + Send + Sync + 'static,
{
reporter.source(self.clone());
reporter.add_source(self.clone());

self
}
Expand Down Expand Up @@ -511,14 +517,17 @@ mod alloc_support {

use alloc::{boxed::Box, vec::Vec};

/**
A set of [`Source`]s that are all sampled together.
*/
pub struct Reporter(Vec<Box<dyn ErasedSource + Send + Sync>>);

impl Reporter {
pub const fn new() -> Self {
Reporter(Vec::new())
}

pub fn source(&mut self, source: impl Source + Send + Sync + 'static) -> &mut Self {
pub fn add_source(&mut self, source: impl Source + Send + Sync + 'static) -> &mut Self {
self.0.push(Box::new(source));

self
Expand Down Expand Up @@ -600,10 +609,17 @@ impl<'a> Source for dyn ErasedSource + Send + Sync + 'a {
}

pub mod sampler {
/*!
The [`Sampler`] type.
*/

use emit_core::empty::Empty;

use super::*;

/**
A receiver of [`Metric`]s as produced by a [`Source`].
*/
pub trait Sampler {
fn metric<P: Props>(&self, metric: &Metric<P>);
}
Expand Down
9 changes: 9 additions & 0 deletions src/platform.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
/*!
Components provided by the underlying platform.
This module defines implementations of [`crate::runtime::Runtime`] components that use capabilities of the host platform.
*/

#[cfg(feature = "std")]
use emit_core::{clock::ErasedClock, rng::ErasedRng, runtime::AssertInternal};

Expand All @@ -19,6 +25,9 @@ type DefaultIdGen = rand_rng::RandRng;
#[cfg(feature = "std")]
pub(crate) type DefaultCtxt = thread_local_ctxt::ThreadLocalCtxt;

/**
A type-erased container for system services used when intiailizing runtimes.
*/
pub(crate) struct Platform {
#[cfg(feature = "std")]
pub(crate) clock: AssertInternal<Box<dyn ErasedClock + Send + Sync>>,
Expand Down
10 changes: 10 additions & 0 deletions src/platform/rand_rng.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,20 @@
/*!
The [`RandRng`] type.
*/

use emit_core::{rng::Rng, runtime::InternalRng};
use rand::{Rng as _, RngCore};

/**
An [`Rng`] based on the [`rand`] library.
*/
#[derive(Default, Debug, Clone, Copy)]
pub struct RandRng {}

impl RandRng {
/**
Create a new source of randomness.
*/
pub const fn new() -> Self {
RandRng {}
}
Expand Down
10 changes: 10 additions & 0 deletions src/platform/system_clock.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,19 @@
/*!
The [`SystemClock`] type.
*/

use emit_core::{clock::Clock, runtime::InternalClock, timestamp::Timestamp};

/**
A [`Clock`] based on the standard library's [`std::time::SystemTime`].
*/
#[derive(Default, Debug, Clone, Copy)]
pub struct SystemClock {}

impl SystemClock {
/**
Create a new clock.
*/
pub const fn new() -> Self {
SystemClock {}
}
Expand Down
104 changes: 63 additions & 41 deletions src/platform/thread_local_ctxt.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
/*!
The [`ThreadLocalCtxt`] type.
*/

use core::mem;
use std::{cell::RefCell, collections::HashMap, ops::ControlFlow, sync::Mutex};

Expand All @@ -10,43 +14,11 @@ use emit_core::{
value::{OwnedValue, Value},
};

// Start this id from 1 so it doesn't intersect with the `shared` variant below
static NEXT_CTXT_ID: Mutex<usize> = Mutex::new(1);

fn ctxt_id() -> usize {
let mut next_id = NEXT_CTXT_ID.lock().unwrap();
let id = *next_id;
*next_id = id.wrapping_add(1);

id
}

thread_local! {
static ACTIVE: RefCell<HashMap<usize, ThreadLocalSpan>> = RefCell::new(HashMap::new());
}

fn current(id: usize) -> ThreadLocalSpan {
ACTIVE.with(|active| {
active
.borrow_mut()
.entry(id)
.or_insert_with(|| ThreadLocalSpan { props: None })
.clone()
})
}

fn swap(id: usize, incoming: &mut ThreadLocalSpan) {
ACTIVE.with(|active| {
let mut active = active.borrow_mut();

let current = active
.entry(id)
.or_insert_with(|| ThreadLocalSpan { props: None });

mem::swap(current, incoming);
})
}
/**
A [`Ctxt`] that stores ambient state in thread local storage.
Frames fully encapsulate all properties that were active when they were created so can be sent across threads to move that state with them.
*/
#[derive(Debug, Clone, Copy)]
pub struct ThreadLocalCtxt {
id: usize,
Expand All @@ -59,21 +31,30 @@ impl Default for ThreadLocalCtxt {
}

impl ThreadLocalCtxt {
/**
Create a new thread local store with fully isolated storage.
*/
pub fn new() -> Self {
ThreadLocalCtxt { id: ctxt_id() }
}

/**
Create a new thread local store sharing the same storage as any other [`ThreadLocalCtxt::shared`].
*/
pub const fn shared() -> Self {
ThreadLocalCtxt { id: 0 }
}
}

/**
A [`Ctxt::Frame`] on a [`ThreadLocalCtxt`].
*/
#[derive(Clone)]
pub struct ThreadLocalSpan {
pub struct ThreadLocalCtxtFrame {
props: Option<Arc<HashMap<Str<'static>, OwnedValue>>>,
}

impl Props for ThreadLocalSpan {
impl Props for ThreadLocalCtxtFrame {
fn for_each<'a, F: FnMut(Str<'a>, Value<'a>) -> ControlFlow<()>>(
&'a self,
mut for_each: F,
Expand All @@ -87,14 +68,18 @@ impl Props for ThreadLocalSpan {
ControlFlow::Continue(())
}

fn get<'v, K: emit_core::str::ToStr>(&'v self, key: K) -> Option<Value<'v>> {
self.props.as_ref().and_then(|props| props.get(key))
}

fn is_unique(&self) -> bool {
true
}
}

impl Ctxt for ThreadLocalCtxt {
type Current = ThreadLocalSpan;
type Frame = ThreadLocalSpan;
type Current = ThreadLocalCtxtFrame;
type Frame = ThreadLocalCtxtFrame;

fn with_current<R, F: FnOnce(&Self::Current) -> R>(&self, with: F) -> R {
let current = current(self.id);
Expand All @@ -109,7 +94,7 @@ impl Ctxt for ThreadLocalCtxt {
ControlFlow::Continue(())
});

ThreadLocalSpan {
ThreadLocalCtxtFrame {
props: Some(Arc::new(span)),
}
}
Expand Down Expand Up @@ -143,3 +128,40 @@ impl Ctxt for ThreadLocalCtxt {
}

impl InternalCtxt for ThreadLocalCtxt {}

// Start this id from 1 so it doesn't intersect with the `shared` variant below
static NEXT_CTXT_ID: Mutex<usize> = Mutex::new(1);

fn ctxt_id() -> usize {
let mut next_id = NEXT_CTXT_ID.lock().unwrap();
let id = *next_id;
*next_id = id.wrapping_add(1);

id
}

thread_local! {
static ACTIVE: RefCell<HashMap<usize, ThreadLocalCtxtFrame>> = RefCell::new(HashMap::new());
}

fn current(id: usize) -> ThreadLocalCtxtFrame {
ACTIVE.with(|active| {
active
.borrow_mut()
.entry(id)
.or_insert_with(|| ThreadLocalCtxtFrame { props: None })
.clone()
})
}

fn swap(id: usize, incoming: &mut ThreadLocalCtxtFrame) {
ACTIVE.with(|active| {
let mut active = active.borrow_mut();

let current = active
.entry(id)
.or_insert_with(|| ThreadLocalCtxtFrame { props: None });

mem::swap(current, incoming);
})
}
Loading

0 comments on commit be36fd9

Please sign in to comment.