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}