saluki_app/bootstrap.rs
1//! Bootstrap utilities.
2
3use saluki_config::GenericConfiguration;
4use saluki_error::{ErrorContext as _, GenericError};
5
6use crate::{
7 logging::{initialize_logging, LoggingConfiguration, LoggingGuard},
8 memory::initialize_allocator_telemetry,
9 metrics::initialize_metrics,
10 tls::initialize_tls,
11};
12
13/// A drop guard for ensuring deferred cleanup of resources acquired during bootstrap.
14#[derive(Default)]
15pub struct BootstrapGuard {
16 logging_guard: Option<LoggingGuard>,
17}
18
19/// Early application initialization.
20///
21/// This helper type is used to configure the various low-level shared resources required by the application, such as
22/// the logging and metrics subsystems. It allows for programatic configuration through the use of environment
23/// variables.
24pub struct AppBootstrapper {
25 logging_config: LoggingConfiguration,
26 // TODO: Just prefix at the moment.
27 metrics_config: String,
28}
29
30impl AppBootstrapper {
31 /// Creates a new `AppBootstrapper` from environment-based configuration.
32 ///
33 /// Configuration for bootstrapping will be loaded from environment variables, with a prefix of `DD`.
34 ///
35 /// # Errors
36 ///
37 /// If the given configuration cannot be deserialized correctly, an error is returned.
38 pub fn from_configuration(config: &GenericConfiguration) -> Result<Self, GenericError> {
39 let logging_config = LoggingConfiguration::from_configuration(config)?;
40 Ok(Self {
41 logging_config,
42 metrics_config: "saluki".to_string(),
43 })
44 }
45
46 /// Sets the prefix to use for internal metrics.
47 ///
48 /// Defaults to "saluki".
49 pub fn with_metrics_prefix<S: Into<String>>(mut self, prefix: S) -> Self {
50 self.metrics_config = prefix.into();
51 self
52 }
53
54 /// Executes the bootstrap operation, initializing all configured subsystems.
55 ///
56 /// A [`BootstrapGuard`] is returned, which must be held until the application is ready to shut down. This guard
57 /// ensures that all relevant resources created during the bootstrap phase are properly cleaned up/flushed before
58 /// the application exits.
59 ///
60 /// # Errors
61 ///
62 /// If any of the bootstrap steps fail, an error will be returned.
63 pub async fn bootstrap(self) -> Result<BootstrapGuard, GenericError> {
64 let mut bootstrap_guard = BootstrapGuard::default();
65
66 // Initialize the logging subsystem first, since we want to make it possible to get any logs from the rest of
67 // the bootstrap process.
68 let logging_guard = initialize_logging(&self.logging_config)
69 .await
70 .error_context("Failed to initialize logging subsystem.")?;
71 bootstrap_guard.logging_guard = Some(logging_guard);
72
73 // Initialize everything else.
74 initialize_tls().error_context("Failed to initialize TLS subsystem.")?;
75 initialize_allocator_telemetry()
76 .await
77 .error_context("Failed to initialize allocator telemetry subsystem.")?;
78 initialize_metrics(self.metrics_config)
79 .await
80 .error_context("Failed to initialize metrics subsystem.")?;
81
82 Ok(bootstrap_guard)
83 }
84}