sentry/
defaults.rs

1use std::env;
2use std::{borrow::Cow, sync::Arc};
3
4use crate::transports::DefaultTransportFactory;
5use crate::types::Dsn;
6use crate::{ClientOptions, Integration};
7
8/// Apply default client options.
9///
10/// Extends the given `ClientOptions` with default options such as a default
11/// transport, a set of default integrations if not requested otherwise, and
12/// also sets the `dsn`, `release`, `environment`, and proxy settings based on
13/// environment variables.
14///
15/// When the [`ClientOptions::default_integrations`] option is set to
16/// `true` (the default), the following integrations will be added *before*
17/// any manually defined integrations, depending on enabled feature flags:
18///
19/// 1. [`AttachStacktraceIntegration`] (`feature = "backtrace"`)
20/// 2. [`DebugImagesIntegration`] (`feature = "debug-images"`)
21/// 3. [`ContextIntegration`] (`feature = "contexts"`)
22/// 4. [`PanicIntegration`] (`feature = "panic"`)
23/// 5. [`ProcessStacktraceIntegration`] (`feature = "backtrace"`)
24///
25/// Some integrations can be used multiple times, however, the
26/// [`PanicIntegration`] can not, and it will not pick up custom panic
27/// extractors when it is defined multiple times.
28///
29/// # Examples
30/// ```
31/// std::env::set_var("SENTRY_RELEASE", "release-from-env");
32///
33/// let options = sentry::ClientOptions::default();
34/// assert_eq!(options.release, None);
35/// assert!(options.transport.is_none());
36///
37/// let options = sentry::apply_defaults(options);
38/// assert_eq!(options.release, Some("release-from-env".into()));
39/// assert!(options.transport.is_some());
40/// ```
41///
42/// [`AttachStacktraceIntegration`]: integrations/backtrace/struct.AttachStacktraceIntegration.html
43/// [`DebugImagesIntegration`]: integrations/debug_images/struct.DebugImagesIntegration.html
44/// [`ContextIntegration`]: integrations/contexts/struct.ContextIntegration.html
45/// [`PanicIntegration`]: integrations/panic/struct.PanicIntegration.html
46/// [`ProcessStacktraceIntegration`]: integrations/backtrace/struct.ProcessStacktraceIntegration.html
47pub fn apply_defaults(mut opts: ClientOptions) -> ClientOptions {
48    if opts.transport.is_none() {
49        opts.transport = Some(Arc::new(DefaultTransportFactory));
50    }
51    if opts.default_integrations {
52        // default integrations need to be ordered *before* custom integrations,
53        // since they also process events in order
54        let mut integrations: Vec<Arc<dyn Integration>> = vec![];
55        #[cfg(feature = "backtrace")]
56        {
57            integrations.push(Arc::new(sentry_backtrace::AttachStacktraceIntegration));
58        }
59        #[cfg(feature = "debug-images")]
60        {
61            integrations.push(Arc::new(
62                sentry_debug_images::DebugImagesIntegration::default(),
63            ))
64        }
65        #[cfg(feature = "contexts")]
66        {
67            integrations.push(Arc::new(sentry_contexts::ContextIntegration::default()));
68        }
69        #[cfg(feature = "panic")]
70        {
71            integrations.push(Arc::new(sentry_panic::PanicIntegration::default()));
72        }
73        #[cfg(feature = "backtrace")]
74        {
75            integrations.push(Arc::new(sentry_backtrace::ProcessStacktraceIntegration));
76        }
77        integrations.extend(opts.integrations);
78        opts.integrations = integrations;
79    }
80    if opts.dsn.is_none() {
81        opts.dsn = env::var("SENTRY_DSN")
82            .ok()
83            .and_then(|dsn| dsn.parse::<Dsn>().ok());
84    }
85    if opts.release.is_none() {
86        opts.release = env::var("SENTRY_RELEASE").ok().map(Cow::Owned);
87    }
88    if opts.environment.is_none() {
89        opts.environment =
90            env::var("SENTRY_ENVIRONMENT")
91                .ok()
92                .map(Cow::Owned)
93                .or(Some(Cow::Borrowed(if cfg!(debug_assertions) {
94                    "development"
95                } else {
96                    "production"
97                })));
98    }
99    if opts.http_proxy.is_none() {
100        opts.http_proxy = std::env::var("HTTP_PROXY")
101            .ok()
102            .map(Cow::Owned)
103            .or_else(|| std::env::var("http_proxy").ok().map(Cow::Owned));
104    }
105    if opts.https_proxy.is_none() {
106        opts.https_proxy = std::env::var("HTTPS_PROXY")
107            .ok()
108            .map(Cow::Owned)
109            .or_else(|| std::env::var("https_proxy").ok().map(Cow::Owned))
110            .or_else(|| opts.http_proxy.clone());
111    }
112    if let Ok(accept_invalid_certs) = std::env::var("SSL_VERIFY") {
113        opts.accept_invalid_certs = !accept_invalid_certs.parse().unwrap_or(true);
114    }
115    opts
116}
117
118#[cfg(test)]
119mod tests {
120    use super::*;
121
122    #[test]
123    fn test_default_environment() {
124        let opts = ClientOptions {
125            environment: Some("explicit-env".into()),
126            ..Default::default()
127        };
128        let opts = apply_defaults(opts);
129        assert_eq!(opts.environment.unwrap(), "explicit-env");
130
131        let opts = apply_defaults(Default::default());
132        // I doubt anyone runs test code without debug assertions
133        assert_eq!(opts.environment.unwrap(), "development");
134
135        env::set_var("SENTRY_ENVIRONMENT", "env-from-env");
136        let opts = apply_defaults(Default::default());
137        assert_eq!(opts.environment.unwrap(), "env-from-env");
138    }
139}