use core::time::Duration;
use emit_core::{
and::And,
clock::Clock,
ctxt::Ctxt,
emitter::Emitter,
filter::Filter,
rng::Rng,
runtime::{
AmbientRuntime, AmbientSlot, InternalClock, InternalCtxt, InternalEmitter, InternalFilter,
InternalRng, Runtime,
},
};
pub fn setup() -> Setup {
Setup::default()
}
pub use crate::platform::{DefaultClock, DefaultCtxt, DefaultEmitter, DefaultFilter, DefaultRng};
#[must_use = "call `.init()` to finish setup"]
pub struct Setup<
TEmitter = DefaultEmitter,
TFilter = DefaultFilter,
TCtxt = DefaultCtxt,
TClock = DefaultClock,
TRng = DefaultRng,
> {
emitter: SetupCell<TEmitter>,
filter: SetupCell<TFilter>,
ctxt: SetupCell<TCtxt>,
clock: SetupCell<TClock>,
rng: SetupCell<TRng>,
}
struct SetupCell<T> {
value: T,
set: bool,
}
impl<T: Default> SetupCell<T> {
fn initial() -> Self {
SetupCell {
value: Default::default(),
set: false,
}
}
}
impl<T> SetupCell<T> {
fn new(value: T) -> Self {
SetupCell { value, set: true }
}
fn set<U>(self, set: impl FnOnce(T) -> U) -> SetupCell<U> {
SetupCell {
value: set(self.value),
set: true,
}
}
}
impl Default for Setup {
fn default() -> Self {
Self::new()
}
}
impl Setup {
pub fn new() -> Self {
Setup {
emitter: SetupCell::initial(),
filter: SetupCell::initial(),
ctxt: SetupCell::initial(),
clock: SetupCell::initial(),
rng: SetupCell::initial(),
}
}
}
impl<TEmitter: Emitter, TFilter: Filter, TCtxt: Ctxt, TClock: Clock, TRng: Rng>
Setup<TEmitter, TFilter, TCtxt, TClock, TRng>
{
pub fn emit_to<UEmitter: Emitter>(
self,
emitter: UEmitter,
) -> Setup<UEmitter, TFilter, TCtxt, TClock, TRng> {
Setup {
emitter: SetupCell::new(emitter),
filter: self.filter,
ctxt: self.ctxt,
clock: self.clock,
rng: self.rng,
}
}
pub fn and_emit_to<UEmitter: Emitter>(
self,
emitter: UEmitter,
) -> Setup<And<TEmitter, UEmitter>, TFilter, TCtxt, TClock, TRng> {
Setup {
emitter: self.emitter.set(|first| first.and_to(emitter)),
filter: self.filter,
ctxt: self.ctxt,
clock: self.clock,
rng: self.rng,
}
}
pub fn map_emitter<UEmitter: Emitter>(
self,
map: impl FnOnce(TEmitter) -> UEmitter,
) -> Setup<UEmitter, TFilter, TCtxt, TClock, TRng> {
Setup {
emitter: self.emitter.set(map),
filter: self.filter,
ctxt: self.ctxt,
clock: self.clock,
rng: self.rng,
}
}
pub fn emit_when<UFilter: Filter>(
self,
filter: UFilter,
) -> Setup<TEmitter, UFilter, TCtxt, TClock, TRng> {
Setup {
emitter: self.emitter,
filter: SetupCell::new(filter),
ctxt: self.ctxt,
clock: self.clock,
rng: self.rng,
}
}
pub fn and_emit_when<UFilter: Filter>(
self,
filter: UFilter,
) -> Setup<TEmitter, And<TFilter, UFilter>, TCtxt, TClock, TRng> {
Setup {
emitter: self.emitter,
filter: self.filter.set(|first| first.and_when(filter)),
ctxt: self.ctxt,
clock: self.clock,
rng: self.rng,
}
}
pub fn with_ctxt<UCtxt: Ctxt>(
self,
ctxt: UCtxt,
) -> Setup<TEmitter, TFilter, UCtxt, TClock, TRng> {
Setup {
emitter: self.emitter,
filter: self.filter,
ctxt: SetupCell::new(ctxt),
clock: self.clock,
rng: self.rng,
}
}
pub fn map_ctxt<UCtxt: Ctxt>(
self,
map: impl FnOnce(TCtxt) -> UCtxt,
) -> Setup<TEmitter, TFilter, UCtxt, TClock, TRng> {
Setup {
emitter: self.emitter,
filter: self.filter,
ctxt: self.ctxt.set(map),
clock: self.clock,
rng: self.rng,
}
}
pub fn with_clock<UClock: Clock>(
self,
clock: UClock,
) -> Setup<TEmitter, TFilter, TCtxt, UClock, TRng> {
Setup {
emitter: self.emitter,
filter: self.filter,
ctxt: self.ctxt,
clock: SetupCell::new(clock),
rng: self.rng,
}
}
pub fn with_rng<URng: Rng>(self, rng: URng) -> Setup<TEmitter, TFilter, TCtxt, TClock, URng> {
Setup {
emitter: self.emitter,
filter: self.filter,
ctxt: self.ctxt,
clock: self.clock,
rng: SetupCell::new(rng),
}
}
pub fn init_runtime(self) -> Runtime<TEmitter, TFilter, TCtxt, TClock, TRng> {
let _ = (
self.emitter.set,
self.filter.set,
self.ctxt.set,
self.clock.set,
self.rng.set,
);
Runtime::build(
self.emitter.value,
self.filter.value,
self.ctxt.value,
self.clock.value,
self.rng.value,
)
}
}
impl<
TEmitter: Emitter + Send + Sync + 'static,
TFilter: Filter + Send + Sync + 'static,
TCtxt: Ctxt + Send + Sync + 'static,
TClock: Clock + Send + Sync + 'static,
TRng: Rng + Send + Sync + 'static,
> Setup<TEmitter, TFilter, TCtxt, TClock, TRng>
where
TCtxt::Frame: Send + 'static,
{
#[cfg(feature = "implicit_rt")]
fn check_platform_is_initialized(&self) {
let _ = (self.ctxt.set, self.clock.set, self.rng.set);
#[cfg(feature = "implicit_internal_rt")]
{
use crate::Frame;
use emit_core::{
empty::Empty, event::Event, props::Props as _, runtime::internal_slot,
template::Template,
};
if !self.emitter.set {
internal_slot().get().emit(Event::new(
mdl!(),
Template::literal("an `Emitter` hasn't been configured; this means any emitted events will be discarded"),
Empty,
Empty,
));
}
if !self.ctxt.set {
let tracks_props =
Frame::root(&self.ctxt.value, ("check_platform_is_initialized", true))
.with(|props| props.pull("check_platform_is_initialized").unwrap_or(false));
if !tracks_props {
internal_slot().get().emit(Event::new(
mdl!(),
Template::literal("a `Ctxt` hasn't been configured and the default does not track properties; this means contextual logging will be unavailable"),
Empty,
Empty,
));
}
}
if !self.clock.set {
if self.clock.value.now().is_none() {
internal_slot().get().emit(Event::new(
mdl!(),
Template::literal("a `Clock` hasn't been configured and the default does not tell time; this means events will not include timestamps"),
Empty,
Empty,
));
}
}
if !self.rng.set {
if self.rng.value.fill([0; 1]).is_none() {
internal_slot().get().emit(Event::new(
mdl!(),
Template::literal("a `Rng` hasn't been configured and the default does not generate values; this means trace and span ids will not be generated"),
Empty,
Empty,
));
}
}
}
}
#[must_use = "call `flush_on_drop` or call `blocking_flush` at the end of `main` to ensure events are flushed."]
#[cfg(feature = "implicit_rt")]
pub fn init(self) -> Init<'static, TEmitter, TCtxt> {
self.check_platform_is_initialized();
self.init_slot(emit_core::runtime::shared_slot())
}
#[must_use = "call `flush_on_drop` or call `blocking_flush` at the end of `main` to ensure events are flushed."]
#[cfg(feature = "implicit_rt")]
pub fn try_init(self) -> Option<Init<'static, TEmitter, TCtxt>> {
self.check_platform_is_initialized();
self.try_init_slot(emit_core::runtime::shared_slot())
}
#[must_use = "call `flush_on_drop` or call `blocking_flush` at the end of `main` to ensure events are flushed."]
pub fn init_slot<'a>(self, slot: &'a AmbientSlot) -> Init<'a, TEmitter, TCtxt> {
self.try_init_slot(slot).expect("already initialized")
}
#[must_use = "call `flush_on_drop` or call `blocking_flush` at the end of `main` to ensure events are flushed."]
pub fn try_init_slot<'a>(self, slot: &'a AmbientSlot) -> Option<Init<'a, TEmitter, TCtxt>> {
let ambient = slot.init(
Runtime::new()
.with_emitter(self.emitter.value)
.with_filter(self.filter.value)
.with_ctxt(self.ctxt.value)
.with_clock(self.clock.value)
.with_rng(self.rng.value),
)?;
Some(Init {
rt: slot.get(),
emitter: *ambient.emitter(),
ctxt: *ambient.ctxt(),
})
}
}
impl<
TEmitter: InternalEmitter + Send + Sync + 'static,
TFilter: InternalFilter + Send + Sync + 'static,
TCtxt: InternalCtxt + Send + Sync + 'static,
TClock: InternalClock + Send + Sync + 'static,
TRng: InternalRng + Send + Sync + 'static,
> Setup<TEmitter, TFilter, TCtxt, TClock, TRng>
where
TCtxt::Frame: Send + 'static,
{
#[must_use = "call `flush_on_drop` or call `blocking_flush` at the end of `main` (after flushing the main runtime) to ensure events are flushed."]
#[cfg(feature = "implicit_internal_rt")]
pub fn init_internal(self) -> Init<'static, TEmitter, TCtxt> {
self.try_init_internal().expect("already initialized")
}
#[must_use = "call `flush_on_drop` or call `blocking_flush` at the end of `main` (after flushing the main runtime) to ensure events are flushed."]
#[cfg(feature = "implicit_internal_rt")]
pub fn try_init_internal(self) -> Option<Init<'static, TEmitter, TCtxt>> {
let slot = emit_core::runtime::internal_slot();
let ambient = slot.init(
Runtime::new()
.with_emitter(self.emitter.value)
.with_filter(self.filter.value)
.with_ctxt(self.ctxt.value)
.with_clock(self.clock.value)
.with_rng(self.rng.value),
)?;
Some(Init {
rt: slot.get(),
emitter: *ambient.emitter(),
ctxt: *ambient.ctxt(),
})
}
}
pub struct Init<'a, TEmitter: Emitter + ?Sized = DefaultEmitter, TCtxt: Ctxt + ?Sized = DefaultCtxt>
{
rt: &'a AmbientRuntime<'a>,
emitter: &'a TEmitter,
ctxt: &'a TCtxt,
}
impl<'a, TEmitter: Emitter + ?Sized, TCtxt: Ctxt + ?Sized> Init<'a, TEmitter, TCtxt> {
pub fn emitter(&self) -> &'a TEmitter {
self.emitter
}
pub fn ctxt(&self) -> &'a TCtxt {
self.ctxt
}
pub fn get(&self) -> &'a AmbientRuntime<'a> {
self.rt
}
pub fn blocking_flush(&self, timeout: Duration) -> bool {
self.emitter.blocking_flush(timeout)
}
pub fn flush_on_drop(self, timeout: Duration) -> InitGuard<'a, TEmitter, TCtxt> {
InitGuard {
inner: self,
timeout,
}
}
}
pub struct InitGuard<
'a,
TEmitter: Emitter + ?Sized = DefaultEmitter,
TCtxt: Ctxt + ?Sized = DefaultCtxt,
> {
inner: Init<'a, TEmitter, TCtxt>,
timeout: Duration,
}
impl<'a, TEmitter: Emitter + ?Sized, TCtxt: Ctxt + ?Sized> InitGuard<'a, TEmitter, TCtxt> {
pub fn inner(&self) -> &Init<'a, TEmitter, TCtxt> {
&self.inner
}
}
impl<'a, TEmitter: Emitter + ?Sized, TCtxt: Ctxt + ?Sized> Drop for InitGuard<'a, TEmitter, TCtxt> {
fn drop(&mut self) {
self.inner.blocking_flush(self.timeout);
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn try_init() {
let slot = AmbientSlot::new();
assert!(setup().try_init_slot(&slot).is_some());
assert!(setup().try_init_slot(&slot).is_none());
}
}