1use 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
96pub fn setup() -> Setup {
102 Setup::default()
103}
104
105pub use crate::platform::{DefaultClock, DefaultCtxt, DefaultEmitter, DefaultFilter, DefaultRng};
106
107#[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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 #[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 #[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 #[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 #[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 #[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 #[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
534pub 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 pub fn emitter(&self) -> &'a TEmitter {
551 self.emitter
552 }
553
554 pub fn ctxt(&self) -> &'a TCtxt {
558 self.ctxt
559 }
560
561 pub fn get(&self) -> &'a AmbientRuntime<'a> {
565 self.rt
566 }
567
568 pub fn blocking_flush(&self, timeout: Duration) -> bool {
574 self.emitter.blocking_flush(timeout)
575 }
576
577 pub fn flush_on_drop(self, timeout: Duration) -> InitGuard<'a, TEmitter, TCtxt> {
595 InitGuard {
596 inner: self,
597 timeout,
598 }
599 }
600}
601
602pub 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 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}