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}