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
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions sentry-actix/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ Sentry client extension for actix-web 3.
edition = "2021"
rust-version = "1.81"

[features]
default = ["release-health"]
release-health = ["sentry-core/release-health"]

[dependencies]
actix-web = { version = "4", default-features = false }
bytes = "1.2"
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))
}
17 changes: 16 additions & 1 deletion sentry-core/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,18 @@ use std::sync::{Arc, RwLock};
use std::time::Duration;

use rand::random;
#[cfg(feature = "release-health")]
use sentry_types::protocol::v7::SessionUpdate;
use sentry_types::random_uuid;

use crate::constants::SDK_INFO;
use crate::protocol::{ClientSdkInfo, Event};
#[cfg(feature = "release-health")]
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 @@ -43,6 +47,7 @@ pub(crate) type TransportArc = Arc<RwLock<Option<Arc<dyn Transport>>>>;
pub struct Client {
options: ClientOptions,
transport: TransportArc,
#[cfg(feature = "release-health")]
session_flusher: RwLock<Option<SessionFlusher>>,
integrations: Vec<(TypeId, Arc<dyn Integration>)>,
pub(crate) sdk_info: ClientSdkInfo,
Expand All @@ -60,13 +65,17 @@ 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,
)));

Client {
options: self.options.clone(),
transport,
#[cfg(feature = "release-health")]
session_flusher,
integrations: self.integrations.clone(),
sdk_info: self.sdk_info.clone(),
Expand Down Expand Up @@ -131,6 +140,7 @@ 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,
Expand All @@ -139,6 +149,7 @@ impl Client {
Client {
options,
transport,
#[cfg(feature = "release-health")]
session_flusher,
integrations,
sdk_info,
Expand Down Expand Up @@ -266,6 +277,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 Expand Up @@ -300,6 +312,7 @@ impl Client {
}
}

#[cfg(feature = "release-health")]
pub(crate) fn enqueue_session(&self, session_update: SessionUpdate<'static>) {
if let Some(ref flusher) = *self.session_flusher.read().unwrap() {
flusher.enqueue(session_update);
Expand All @@ -308,6 +321,7 @@ impl Client {

/// Drains all pending events without shutting down.
pub fn flush(&self, timeout: Option<Duration>) -> bool {
#[cfg(feature = "release-health")]
if let Some(ref flusher) = *self.session_flusher.read().unwrap() {
flusher.flush();
}
Expand All @@ -326,6 +340,7 @@ impl Client {
/// If no timeout is provided the client will wait for as long a
/// `shutdown_timeout` in the client options.
pub fn close(&self, timeout: Option<Duration>) -> bool {
#[cfg(feature = "release-health")]
drop(self.session_flusher.write().unwrap().take());
let transport_opt = self.transport.write().unwrap().take();
if let Some(transport) = transport_opt {
Expand Down
9 changes: 9 additions & 0 deletions sentry-core/src/clientoptions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@ 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.
///
/// **NOTE**: The `release-health` feature (enabled by default) 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 Expand Up @@ -171,8 +174,14 @@ pub struct ClientOptions {
/// When automatic session tracking is enabled, a new "user-mode" session
/// is started at the time of `sentry::init`, and will persist for the
/// application lifetime.
///
/// **NOTE**: The `release-health` feature (enabled by default) needs to be enabled for this option to have
/// any effect.
pub auto_session_tracking: bool,
/// Determine how Sessions are being tracked.
///
/// **NOTE**: The `release-health` feature (enabled by default) needs to be enabled for this option to have
/// any effect.
pub session_mode: SessionMode,
/// Border frames which indicate a border from a backtrace to
/// useless internals. Some are automatically included.
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
19 changes: 15 additions & 4 deletions sentry-core/src/scope/real.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
use std::borrow::Cow;
use std::collections::{HashMap, VecDeque};
use std::fmt;
use std::sync::{Arc, Mutex, PoisonError, RwLock};
#[cfg(feature = "release-health")]
use std::sync::Mutex;
use std::sync::{Arc, PoisonError, RwLock};

use crate::performance::TransactionOrSpan;
use crate::protocol::{Attachment, Breadcrumb, Context, Event, Level, Transaction, User, Value};
#[cfg(feature = "release-health")]
use crate::session::Session;
use crate::Client;

Expand Down Expand Up @@ -45,14 +48,16 @@ pub struct Scope {
pub(crate) tags: Arc<HashMap<String, String>>,
pub(crate) contexts: Arc<HashMap<String, Context>>,
pub(crate) event_processors: Arc<Vec<EventProcessor>>,
#[cfg(feature = "release-health")]
pub(crate) session: Arc<Mutex<Option<Session>>>,
pub(crate) span: Arc<Option<TransactionOrSpan>>,
pub(crate) attachments: Arc<Vec<Attachment>>,
}

impl fmt::Debug for Scope {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Scope")
let mut debug_struct = f.debug_struct("Scope");
debug_struct
.field("level", &self.level)
.field("fingerprint", &self.fingerprint)
.field("transaction", &self.transaction)
Expand All @@ -61,8 +66,12 @@ impl fmt::Debug for Scope {
.field("extra", &self.extra)
.field("tags", &self.tags)
.field("contexts", &self.contexts)
.field("event_processors", &self.event_processors.len())
.field("session", &self.session)
.field("event_processors", &self.event_processors.len());

#[cfg(feature = "release-health")]
debug_struct.field("session", &self.session);

debug_struct
.field("span", &self.span)
.field("attachments", &self.attachments.len())
.finish()
Expand Down Expand Up @@ -341,7 +350,9 @@ impl Scope {
self.span.as_ref().clone()
}

#[allow(unused_variables)]
pub(crate) fn update_session_from_event(&self, event: &Event<'static>) {
#[cfg(feature = "release-health")]
if let Some(session) = self.session.lock().unwrap().as_mut() {
session.update_from_event(event);
}
Expand Down
Loading
Loading