Skip to content

feat: introduce release-health feature #749

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 14 commits into from
Mar 28, 2025
1 change: 1 addition & 0 deletions sentry-actix/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ actix-web = { version = "4", default-features = false }
futures-util = { version = "0.3.5", default-features = false }
sentry-core = { version = "0.36.0", path = "../sentry-core", default-features = false, features = [
"client",
"release-health",
] }
actix-http = "3.9.0"

Expand Down
3 changes: 2 additions & 1 deletion sentry-core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ client = ["rand"]
# I would love to just have a `log` feature, but this is used inside a macro,
# and macros actually expand features (and extern crate) where they are used!
debug-logs = ["dep:log"]
test = ["client"]
test = ["client", "release-health"]
release-health = []

[dependencies]
cadence = { version = "1.4.0", optional = true }
Expand Down
4 changes: 4 additions & 0 deletions sentry-core/src/api.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#[cfg(feature = "release-health")]
use sentry_types::protocol::v7::SessionStatus;

use crate::protocol::{Event, Level};
Expand Down Expand Up @@ -279,11 +280,13 @@ pub fn last_event_id() -> Option<Uuid> {
///
/// sentry::end_session();
/// ```
#[cfg(feature = "release-health")]
pub fn start_session() {
Hub::with_active(|hub| hub.start_session())
}

/// End the current Release Health Session.
#[cfg(feature = "release-health")]
pub fn end_session() {
end_session_with_status(SessionStatus::Exited)
}
Expand All @@ -295,6 +298,7 @@ pub fn end_session() {
///
/// When an `Abnormal` session should be captured, it has to be done explicitly
/// using this function.
#[cfg(feature = "release-health")]
pub fn end_session_with_status(status: SessionStatus) {
Hub::with_active(|hub| hub.end_session_with_status(status))
}
13 changes: 12 additions & 1 deletion sentry-core/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@ use crate::constants::SDK_INFO;
use crate::protocol::{ClientSdkInfo, Event};
use crate::session::SessionFlusher;
use crate::types::{Dsn, Uuid};
use crate::{ClientOptions, Envelope, Hub, Integration, Scope, SessionMode, Transport};
#[cfg(feature = "release-health")]
use crate::SessionMode;
use crate::{ClientOptions, Envelope, Hub, Integration, Scope, Transport};

impl<T: Into<ClientOptions>> From<T> for Client {
fn from(o: T) -> Client {
Expand Down Expand Up @@ -60,10 +62,15 @@ impl fmt::Debug for Client {
impl Clone for Client {
fn clone(&self) -> Client {
let transport = Arc::new(RwLock::new(self.transport.read().unwrap().clone()));

#[cfg(feature = "release-health")]
let session_flusher = RwLock::new(Some(SessionFlusher::new(
transport.clone(),
self.options.session_mode,
)));
#[cfg(not(feature = "release-health"))]
let session_flusher = RwLock::new(None);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it might be a bit more work, but avoiding the Client.session_flusher field altogether would be slightly more efficient.


Client {
options: self.options.clone(),
transport,
Expand Down Expand Up @@ -131,10 +138,13 @@ impl Client {
sdk_info.integrations.push(integration.name().to_string());
}

#[cfg(feature = "release-health")]
let session_flusher = RwLock::new(Some(SessionFlusher::new(
transport.clone(),
options.session_mode,
)));
#[cfg(not(feature = "release-health"))]
let session_flusher = RwLock::new(None);

Client {
options,
Expand Down Expand Up @@ -266,6 +276,7 @@ impl Client {
let mut envelope: Envelope = event.into();
// For request-mode sessions, we aggregate them all instead of
// flushing them out early.
#[cfg(feature = "release-health")]
if self.options.session_mode == SessionMode::Application {
let session_item = scope.and_then(|scope| {
scope
Expand Down
2 changes: 2 additions & 0 deletions sentry-core/src/clientoptions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ pub type BeforeCallback<T> = Arc<dyn Fn(T) -> Option<T> + Send + Sync>;
///
/// See the [Documentation on Session Modes](https://develop.sentry.dev/sdk/sessions/#sdk-considerations)
/// for more information.
///
/// The `release-health` feature needs to be enabled for this option to have any effect.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum SessionMode {
/// Long running application session.
Expand Down
3 changes: 3 additions & 0 deletions sentry-core/src/hub.rs
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ impl Hub {
///
/// See the global [`start_session`](fn.start_session.html)
/// for more documentation.
#[cfg(feature = "release-health")]
pub fn start_session(&self) {
with_client_impl! {{
self.inner.with_mut(|stack| {
Expand All @@ -143,6 +144,7 @@ impl Hub {
/// End the current Release Health Session.
///
/// See the global [`sentry::end_session`](crate::end_session) for more documentation.
#[cfg(feature = "release-health")]
pub fn end_session(&self) {
self.end_session_with_status(SessionStatus::Exited)
}
Expand All @@ -151,6 +153,7 @@ impl Hub {
///
/// See the global [`end_session_with_status`](crate::end_session_with_status)
/// for more documentation.
#[cfg(feature = "release-health")]
pub fn end_session_with_status(&self, status: SessionStatus) {
with_client_impl! {{
self.inner.with_mut(|stack| {
Expand Down
9 changes: 9 additions & 0 deletions sentry-core/src/session.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,11 @@ use crate::protocol::{
EnvelopeItem, Event, Level, SessionAggregateItem, SessionAggregates, SessionAttributes,
SessionStatus, SessionUpdate,
};

#[cfg(feature = "release-health")]
use crate::scope::StackLayer;

#[cfg(feature = "release-health")]
use crate::types::random_uuid;
use crate::{Client, Envelope};

Expand All @@ -35,6 +39,7 @@ impl Drop for Session {
}

impl Session {
#[cfg(feature = "release-health")]
pub fn from_stack(stack: &StackLayer) -> Option<Self> {
let client = stack.client.as_ref()?;
let options = client.options();
Expand Down Expand Up @@ -110,6 +115,7 @@ impl Session {
}
}

#[cfg(feature = "release-health")]
pub(crate) fn create_envelope_item(&mut self) -> Option<EnvelopeItem> {
if self.dirty {
let item = self.session_update.clone().into();
Expand All @@ -123,6 +129,7 @@ impl Session {

// as defined here: https://develop.sentry.dev/sdk/envelopes/#size-limits
const MAX_SESSION_ITEMS: usize = 100;
#[cfg(feature = "release-health")]
const FLUSH_INTERVAL: Duration = Duration::from_secs(60);

#[derive(Debug, Default)]
Expand Down Expand Up @@ -189,6 +196,7 @@ pub(crate) struct SessionFlusher {

impl SessionFlusher {
/// Creates a new Flusher that will submit envelopes to the given `transport`.
#[cfg(feature = "release-health")]
pub fn new(transport: TransportArc, mode: SessionMode) -> Self {
let queue = Arc::new(Mutex::new(Default::default()));
#[allow(clippy::mutex_atomic)]
Expand Down Expand Up @@ -447,6 +455,7 @@ mod tests {
},
crate::ClientOptions {
release: Some("some-release".into()),
#[cfg(feature = "release-health")]
session_mode: SessionMode::Request,
..Default::default()
},
Expand Down
10 changes: 9 additions & 1 deletion sentry/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,14 @@ all-features = true
rustdoc-args = ["--cfg", "doc_cfg"]

[features]
default = ["backtrace", "contexts", "debug-images", "panic", "transport"]
default = [
"backtrace",
"contexts",
"debug-images",
"panic",
"transport",
"release-health",
]

# default integrations
backtrace = ["sentry-backtrace", "sentry-tracing?/backtrace"]
Expand All @@ -39,6 +46,7 @@ tracing = ["sentry-tracing"]
# other features
test = ["sentry-core/test"]
debug-logs = ["dep:log", "sentry-core/debug-logs"]
release-health = ["sentry-core/release-health"]
# transports
transport = ["reqwest", "native-tls"]
reqwest = ["dep:reqwest", "httpdate", "tokio"]
Expand Down
10 changes: 9 additions & 1 deletion sentry/src/init.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
use std::sync::Arc;

use sentry_core::{sentry_debug, SessionMode};
use sentry_core::sentry_debug;
#[cfg(feature = "release-health")]
use sentry_core::SessionMode;

use crate::defaults::apply_defaults;
use crate::{Client, ClientOptions, Hub};
Expand Down Expand Up @@ -34,6 +36,7 @@ impl Drop for ClientInitGuard {
sentry_debug!("dropping client guard (no client to dispose)");
}
// end any session that might be open before closing the client
#[cfg(feature = "release-health")]
crate::end_session();
self.0.close(None);
}
Expand Down Expand Up @@ -93,8 +96,12 @@ where
C: Into<ClientOptions>,
{
let opts = apply_defaults(opts.into());

#[allow(unused)]
let auto_session_tracking = opts.auto_session_tracking;
#[allow(unused)]
let session_mode = opts.session_mode;

let client = Arc::new(Client::from(opts));

Hub::with(|hub| hub.bind_client(Some(client.clone())));
Expand All @@ -103,6 +110,7 @@ where
} else {
sentry_debug!("initialized disabled sentry client due to disabled or invalid DSN");
}
#[cfg(feature = "release-health")]
if auto_session_tracking && session_mode == SessionMode::Application {
crate::start_session()
}
Expand Down