Skip to main content

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