emit/
setup.rs

1/*!
2The [`Setup`] type.
3
4All 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.
5
6You 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`:
7
8```toml
9[dependencies.emit]
10version = "1.8.0"
11default-features = false
12features = ["std"]
13```
14
15This will ensure the `rt` control parameter is always passed to macros so that your custom runtime will always be used.
16
17You can define your runtime as a [`crate::runtime::AmbientSlot`] in a static and initialize it through [`Setup::init_slot`]:
18
19```
20// Define a static runtime to use
21// In this example, we use the default implementations of most things,
22// but you can also bring-your-own
23static RUNTIME: emit::runtime::AmbientSlot = emit::runtime::AmbientSlot::new();
24
25let rt = emit::setup()
26    .emit_to(emit::emitter::from_fn(|evt| println!("{}", evt.msg())))
27    .init_slot(&RUNTIME);
28
29// Use your runtime with the `rt` control parameter
30emit::emit!(rt: RUNTIME.get(), "emitted through a custom runtime");
31
32rt.blocking_flush(std::time::Duration::from_secs(5));
33```
34
35```text
36emitted through a custom runtime
37```
38
39The [`crate::runtime::AmbientSlot`] is type-erased, but you can also define your own fully concrete runtimes too:
40
41```
42// Define a static runtime to use
43// In this example, we use the default implementations of most things,
44// but you can also bring-your-own
45static RUNTIME: emit::runtime::Runtime<
46    MyEmitter,
47    emit::Empty,
48    emit::platform::thread_local_ctxt::ThreadLocalCtxt,
49    emit::platform::system_clock::SystemClock,
50    emit::platform::rand_rng::RandRng,
51> = emit::runtime::Runtime::build(
52    MyEmitter,
53    emit::Empty,
54    emit::platform::thread_local_ctxt::ThreadLocalCtxt::shared(),
55    emit::platform::system_clock::SystemClock::new(),
56    emit::platform::rand_rng::RandRng::new(),
57);
58
59struct MyEmitter;
60
61impl emit::Emitter for MyEmitter {
62    fn emit<E: emit::event::ToEvent>(&self, evt: E) {
63        println!("{}", evt.to_event().msg());
64    }
65
66    fn blocking_flush(&self, _: std::time::Duration) -> bool {
67        // Nothing to flush
68        true
69    }
70}
71
72// Use your runtime with the `rt` control parameter
73emit::emit!(rt: &RUNTIME, "emitted through a custom runtime");
74```
75
76```text
77emitted through a custom runtime
78```
79*/
80
81use core::time::Duration;
82
83use emit_core::{
84    and::And,
85    clock::Clock,
86    ctxt::Ctxt,
87    emitter::Emitter,
88    filter::Filter,
89    rng::Rng,
90    runtime::{
91        AmbientRuntime, AmbientSlot, InternalClock, InternalCtxt, InternalEmitter, InternalFilter,
92        InternalRng, Runtime,
93    },
94};
95
96/**
97Configure `emit` with [`Emitter`]s, [`Filter`]s, and [`Ctxt`].
98
99This function should be called as early in your application as possible. It returns a [`Setup`] builder that, once configured, can be initialized with a call to [`Setup::init`].
100*/
101pub fn setup() -> Setup {
102    Setup::default()
103}
104
105pub use crate::platform::{DefaultClock, DefaultCtxt, DefaultEmitter, DefaultFilter, DefaultRng};
106
107/**
108A configuration builder for an `emit` runtime.
109*/
110#[must_use = "call `.init()` to finish setup"]
111pub struct Setup<
112    TEmitter = DefaultEmitter,
113    TFilter = DefaultFilter,
114    TCtxt = DefaultCtxt,
115    TClock = DefaultClock,
116    TRng = DefaultRng,
117> {
118    emitter: TEmitter,
119    filter: TFilter,
120    ctxt: TCtxt,
121    clock: TClock,
122    rng: TRng,
123}
124
125impl Default for Setup {
126    fn default() -> Self {
127        Self::new()
128    }
129}
130
131impl Setup {
132    /**
133    Create a new builder with the default [`Emitter`], [`Filter`], and [`Ctxt`].
134    */
135    pub fn new() -> Self {
136        Setup {
137            emitter: Default::default(),
138            filter: Default::default(),
139            ctxt: Default::default(),
140            clock: Default::default(),
141            rng: Default::default(),
142        }
143    }
144}
145
146impl<TEmitter: Emitter, TFilter: Filter, TCtxt: Ctxt, TClock: Clock, TRng: Rng>
147    Setup<TEmitter, TFilter, TCtxt, TClock, TRng>
148{
149    /**
150    Set the [`Emitter`] that will receive diagnostic events.
151    */
152    pub fn emit_to<UEmitter: Emitter>(
153        self,
154        emitter: UEmitter,
155    ) -> Setup<UEmitter, TFilter, TCtxt, TClock, TRng> {
156        Setup {
157            emitter,
158            filter: self.filter,
159            ctxt: self.ctxt,
160            clock: self.clock,
161            rng: self.rng,
162        }
163    }
164
165    /**
166    Add an [`Emitter`] that will also receive diagnostic events.
167    */
168    pub fn and_emit_to<UEmitter: Emitter>(
169        self,
170        emitter: UEmitter,
171    ) -> Setup<And<TEmitter, UEmitter>, TFilter, TCtxt, TClock, TRng> {
172        Setup {
173            emitter: self.emitter.and_to(emitter),
174            filter: self.filter,
175            ctxt: self.ctxt,
176            clock: self.clock,
177            rng: self.rng,
178        }
179    }
180
181    /**
182    Map the current [`Emitter`] into a new value.
183    */
184    pub fn map_emitter<UEmitter: Emitter>(
185        self,
186        map: impl FnOnce(TEmitter) -> UEmitter,
187    ) -> Setup<UEmitter, TFilter, TCtxt, TClock, TRng> {
188        Setup {
189            emitter: map(self.emitter),
190            filter: self.filter,
191            ctxt: self.ctxt,
192            clock: self.clock,
193            rng: self.rng,
194        }
195    }
196
197    /**
198    Set the [`Filter`] that will be applied before diagnostic events are emitted.
199    */
200    pub fn emit_when<UFilter: Filter>(
201        self,
202        filter: UFilter,
203    ) -> Setup<TEmitter, UFilter, TCtxt, TClock, TRng> {
204        Setup {
205            emitter: self.emitter,
206            filter,
207            ctxt: self.ctxt,
208            clock: self.clock,
209            rng: self.rng,
210        }
211    }
212
213    /**
214    Add a [`Filter`] that will also be applied before diagnostic events are emitted.
215    */
216    pub fn and_emit_when<UFilter: Filter>(
217        self,
218        filter: UFilter,
219    ) -> Setup<TEmitter, And<TFilter, UFilter>, TCtxt, TClock, TRng> {
220        Setup {
221            emitter: self.emitter,
222            filter: self.filter.and_when(filter),
223            ctxt: self.ctxt,
224            clock: self.clock,
225            rng: self.rng,
226        }
227    }
228
229    /**
230    Set the [`Ctxt`] that will store ambient properties and attach them to diagnostic events.
231    */
232    pub fn with_ctxt<UCtxt: Ctxt>(
233        self,
234        ctxt: UCtxt,
235    ) -> Setup<TEmitter, TFilter, UCtxt, TClock, TRng> {
236        Setup {
237            emitter: self.emitter,
238            filter: self.filter,
239            ctxt,
240            clock: self.clock,
241            rng: self.rng,
242        }
243    }
244
245    /**
246    Map the current [`Ctxt`] into a new value.
247    */
248    pub fn map_ctxt<UCtxt: Ctxt>(
249        self,
250        map: impl FnOnce(TCtxt) -> UCtxt,
251    ) -> Setup<TEmitter, TFilter, UCtxt, TClock, TRng> {
252        Setup {
253            emitter: self.emitter,
254            filter: self.filter,
255            ctxt: map(self.ctxt),
256            clock: self.clock,
257            rng: self.rng,
258        }
259    }
260
261    /**
262    Set the [`Clock`] used to assign timestamps and run timers.
263    */
264    pub fn with_clock<UClock: Clock>(
265        self,
266        clock: UClock,
267    ) -> Setup<TEmitter, TFilter, TCtxt, UClock, TRng> {
268        Setup {
269            emitter: self.emitter,
270            filter: self.filter,
271            ctxt: self.ctxt,
272            clock,
273            rng: self.rng,
274        }
275    }
276
277    /**
278    Set the [`Rng`] used to assign trace and span ids.
279    */
280    pub fn with_rng<URng: Rng>(self, rng: URng) -> Setup<TEmitter, TFilter, TCtxt, TClock, URng> {
281        Setup {
282            emitter: self.emitter,
283            filter: self.filter,
284            ctxt: self.ctxt,
285            clock: self.clock,
286            rng,
287        }
288    }
289
290    /**
291    Initialize a standalone runtime.
292    */
293    pub fn init_runtime(self) -> Runtime<TEmitter, TFilter, TCtxt, TClock, TRng> {
294        Runtime::build(self.emitter, self.filter, self.ctxt, self.clock, self.rng)
295    }
296}
297
298impl<
299        TEmitter: Emitter + Send + Sync + 'static,
300        TFilter: Filter + Send + Sync + 'static,
301        TCtxt: Ctxt + Send + Sync + 'static,
302        TClock: Clock + Send + Sync + 'static,
303        TRng: Rng + Send + Sync + 'static,
304    > Setup<TEmitter, TFilter, TCtxt, TClock, TRng>
305where
306    TCtxt::Frame: Send + 'static,
307{
308    /**
309    Initialize the default runtime used by `emit` macros.
310
311    This method initializes [`crate::runtime::shared`].
312
313    # Panics
314
315    This method will panic if the slot has already been initialized.
316    */
317    #[must_use = "call `flush_on_drop` or call `blocking_flush` at the end of `main` to ensure events are flushed."]
318    #[cfg(feature = "implicit_rt")]
319    pub fn init(self) -> Init<'static, TEmitter, TCtxt> {
320        self.init_slot(emit_core::runtime::shared_slot())
321    }
322
323    /**
324    Try initialize the default runtime used by `emit` macros.
325
326    This method initializes [`crate::runtime::shared`].
327
328    If the slot is already initialized, this method will return `None`.
329    */
330    #[must_use = "call `flush_on_drop` or call `blocking_flush` at the end of `main` to ensure events are flushed."]
331    #[cfg(feature = "implicit_rt")]
332    pub fn try_init(self) -> Option<Init<'static, TEmitter, TCtxt>> {
333        self.try_init_slot(emit_core::runtime::shared_slot())
334    }
335
336    /**
337    Initialize a runtime in the given static `slot`.
338
339    # Panics
340
341    This method will panic if the slot has already been initialized.
342    */
343    #[must_use = "call `flush_on_drop` or call `blocking_flush` at the end of `main` to ensure events are flushed."]
344    pub fn init_slot<'a>(self, slot: &'a AmbientSlot) -> Init<'a, TEmitter, TCtxt> {
345        self.try_init_slot(slot).expect("already initialized")
346    }
347
348    /**
349    Try initialize a runtime in the given static `slot`.
350
351    If the slot is already initialized, this method will return `None`.
352    */
353    #[must_use = "call `flush_on_drop` or call `blocking_flush` at the end of `main` to ensure events are flushed."]
354    pub fn try_init_slot<'a>(self, slot: &'a AmbientSlot) -> Option<Init<'a, TEmitter, TCtxt>> {
355        let ambient = slot.init(
356            Runtime::new()
357                .with_emitter(self.emitter)
358                .with_filter(self.filter)
359                .with_ctxt(self.ctxt)
360                .with_clock(self.clock)
361                .with_rng(self.rng),
362        )?;
363
364        Some(Init {
365            rt: slot.get(),
366            emitter: *ambient.emitter(),
367            ctxt: *ambient.ctxt(),
368        })
369    }
370}
371
372impl<
373        TEmitter: InternalEmitter + Send + Sync + 'static,
374        TFilter: InternalFilter + Send + Sync + 'static,
375        TCtxt: InternalCtxt + Send + Sync + 'static,
376        TClock: InternalClock + Send + Sync + 'static,
377        TRng: InternalRng + Send + Sync + 'static,
378    > Setup<TEmitter, TFilter, TCtxt, TClock, TRng>
379where
380    TCtxt::Frame: Send + 'static,
381{
382    /**
383    Initialize the internal runtime used for diagnosing runtimes themselves.
384
385    This method initializes [`crate::runtime::internal`].
386
387    # Panics
388
389    This method will panic if the slot has already been initialized.
390    */
391    #[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."]
392    #[cfg(feature = "implicit_internal_rt")]
393    pub fn init_internal(self) -> Init<'static, TEmitter, TCtxt> {
394        self.try_init_internal().expect("already initialized")
395    }
396
397    /**
398    Initialize the internal runtime used for diagnosing runtimes themselves.
399
400    This method initializes [`crate::runtime::internal`].
401
402    If the slot is already initialized, this method will return `None`.
403    */
404    #[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."]
405    #[cfg(feature = "implicit_internal_rt")]
406    pub fn try_init_internal(self) -> Option<Init<'static, TEmitter, TCtxt>> {
407        let slot = emit_core::runtime::internal_slot();
408
409        let ambient = slot.init(
410            Runtime::new()
411                .with_emitter(self.emitter)
412                .with_filter(self.filter)
413                .with_ctxt(self.ctxt)
414                .with_clock(self.clock)
415                .with_rng(self.rng),
416        )?;
417
418        Some(Init {
419            rt: slot.get(),
420            emitter: *ambient.emitter(),
421            ctxt: *ambient.ctxt(),
422        })
423    }
424}
425
426/**
427The result of calling [`Setup::init`].
428
429This type is a handle to an initialized runtime that can be used to ensure it's fully flushed with a call to [`Init::blocking_flush`] before your application exits.
430*/
431pub struct Init<'a, TEmitter: Emitter + ?Sized = DefaultEmitter, TCtxt: Ctxt + ?Sized = DefaultCtxt>
432{
433    rt: &'a AmbientRuntime<'a>,
434    emitter: &'a TEmitter,
435    ctxt: &'a TCtxt,
436}
437
438impl<'a, TEmitter: Emitter + ?Sized, TCtxt: Ctxt + ?Sized> Init<'a, TEmitter, TCtxt> {
439    /**
440    Get a reference to the initialized [`Emitter`].
441    */
442    pub fn emitter(&self) -> &'a TEmitter {
443        self.emitter
444    }
445
446    /**
447    Get a reference to the initialized [`Ctxt`].
448    */
449    pub fn ctxt(&self) -> &'a TCtxt {
450        self.ctxt
451    }
452
453    /**
454    Get the underlying runtime that was initialized.
455    */
456    pub fn get(&self) -> &'a AmbientRuntime<'a> {
457        self.rt
458    }
459
460    /**
461    Flush the runtime, ensuring all diagnostic events are fully processed.
462
463    This method forwards to [`Emitter::blocking_flush`], which has details on how the timeout is handled.
464    */
465    pub fn blocking_flush(&self, timeout: Duration) -> bool {
466        self.emitter.blocking_flush(timeout)
467    }
468
469    /**
470    Flush the runtime when the returned guard is dropped, ensuring all diagnostic events are fully processed.
471
472    This method forwards to [`Emitter::blocking_flush`], which has details on how the timeout is handled.
473
474    **Important:** Ensure you bind an identifier to this method, otherwise it will be immediately dropped instead of at the end of your `main`:
475
476    ```
477    # use std::time::Duration;
478    fn main() {
479        // Use an ident like `_guard`, not `_`
480        let _guard = emit::setup().init().flush_on_drop(Duration::from_secs(5));
481
482        // Your code goes here
483    }
484    ```
485    */
486    pub fn flush_on_drop(self, timeout: Duration) -> InitGuard<'a, TEmitter, TCtxt> {
487        InitGuard {
488            inner: self,
489            timeout,
490        }
491    }
492}
493
494/**
495The result of calling [`Init::flush_on_drop`].
496
497This type is a guard that will call [`Init::blocking_flush`] when it goes out of scope. It helps ensure diagnostics are emitted, even if a panic unwinds through your `main` function.
498*/
499pub struct InitGuard<
500    'a,
501    TEmitter: Emitter + ?Sized = DefaultEmitter,
502    TCtxt: Ctxt + ?Sized = DefaultCtxt,
503> {
504    inner: Init<'a, TEmitter, TCtxt>,
505    timeout: Duration,
506}
507
508impl<'a, TEmitter: Emitter + ?Sized, TCtxt: Ctxt + ?Sized> InitGuard<'a, TEmitter, TCtxt> {
509    /**
510    Get the inner [`Init`] value, which can then be used to get the underlying [`AmbientRuntime`].
511    */
512    pub fn inner(&self) -> &Init<'a, TEmitter, TCtxt> {
513        &self.inner
514    }
515}
516
517impl<'a, TEmitter: Emitter + ?Sized, TCtxt: Ctxt + ?Sized> Drop for InitGuard<'a, TEmitter, TCtxt> {
518    fn drop(&mut self) {
519        self.inner.blocking_flush(self.timeout);
520    }
521}
522
523#[cfg(test)]
524mod tests {
525    use super::*;
526
527    #[test]
528    fn try_init() {
529        let slot = AmbientSlot::new();
530
531        assert!(setup().try_init_slot(&slot).is_some());
532        assert!(setup().try_init_slot(&slot).is_none());
533    }
534}