Skip to main content

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.17.2"
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::DefaultCtxt,
49    emit::platform::DefaultClock,
50    emit::platform::DefaultRng,
51> = emit::runtime::Runtime::build(
52    MyEmitter,
53    emit::Empty,
54    emit::platform::DefaultCtxt::shared(),
55    emit::platform::DefaultClock::new(),
56    emit::platform::DefaultRng::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: SetupCell<TEmitter>,
119    filter: SetupCell<TFilter>,
120    ctxt: SetupCell<TCtxt>,
121    clock: SetupCell<TClock>,
122    rng: SetupCell<TRng>,
123}
124
125struct SetupCell<T> {
126    value: T,
127    set: bool,
128}
129
130impl<T: Default> SetupCell<T> {
131    fn initial() -> Self {
132        SetupCell {
133            value: Default::default(),
134            set: false,
135        }
136    }
137}
138
139impl<T> SetupCell<T> {
140    fn new(value: T) -> Self {
141        SetupCell { value, set: true }
142    }
143
144    fn set<U>(self, set: impl FnOnce(T) -> U) -> SetupCell<U> {
145        SetupCell {
146            value: set(self.value),
147            set: true,
148        }
149    }
150}
151
152impl Default for Setup {
153    fn default() -> Self {
154        Self::new()
155    }
156}
157
158impl Setup {
159    /**
160    Create a new builder with the default [`Emitter`], [`Filter`], and [`Ctxt`].
161    */
162    pub fn new() -> Self {
163        Setup {
164            emitter: SetupCell::initial(),
165            filter: SetupCell::initial(),
166            ctxt: SetupCell::initial(),
167            clock: SetupCell::initial(),
168            rng: SetupCell::initial(),
169        }
170    }
171}
172
173impl<TEmitter: Emitter, TFilter: Filter, TCtxt: Ctxt, TClock: Clock, TRng: Rng>
174    Setup<TEmitter, TFilter, TCtxt, TClock, TRng>
175{
176    /**
177    Set the [`Emitter`] that will receive diagnostic events.
178    */
179    pub fn emit_to<UEmitter: Emitter>(
180        self,
181        emitter: UEmitter,
182    ) -> Setup<UEmitter, TFilter, TCtxt, TClock, TRng> {
183        Setup {
184            emitter: SetupCell::new(emitter),
185            filter: self.filter,
186            ctxt: self.ctxt,
187            clock: self.clock,
188            rng: self.rng,
189        }
190    }
191
192    /**
193    Add an [`Emitter`] that will also receive diagnostic events.
194    */
195    pub fn and_emit_to<UEmitter: Emitter>(
196        self,
197        emitter: UEmitter,
198    ) -> Setup<And<TEmitter, UEmitter>, TFilter, TCtxt, TClock, TRng> {
199        Setup {
200            emitter: self.emitter.set(|first| first.and_to(emitter)),
201            filter: self.filter,
202            ctxt: self.ctxt,
203            clock: self.clock,
204            rng: self.rng,
205        }
206    }
207
208    /**
209    Map the current [`Emitter`] into a new value.
210    */
211    pub fn map_emitter<UEmitter: Emitter>(
212        self,
213        map: impl FnOnce(TEmitter) -> UEmitter,
214    ) -> Setup<UEmitter, TFilter, TCtxt, TClock, TRng> {
215        Setup {
216            emitter: self.emitter.set(map),
217            filter: self.filter,
218            ctxt: self.ctxt,
219            clock: self.clock,
220            rng: self.rng,
221        }
222    }
223
224    /**
225    Set the [`Filter`] that will be applied before diagnostic events are emitted.
226    */
227    pub fn emit_when<UFilter: Filter>(
228        self,
229        filter: UFilter,
230    ) -> Setup<TEmitter, UFilter, TCtxt, TClock, TRng> {
231        Setup {
232            emitter: self.emitter,
233            filter: SetupCell::new(filter),
234            ctxt: self.ctxt,
235            clock: self.clock,
236            rng: self.rng,
237        }
238    }
239
240    /**
241    Add a [`Filter`] that will also be applied before diagnostic events are emitted.
242    */
243    pub fn and_emit_when<UFilter: Filter>(
244        self,
245        filter: UFilter,
246    ) -> Setup<TEmitter, And<TFilter, UFilter>, TCtxt, TClock, TRng> {
247        Setup {
248            emitter: self.emitter,
249            filter: self.filter.set(|first| first.and_when(filter)),
250            ctxt: self.ctxt,
251            clock: self.clock,
252            rng: self.rng,
253        }
254    }
255
256    /**
257    Set the [`Ctxt`] that will store ambient properties and attach them to diagnostic events.
258    */
259    pub fn with_ctxt<UCtxt: Ctxt>(
260        self,
261        ctxt: UCtxt,
262    ) -> Setup<TEmitter, TFilter, UCtxt, TClock, TRng> {
263        Setup {
264            emitter: self.emitter,
265            filter: self.filter,
266            ctxt: SetupCell::new(ctxt),
267            clock: self.clock,
268            rng: self.rng,
269        }
270    }
271
272    /**
273    Map the current [`Ctxt`] into a new value.
274    */
275    pub fn map_ctxt<UCtxt: Ctxt>(
276        self,
277        map: impl FnOnce(TCtxt) -> UCtxt,
278    ) -> Setup<TEmitter, TFilter, UCtxt, TClock, TRng> {
279        Setup {
280            emitter: self.emitter,
281            filter: self.filter,
282            ctxt: self.ctxt.set(map),
283            clock: self.clock,
284            rng: self.rng,
285        }
286    }
287
288    /**
289    Set the [`Clock`] used to assign timestamps and run timers.
290    */
291    pub fn with_clock<UClock: Clock>(
292        self,
293        clock: UClock,
294    ) -> Setup<TEmitter, TFilter, TCtxt, UClock, TRng> {
295        Setup {
296            emitter: self.emitter,
297            filter: self.filter,
298            ctxt: self.ctxt,
299            clock: SetupCell::new(clock),
300            rng: self.rng,
301        }
302    }
303
304    /**
305    Set the [`Rng`] used to assign trace and span ids.
306    */
307    pub fn with_rng<URng: Rng>(self, rng: URng) -> Setup<TEmitter, TFilter, TCtxt, TClock, URng> {
308        Setup {
309            emitter: self.emitter,
310            filter: self.filter,
311            ctxt: self.ctxt,
312            clock: self.clock,
313            rng: SetupCell::new(rng),
314        }
315    }
316
317    /**
318    Initialize a standalone runtime.
319    */
320    pub fn init_runtime(self) -> Runtime<TEmitter, TFilter, TCtxt, TClock, TRng> {
321        let _ = (
322            self.emitter.set,
323            self.filter.set,
324            self.ctxt.set,
325            self.clock.set,
326            self.rng.set,
327        );
328
329        Runtime::build(
330            self.emitter.value,
331            self.filter.value,
332            self.ctxt.value,
333            self.clock.value,
334            self.rng.value,
335        )
336    }
337}
338
339impl<
340        TEmitter: Emitter + Send + Sync + 'static,
341        TFilter: Filter + Send + Sync + 'static,
342        TCtxt: Ctxt + Send + Sync + 'static,
343        TClock: Clock + Send + Sync + 'static,
344        TRng: Rng + Send + Sync + 'static,
345    > Setup<TEmitter, TFilter, TCtxt, TClock, TRng>
346where
347    TCtxt::Frame: Send + 'static,
348{
349    #[cfg(feature = "implicit_rt")]
350    fn check_platform_is_initialized(&self) {
351        let _ = (self.ctxt.set, self.clock.set, self.rng.set);
352
353        #[cfg(feature = "implicit_internal_rt")]
354        {
355            use crate::Frame;
356            use emit_core::{
357                empty::Empty, event::Event, props::Props as _, runtime::internal_slot,
358                template::Template,
359            };
360
361            if !self.emitter.set {
362                internal_slot().get().emit(Event::new(
363                    mdl!(),
364                    Template::literal("an `Emitter` hasn't been configured; this means any emitted events will be discarded"),
365                    Empty,
366                    Empty,
367                ));
368            }
369
370            if !self.ctxt.set {
371                // Check whether the default context is able to track properties
372                let tracks_props =
373                    Frame::root(&self.ctxt.value, ("check_platform_is_initialized", true))
374                        .with(|props| props.pull("check_platform_is_initialized").unwrap_or(false));
375
376                if !tracks_props {
377                    internal_slot().get().emit(Event::new(
378                        mdl!(),
379                        Template::literal("a `Ctxt` hasn't been configured and the default does not track properties; this means contextual logging will be unavailable"),
380                        Empty,
381                        Empty,
382                    ));
383                }
384            }
385
386            if !self.clock.set {
387                // Check whether the default clock is able to tell time
388                if self.clock.value.now().is_none() {
389                    internal_slot().get().emit(Event::new(
390                        mdl!(),
391                        Template::literal("a `Clock` hasn't been configured and the default does not tell time; this means events will not include timestamps"),
392                        Empty,
393                        Empty,
394                    ));
395                }
396            }
397
398            if !self.rng.set {
399                // Check whether the default rng is able to generate data
400                if self.rng.value.fill([0; 1]).is_none() {
401                    internal_slot().get().emit(Event::new(
402                        mdl!(),
403                        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"),
404                        Empty,
405                        Empty,
406                    ));
407                }
408            }
409        }
410    }
411
412    /**
413    Initialize the default runtime used by `emit` macros.
414
415    This method initializes [`crate::runtime::shared`].
416
417    # Panics
418
419    This method will panic if the slot has already been initialized.
420    */
421    #[must_use = "call `flush_on_drop` or call `blocking_flush` at the end of `main` to ensure events are flushed."]
422    #[cfg(feature = "implicit_rt")]
423    pub fn init(self) -> Init<'static, TEmitter, TCtxt> {
424        self.check_platform_is_initialized();
425
426        self.init_slot(emit_core::runtime::shared_slot())
427    }
428
429    /**
430    Try initialize the default runtime used by `emit` macros.
431
432    This method initializes [`crate::runtime::shared`].
433
434    If the slot is already initialized, this method will return `None`.
435    */
436    #[must_use = "call `flush_on_drop` or call `blocking_flush` at the end of `main` to ensure events are flushed."]
437    #[cfg(feature = "implicit_rt")]
438    pub fn try_init(self) -> Option<Init<'static, TEmitter, TCtxt>> {
439        self.check_platform_is_initialized();
440
441        self.try_init_slot(emit_core::runtime::shared_slot())
442    }
443
444    /**
445    Initialize a runtime in the given static `slot`.
446
447    # Panics
448
449    This method will panic if the slot has already been initialized.
450    */
451    #[must_use = "call `flush_on_drop` or call `blocking_flush` at the end of `main` to ensure events are flushed."]
452    pub fn init_slot<'a>(self, slot: &'a AmbientSlot) -> Init<'a, TEmitter, TCtxt> {
453        self.try_init_slot(slot).expect("already initialized")
454    }
455
456    /**
457    Try initialize a runtime in the given static `slot`.
458
459    If the slot is already initialized, this method will return `None`.
460    */
461    #[must_use = "call `flush_on_drop` or call `blocking_flush` at the end of `main` to ensure events are flushed."]
462    pub fn try_init_slot<'a>(self, slot: &'a AmbientSlot) -> Option<Init<'a, TEmitter, TCtxt>> {
463        let ambient = slot.init(
464            Runtime::new()
465                .with_emitter(self.emitter.value)
466                .with_filter(self.filter.value)
467                .with_ctxt(self.ctxt.value)
468                .with_clock(self.clock.value)
469                .with_rng(self.rng.value),
470        )?;
471
472        Some(Init {
473            rt: slot.get(),
474            emitter: *ambient.emitter(),
475            ctxt: *ambient.ctxt(),
476        })
477    }
478}
479
480impl<
481        TEmitter: InternalEmitter + Send + Sync + 'static,
482        TFilter: InternalFilter + Send + Sync + 'static,
483        TCtxt: InternalCtxt + Send + Sync + 'static,
484        TClock: InternalClock + Send + Sync + 'static,
485        TRng: InternalRng + Send + Sync + 'static,
486    > Setup<TEmitter, TFilter, TCtxt, TClock, TRng>
487where
488    TCtxt::Frame: Send + 'static,
489{
490    /**
491    Initialize the internal runtime used for diagnosing runtimes themselves.
492
493    This method initializes [`crate::runtime::internal`].
494
495    # Panics
496
497    This method will panic if the slot has already been initialized.
498    */
499    #[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."]
500    #[cfg(feature = "implicit_internal_rt")]
501    pub fn init_internal(self) -> Init<'static, TEmitter, TCtxt> {
502        self.try_init_internal().expect("already initialized")
503    }
504
505    /**
506    Initialize the internal runtime used for diagnosing runtimes themselves.
507
508    This method initializes [`crate::runtime::internal`].
509
510    If the slot is already initialized, this method will return `None`.
511    */
512    #[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."]
513    #[cfg(feature = "implicit_internal_rt")]
514    pub fn try_init_internal(self) -> Option<Init<'static, TEmitter, TCtxt>> {
515        let slot = emit_core::runtime::internal_slot();
516
517        let ambient = slot.init(
518            Runtime::new()
519                .with_emitter(self.emitter.value)
520                .with_filter(self.filter.value)
521                .with_ctxt(self.ctxt.value)
522                .with_clock(self.clock.value)
523                .with_rng(self.rng.value),
524        )?;
525
526        Some(Init {
527            rt: slot.get(),
528            emitter: *ambient.emitter(),
529            ctxt: *ambient.ctxt(),
530        })
531    }
532}
533
534/**
535The result of calling [`Setup::init`].
536
537This 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.
538*/
539pub struct Init<'a, TEmitter: Emitter + ?Sized = DefaultEmitter, TCtxt: Ctxt + ?Sized = DefaultCtxt>
540{
541    rt: &'a AmbientRuntime<'a>,
542    emitter: &'a TEmitter,
543    ctxt: &'a TCtxt,
544}
545
546impl<'a, TEmitter: Emitter + ?Sized, TCtxt: Ctxt + ?Sized> Init<'a, TEmitter, TCtxt> {
547    /**
548    Get a reference to the initialized [`Emitter`].
549    */
550    pub fn emitter(&self) -> &'a TEmitter {
551        self.emitter
552    }
553
554    /**
555    Get a reference to the initialized [`Ctxt`].
556    */
557    pub fn ctxt(&self) -> &'a TCtxt {
558        self.ctxt
559    }
560
561    /**
562    Get the underlying runtime that was initialized.
563    */
564    pub fn get(&self) -> &'a AmbientRuntime<'a> {
565        self.rt
566    }
567
568    /**
569    Flush the runtime, ensuring all diagnostic events are fully processed.
570
571    This method forwards to [`Emitter::blocking_flush`], which has details on how the timeout is handled.
572    */
573    pub fn blocking_flush(&self, timeout: Duration) -> bool {
574        self.emitter.blocking_flush(timeout)
575    }
576
577    /**
578    Flush the runtime when the returned guard is dropped, ensuring all diagnostic events are fully processed.
579
580    This method forwards to [`Emitter::blocking_flush`], which has details on how the timeout is handled.
581
582    **Important:** Ensure you bind an identifier to this method, otherwise it will be immediately dropped instead of at the end of your `main`:
583
584    ```
585    # use std::time::Duration;
586    fn main() {
587        // Use an ident like `_guard`, not `_`
588        let _guard = emit::setup().init().flush_on_drop(Duration::from_secs(5));
589
590        // Your code goes here
591    }
592    ```
593    */
594    pub fn flush_on_drop(self, timeout: Duration) -> InitGuard<'a, TEmitter, TCtxt> {
595        InitGuard {
596            inner: self,
597            timeout,
598        }
599    }
600}
601
602/**
603The result of calling [`Init::flush_on_drop`].
604
605This 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.
606*/
607pub struct InitGuard<
608    'a,
609    TEmitter: Emitter + ?Sized = DefaultEmitter,
610    TCtxt: Ctxt + ?Sized = DefaultCtxt,
611> {
612    inner: Init<'a, TEmitter, TCtxt>,
613    timeout: Duration,
614}
615
616impl<'a, TEmitter: Emitter + ?Sized, TCtxt: Ctxt + ?Sized> InitGuard<'a, TEmitter, TCtxt> {
617    /**
618    Get the inner [`Init`] value, which can then be used to get the underlying [`AmbientRuntime`].
619    */
620    pub fn inner(&self) -> &Init<'a, TEmitter, TCtxt> {
621        &self.inner
622    }
623}
624
625impl<'a, TEmitter: Emitter + ?Sized, TCtxt: Ctxt + ?Sized> Drop for InitGuard<'a, TEmitter, TCtxt> {
626    fn drop(&mut self) {
627        self.inner.blocking_flush(self.timeout);
628    }
629}
630
631#[cfg(test)]
632mod tests {
633    use super::*;
634
635    #[test]
636    fn try_init() {
637        let slot = AmbientSlot::new();
638
639        assert!(setup().try_init_slot(&slot).is_some());
640        assert!(setup().try_init_slot(&slot).is_none());
641    }
642}