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}