From 865559723ad8ad779fe5c523f766ba1ad3a564cd Mon Sep 17 00:00:00 2001 From: Arpad Borsos Date: Fri, 14 Aug 2020 15:26:02 +0200 Subject: [PATCH 01/10] feat: Add basic session support --- sentry-core/Cargo.toml | 1 + sentry-core/src/api.rs | 15 +++ sentry-core/src/client.rs | 25 +++- sentry-core/src/envelope.rs | 34 +++-- sentry-core/src/hub.rs | 44 ++++++- sentry-core/src/lib.rs | 1 + sentry-core/src/scope/real.rs | 44 +++++-- sentry-core/src/session.rs | 231 ++++++++++++++++++++++++++++++++++ sentry/examples/health.rs | 34 +++++ sentry/src/transport.rs | 3 + 10 files changed, 410 insertions(+), 22 deletions(-) create mode 100644 sentry-core/src/session.rs create mode 100644 sentry/examples/health.rs diff --git a/sentry-core/Cargo.toml b/sentry-core/Cargo.toml index aaf41313b..cc82f456e 100644 --- a/sentry-core/Cargo.toml +++ b/sentry-core/Cargo.toml @@ -24,6 +24,7 @@ test = ["client"] [dependencies] sentry-types = { version = "0.19.1", path = "../sentry-types" } +serde = { version = "1.0.104", features = ["derive"] } lazy_static = "1.4.0" im = { version = "14.2.0", optional = true } rand = { version = "0.7.3", optional = true } diff --git a/sentry-core/src/api.rs b/sentry-core/src/api.rs index 10c21df9b..0cdde3402 100644 --- a/sentry-core/src/api.rs +++ b/sentry-core/src/api.rs @@ -261,3 +261,18 @@ pub fn last_event_id() -> Option { Hub::with(|hub| hub.last_event_id()) }} } + +/// Start a new session for Release Health. +/// +/// This implicitly closes any previous session and starts recording a new +/// session. +pub fn start_session() { + Hub::with_active(|hub| hub.start_session()) +} + +/// Stop the current Release Health session. +/// +/// See [`start_session`](fn.start_session.html) for examples. +pub fn end_session() { + Hub::with_active(|hub| hub.end_session()) +} diff --git a/sentry-core/src/client.rs b/sentry-core/src/client.rs index 6dcb4e09d..3ce252918 100644 --- a/sentry-core/src/client.rs +++ b/sentry-core/src/client.rs @@ -10,8 +10,9 @@ use rand::random; use crate::constants::SDK_INFO; use crate::protocol::{ClientSdkInfo, Event}; +use crate::session::SessionUpdate; use crate::types::{Dsn, Uuid}; -use crate::{ClientOptions, Hub, Integration, Scope, Transport}; +use crate::{ClientOptions, Envelope, Hub, Integration, Scope, Transport}; impl> From for Client { fn from(o: T) -> Client { @@ -239,7 +240,21 @@ impl Client { if self.sample_should_send() { if let Some(event) = self.prepare_event(event, scope) { let event_id = event.event_id; - transport.send_envelope(event.into()); + let session = scope.and_then(|scope| { + if let SessionUpdate::NeedsFlushing(session) = + scope.update_session_from_event(&event) + { + Some(session) + } else { + None + } + }); + let mut envelope: Envelope = event.into(); + if let Some(session) = session { + envelope.add(session.into()); + } + //sentry_debug!("sending envelope {:#?}", envelope); + transport.send_envelope(envelope); return event_id; } } @@ -247,6 +262,12 @@ impl Client { Default::default() } + pub(crate) fn capture_envelope(&self, envelope: Envelope) { + if let Some(ref transport) = *self.transport.read().unwrap() { + transport.send_envelope(envelope); + } + } + /// Drains all pending events and shuts down the transport behind the /// client. After shutting down the transport is removed. /// diff --git a/sentry-core/src/envelope.rs b/sentry-core/src/envelope.rs index 9d4ad3059..350feba32 100644 --- a/sentry-core/src/envelope.rs +++ b/sentry-core/src/envelope.rs @@ -1,14 +1,15 @@ use std::io::Write; use crate::protocol::Event; +use crate::session::Session; use crate::types::Uuid; -#[derive(Clone, Debug, PartialEq)] +#[derive(Clone, Debug)] #[non_exhaustive] -enum EnvelopeItem { +pub(crate) enum EnvelopeItem { Event(Event<'static>), + Session(Session), // TODO: - // * Session, // * Attachment, // etc… } @@ -19,6 +20,12 @@ impl From> for EnvelopeItem { } } +impl From for EnvelopeItem { + fn from(session: Session) -> Self { + EnvelopeItem::Session(session) + } +} + /// A Sentry Envelope. /// /// An Envelope is the data format that Sentry uses for Ingestion. It can contain @@ -27,7 +34,7 @@ impl From> for EnvelopeItem { /// /// See the [documentation on Envelopes](https://develop.sentry.dev/sdk/envelopes/) /// for more details. -#[derive(Clone, Default, Debug, PartialEq)] +#[derive(Clone, Default, Debug)] pub struct Envelope { event_id: Option, items: Vec, @@ -39,6 +46,10 @@ impl Envelope { Default::default() } + pub(crate) fn add(&mut self, item: EnvelopeItem) { + self.items.push(item); + } + /// Returns the Envelopes Uuid, if any. pub fn uuid(&self) -> Option<&Uuid> { self.event_id.as_ref() @@ -48,12 +59,11 @@ impl Envelope { /// /// [`Event`]: protocol/struct.Event.html pub fn event(&self) -> Option<&Event<'static>> { - // until we actually add more items: - #[allow(clippy::unnecessary_filter_map)] self.items .iter() .filter_map(|item| match item { EnvelopeItem::Event(event) => Some(event), + _ => None, }) .next() } @@ -77,14 +87,13 @@ impl Envelope { // write each item: for item in &self.items { // we write them to a temporary buffer first, since we need their length - serde_json::to_writer( - &mut item_buf, - match item { - EnvelopeItem::Event(event) => event, - }, - )?; + match item { + EnvelopeItem::Event(event) => serde_json::to_writer(&mut item_buf, event)?, + EnvelopeItem::Session(session) => serde_json::to_writer(&mut item_buf, session)?, + } let item_type = match item { EnvelopeItem::Event(_) => "event", + EnvelopeItem::Session(_) => "session", }; writeln!( writer, @@ -113,6 +122,7 @@ impl From> for Envelope { #[cfg(test)] mod test { use super::*; + fn to_buf(envelope: Envelope) -> Vec { let mut vec = Vec::new(); envelope.to_writer(&mut vec).unwrap(); diff --git a/sentry-core/src/hub.rs b/sentry-core/src/hub.rs index 2c0018c79..b094ae51f 100644 --- a/sentry-core/src/hub.rs +++ b/sentry-core/src/hub.rs @@ -9,10 +9,11 @@ use std::thread; use std::time::Duration; use crate::protocol::{Breadcrumb, Event, Level}; +use crate::session::{Session, SessionStatus}; use crate::types::Uuid; use crate::{event_from_error, Integration, IntoBreadcrumbs, Scope, ScopeGuard}; #[cfg(feature = "client")] -use crate::{scope::Stack, Client}; +use crate::{scope::Stack, Client, Envelope}; #[cfg(feature = "client")] lazy_static::lazy_static! { @@ -311,6 +312,47 @@ impl Hub { }) } + /// Start a new session for Release Health. + /// + /// This implicitly closes any previous session and starts recording a new + /// session. + /// + /// See the global [`start_session`](fn.start_session.html) + /// for more documentation. + pub fn start_session(&self) { + self.end_session(); + // in theory, this could race and we should really do the end/start in a + // single locked section. + with_client_impl! {{ + self.inner.with_mut(|stack| { + if let Some(session) = Session::from_stack(stack.top()) { + let mut scope = Arc::make_mut(&mut stack.top_mut().scope); + scope.session = Some(Arc::new(Mutex::new(session))); + } + }); + }} + } + + /// Stop the current Release Health session. + /// + /// See the global [`end_session`](fn.end_session.html) + /// for more documentation. + pub fn end_session(&self) { + with_client_impl! {{ + let _ = self.inner.with_mut(|stack| { + let mut scope = Arc::make_mut(&mut stack.top_mut().scope); + let mut session = Arc::try_unwrap(scope.session.take()?).ok()?.into_inner().ok()?; + let client = stack.top().client.as_ref()?; + + session.close(); + let mut envelope = Envelope::new(); + envelope.add(session.into()); + client.capture_envelope(envelope); + None::<()> + }); + }} + } + /// Pushes a new scope. /// /// This returns a guard that when dropped will pop the scope again. diff --git a/sentry-core/src/lib.rs b/sentry-core/src/lib.rs index c2a9d50bb..67060b962 100644 --- a/sentry-core/src/lib.rs +++ b/sentry-core/src/lib.rs @@ -64,6 +64,7 @@ mod hub; mod integration; mod intodsn; mod scope; +mod session; mod transport; // public api or exports from this crate diff --git a/sentry-core/src/scope/real.rs b/sentry-core/src/scope/real.rs index 26929f766..1feebce31 100644 --- a/sentry-core/src/scope/real.rs +++ b/sentry-core/src/scope/real.rs @@ -1,9 +1,10 @@ use std::borrow::Cow; use std::fmt; -use std::sync::{Arc, PoisonError, RwLock}; +use std::sync::{Arc, Mutex, PoisonError, RwLock}; use crate::protocol::{Breadcrumb, Context, Event, Level, User, Value}; -use crate::Client; +use crate::session::{Session, SessionUpdate}; +use crate::{Client, Envelope}; #[derive(Debug)] pub struct Stack { @@ -41,6 +42,7 @@ pub struct Scope { pub(crate) tags: im::HashMap, pub(crate) contexts: im::HashMap, pub(crate) event_processors: im::Vector>, + pub(crate) session: Option>>, } impl fmt::Debug for Scope { @@ -55,6 +57,7 @@ impl fmt::Debug for Scope { .field("tags", &self.tags) .field("contexts", &self.contexts) .field("event_processors", &self.event_processors.len()) + .field("session", &self.session) .finish() } } @@ -71,6 +74,7 @@ impl Default for Scope { tags: Default::default(), contexts: Default::default(), event_processors: Default::default(), + session: None, } } } @@ -89,15 +93,19 @@ impl Stack { } pub fn push(&mut self) { - let scope = self.layers[self.layers.len() - 1].clone(); - self.layers.push(scope); + let mut layer = self.layers[self.layers.len() - 1].clone(); + // don’t clone the session itself, it should only be on one layer, so + // that `end`-ing it works correctly. + let mut scope = Arc::make_mut(&mut layer.scope); + scope.session = None; + self.layers.push(layer); } - pub fn pop(&mut self) { + pub fn pop(&mut self) -> Option { if self.layers.len() <= 1 { panic!("Pop from empty stack"); } - self.layers.pop().unwrap(); + self.layers.pop() } pub fn top(&self) -> &StackLayer { @@ -136,7 +144,21 @@ impl Drop for ScopeGuard { if stack.depth() != depth { panic!("Tried to pop guards out of order"); } - stack.pop(); + let mut layer = stack.pop().unwrap(); + (|| { + let scope = Arc::make_mut(&mut layer.scope); + let mut session = Arc::try_unwrap(scope.session.take()?) + .ok()? + .into_inner() + .ok()?; + let client = layer.client.as_ref()?; + + session.close(); + let mut envelope = Envelope::new(); + envelope.add(session.into()); + client.capture_envelope(envelope); + None::<()> + })(); } } } @@ -257,4 +279,12 @@ impl Scope { Some(event) } + + pub(crate) fn update_session_from_event(&self, event: &Event<'static>) -> SessionUpdate { + if let Some(session) = &self.session { + session.lock().unwrap().update_from_event(event) + } else { + SessionUpdate::Unchanged + } + } } diff --git a/sentry-core/src/session.rs b/sentry-core/src/session.rs new file mode 100644 index 000000000..638bd0c88 --- /dev/null +++ b/sentry-core/src/session.rs @@ -0,0 +1,231 @@ +//! Release Health Sessions +//! +//! https://develop.sentry.dev/sdk/sessions/ + +use std::fmt; +use std::{borrow::Cow, sync::Arc, time::Instant}; + +use crate::protocol::{Event, Level}; +use crate::{ + scope::StackLayer, + types::{DateTime, Utc, Uuid}, +}; +use sentry_types::protocol::v7::User; + +/// Represents the status of a session. +#[derive(Copy, Clone, Debug, PartialEq)] +pub enum SessionStatus { + Ok, + Crashed, + #[allow(dead_code)] + Abnormal, + Exited, +} + +pub enum SessionUpdate { + NeedsFlushing(Session), + Unchanged, +} + +#[derive(Clone, Debug, PartialEq)] +pub struct Session { + session_id: Uuid, + status: SessionStatus, + errors: usize, + user: Option>, + release: Cow<'static, str>, + environment: Option>, + started: Instant, + started_utc: DateTime, + duration: Option, + init: bool, + dirty: bool, +} + +impl Session { + pub fn from_stack(stack: &StackLayer) -> Option { + let options = stack.client.as_ref()?.options(); + Some(Self { + session_id: Uuid::new_v4(), + status: SessionStatus::Ok, + errors: 0, + user: stack.scope.user.clone(), + release: options.release.clone()?, + environment: options.environment.clone(), + started: Instant::now(), + started_utc: Utc::now(), + duration: None, + init: true, + dirty: true, + }) + } + + pub(crate) fn update_from_event(&mut self, event: &Event<'static>) -> SessionUpdate { + let mut has_error = event.level >= Level::Error; + let mut is_crash = false; + for exc in &event.exception.values { + has_error = true; + if let Some(mechanism) = &exc.mechanism { + if matches!(mechanism.handled, Some(false)) { + is_crash = true; + break; + } + } + } + + if is_crash { + self.status = SessionStatus::Crashed; + } + if has_error { + self.errors += 1; + self.dirty = true; + } + + if self.dirty { + self.dirty = false; + let session = self.clone(); + self.init = false; + SessionUpdate::NeedsFlushing(session) + } else { + SessionUpdate::Unchanged + } + } + + pub(crate) fn close(&mut self) { + self.duration = Some(self.started.elapsed().as_secs_f64()); + if self.status == SessionStatus::Ok { + self.status = SessionStatus::Exited; + } + } +} + +#[derive(serde::Serialize)] +struct Attrs { + release: Cow<'static, str>, + #[serde(skip_serializing_if = "Option::is_none")] + environment: Option>, +} + +impl serde::Serialize for Session { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + use serde::ser::SerializeStruct; + + let mut session = serializer.serialize_struct("Session", 8)?; + session.serialize_field("sid", &self.session_id)?; + let did = self.user.as_ref().and_then(|user| { + user.id + .as_ref() + .or_else(|| user.email.as_ref()) + .or_else(|| user.username.as_ref()) + }); + if let Some(did) = did { + session.serialize_field("did", &did)?; + } else { + session.skip_field("did")?; + } + + session.serialize_field( + "status", + match self.status { + SessionStatus::Ok => "ok", + SessionStatus::Crashed => "crashed", + SessionStatus::Abnormal => "abnormal", + SessionStatus::Exited => "exited", + }, + )?; + session.serialize_field("errors", &self.errors)?; + session.serialize_field("started", &self.started_utc)?; + + if let Some(duration) = self.duration { + session.serialize_field("duration", &duration)?; + } else { + session.skip_field("duration")?; + } + if self.init { + session.serialize_field("init", &true)?; + } else { + session.skip_field("init")?; + } + + session.serialize_field( + "attrs", + &Attrs { + release: self.release.clone(), + environment: self.environment.clone(), + }, + )?; + + session.end() + } +} + +#[cfg(all(test, feature = "test"))] +mod tests { + use crate as sentry; + use crate::test::with_captured_envelopes_options; + use crate::{ClientOptions, Envelope}; + + fn to_buf(envelope: &Envelope) -> Vec { + let mut vec = Vec::new(); + envelope.to_writer(&mut vec).unwrap(); + vec + } + fn to_str(envelope: &Envelope) -> String { + String::from_utf8(to_buf(envelope)).unwrap() + } + + #[test] + fn test_session_startstop() { + let envelopes = with_captured_envelopes_options( + || { + sentry::start_session(); + std::thread::sleep(std::time::Duration::from_millis(10)); + sentry::end_session(); + }, + ClientOptions { + release: Some("some-release".into()), + ..Default::default() + }, + ); + assert_eq!(envelopes.len(), 1); + + let body = to_str(&envelopes[0]); + assert!(body.starts_with("{}\n{\"type\":\"session\",")); + assert!(body.contains(r#""attrs":{"release":"some-release"}"#)); + assert!(body.contains(r#""status":"exited","errors":0"#)); + assert!(body.contains(r#""init":true"#)); + } + + #[test] + fn test_session_error() { + let envelopes = with_captured_envelopes_options( + || { + sentry::start_session(); + + let err = "NaN".parse::().unwrap_err(); + sentry::capture_error(&err); + + sentry::end_session(); + }, + ClientOptions { + release: Some("some-release".into()), + ..Default::default() + }, + ); + assert_eq!(envelopes.len(), 2); + + let body = to_str(&envelopes[0]); + assert!(body.contains("{\"type\":\"session\",")); + assert!(body.contains(r#""attrs":{"release":"some-release"}"#)); + assert!(body.contains(r#""status":"ok","errors":1"#)); + assert!(body.contains(r#""init":true"#)); + + let body = to_str(&envelopes[1]); + assert!(body.contains("{\"type\":\"session\",")); + assert!(body.contains(r#""status":"exited","errors":1"#)); + assert!(!body.contains(r#""init":true"#)); + } +} diff --git a/sentry/examples/health.rs b/sentry/examples/health.rs new file mode 100644 index 000000000..bef3a9a9b --- /dev/null +++ b/sentry/examples/health.rs @@ -0,0 +1,34 @@ +fn main() { + let _sentry = sentry::init(sentry::ClientOptions { + // release health requires a session to be set + release: sentry::release_name!(), + debug: true, + ..Default::default() + }); + + let handle = std::thread::spawn(|| { + // this session will be set to crashed + sentry::start_session(); + std::thread::sleep(std::time::Duration::from_secs(3)); + panic!("oh no!"); + }); + + sentry::start_session(); + + sentry::capture_message( + "anything with a level >= Error will increase the error count", + sentry::Level::Error, + ); + + // or any error that has an explicit exception attached + let err = "NaN".parse::().unwrap_err(); + sentry::capture_error(&err); + + std::thread::sleep(std::time::Duration::from_secs(2)); + + // so this session will increase the errors count by 2, but otherwise has + // a clean exit. + sentry::end_session(); + + handle.join().ok(); +} diff --git a/sentry/src/transport.rs b/sentry/src/transport.rs index 632fe4c51..aab1162da 100644 --- a/sentry/src/transport.rs +++ b/sentry/src/transport.rs @@ -225,6 +225,7 @@ implement_http_transport! { let mut body = Vec::new(); envelope.to_writer(&mut body).unwrap(); + sentry_debug!("sending to \"{}\"", url); match http_client .post(url.as_str()) .body(body) @@ -232,6 +233,7 @@ implement_http_transport! { .send() { Ok(resp) => { + sentry_debug!("got response {} {:#?}", resp.status(), resp.headers()); if resp.status() == 429 { if let Some(retry_after) = resp .headers() @@ -242,6 +244,7 @@ implement_http_transport! { disabled = Some(retry_after); } } + sentry_debug!("{}", resp.text().unwrap()); } Err(err) => { sentry_debug!("Failed to send event: {}", err); From 72b6d4f049fecc66a36ef094feedb8db37f27cf7 Mon Sep 17 00:00:00 2001 From: Arpad Borsos Date: Mon, 31 Aug 2020 10:38:42 +0200 Subject: [PATCH 02/10] fix MSRV and imports --- sentry-core/src/session.rs | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/sentry-core/src/session.rs b/sentry-core/src/session.rs index 638bd0c88..d446d9776 100644 --- a/sentry-core/src/session.rs +++ b/sentry-core/src/session.rs @@ -2,15 +2,13 @@ //! //! https://develop.sentry.dev/sdk/sessions/ -use std::fmt; -use std::{borrow::Cow, sync::Arc, time::Instant}; +use std::borrow::Cow; +use std::sync::Arc; +use std::time::Instant; -use crate::protocol::{Event, Level}; -use crate::{ - scope::StackLayer, - types::{DateTime, Utc, Uuid}, -}; -use sentry_types::protocol::v7::User; +use crate::protocol::{Event, Level, User}; +use crate::scope::StackLayer; +use crate::types::{DateTime, Utc, Uuid}; /// Represents the status of a session. #[derive(Copy, Clone, Debug, PartialEq)] @@ -66,7 +64,7 @@ impl Session { for exc in &event.exception.values { has_error = true; if let Some(mechanism) = &exc.mechanism { - if matches!(mechanism.handled, Some(false)) { + if let Some(false) = mechanism.handled { is_crash = true; break; } From 4af2cd745f35b197ecfa0d70408750537a011653 Mon Sep 17 00:00:00 2001 From: Arpad Borsos Date: Mon, 31 Aug 2020 10:48:10 +0200 Subject: [PATCH 03/10] remove excessive transport logging --- sentry/src/transport.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/sentry/src/transport.rs b/sentry/src/transport.rs index aab1162da..632fe4c51 100644 --- a/sentry/src/transport.rs +++ b/sentry/src/transport.rs @@ -225,7 +225,6 @@ implement_http_transport! { let mut body = Vec::new(); envelope.to_writer(&mut body).unwrap(); - sentry_debug!("sending to \"{}\"", url); match http_client .post(url.as_str()) .body(body) @@ -233,7 +232,6 @@ implement_http_transport! { .send() { Ok(resp) => { - sentry_debug!("got response {} {:#?}", resp.status(), resp.headers()); if resp.status() == 429 { if let Some(retry_after) = resp .headers() @@ -244,7 +242,6 @@ implement_http_transport! { disabled = Some(retry_after); } } - sentry_debug!("{}", resp.text().unwrap()); } Err(err) => { sentry_debug!("Failed to send event: {}", err); From ed12e7a124cb64810f77724729de278cf183213c Mon Sep 17 00:00:00 2001 From: Arpad Borsos Date: Mon, 31 Aug 2020 11:39:19 +0200 Subject: [PATCH 04/10] clear up and test session inheritance --- sentry-core/src/hub.rs | 19 ++--- sentry-core/src/scope/real.rs | 36 +++------ sentry-core/src/session.rs | 133 +++++++++++++++++++++++++++------- 3 files changed, 125 insertions(+), 63 deletions(-) diff --git a/sentry-core/src/hub.rs b/sentry-core/src/hub.rs index b094ae51f..0a72cb544 100644 --- a/sentry-core/src/hub.rs +++ b/sentry-core/src/hub.rs @@ -321,13 +321,14 @@ impl Hub { /// for more documentation. pub fn start_session(&self) { self.end_session(); - // in theory, this could race and we should really do the end/start in a - // single locked section. with_client_impl! {{ self.inner.with_mut(|stack| { - if let Some(session) = Session::from_stack(stack.top()) { - let mut scope = Arc::make_mut(&mut stack.top_mut().scope); - scope.session = Some(Arc::new(Mutex::new(session))); + let top = stack.top_mut(); + if let Some(session) = Session::from_stack(top) { + // When creating a *new* session, we make sure it is unique, + // as to no inherit *backwards* to any parents. + let mut scope = Arc::make_mut(&mut top.scope); + scope.session = Arc::new(Mutex::new(Some(session))); } }); }} @@ -339,10 +340,10 @@ impl Hub { /// for more documentation. pub fn end_session(&self) { with_client_impl! {{ - let _ = self.inner.with_mut(|stack| { - let mut scope = Arc::make_mut(&mut stack.top_mut().scope); - let mut session = Arc::try_unwrap(scope.session.take()?).ok()?.into_inner().ok()?; - let client = stack.top().client.as_ref()?; + let _ = self.inner.with(|stack| { + let top = stack.top(); + let mut session = top.scope.session.lock().unwrap().take()?; + let client = top.client.as_ref()?; session.close(); let mut envelope = Envelope::new(); diff --git a/sentry-core/src/scope/real.rs b/sentry-core/src/scope/real.rs index 1feebce31..19af77282 100644 --- a/sentry-core/src/scope/real.rs +++ b/sentry-core/src/scope/real.rs @@ -4,7 +4,7 @@ use std::sync::{Arc, Mutex, PoisonError, RwLock}; use crate::protocol::{Breadcrumb, Context, Event, Level, User, Value}; use crate::session::{Session, SessionUpdate}; -use crate::{Client, Envelope}; +use crate::Client; #[derive(Debug)] pub struct Stack { @@ -42,7 +42,7 @@ pub struct Scope { pub(crate) tags: im::HashMap, pub(crate) contexts: im::HashMap, pub(crate) event_processors: im::Vector>, - pub(crate) session: Option>>, + pub(crate) session: Arc>>, } impl fmt::Debug for Scope { @@ -74,7 +74,7 @@ impl Default for Scope { tags: Default::default(), contexts: Default::default(), event_processors: Default::default(), - session: None, + session: Default::default(), } } } @@ -93,19 +93,15 @@ impl Stack { } pub fn push(&mut self) { - let mut layer = self.layers[self.layers.len() - 1].clone(); - // don’t clone the session itself, it should only be on one layer, so - // that `end`-ing it works correctly. - let mut scope = Arc::make_mut(&mut layer.scope); - scope.session = None; + let layer = self.layers[self.layers.len() - 1].clone(); self.layers.push(layer); } - pub fn pop(&mut self) -> Option { + pub fn pop(&mut self) { if self.layers.len() <= 1 { panic!("Pop from empty stack"); } - self.layers.pop() + self.layers.pop().unwrap(); } pub fn top(&self) -> &StackLayer { @@ -144,21 +140,7 @@ impl Drop for ScopeGuard { if stack.depth() != depth { panic!("Tried to pop guards out of order"); } - let mut layer = stack.pop().unwrap(); - (|| { - let scope = Arc::make_mut(&mut layer.scope); - let mut session = Arc::try_unwrap(scope.session.take()?) - .ok()? - .into_inner() - .ok()?; - let client = layer.client.as_ref()?; - - session.close(); - let mut envelope = Envelope::new(); - envelope.add(session.into()); - client.capture_envelope(envelope); - None::<()> - })(); + stack.pop(); } } } @@ -281,8 +263,8 @@ impl Scope { } pub(crate) fn update_session_from_event(&self, event: &Event<'static>) -> SessionUpdate { - if let Some(session) = &self.session { - session.lock().unwrap().update_from_event(event) + if let Some(session) = self.session.lock().unwrap().as_mut() { + session.update_from_event(event) } else { SessionUpdate::Unchanged } diff --git a/sentry-core/src/session.rs b/sentry-core/src/session.rs index d446d9776..08aeaca7f 100644 --- a/sentry-core/src/session.rs +++ b/sentry-core/src/session.rs @@ -163,8 +163,7 @@ impl serde::Serialize for Session { #[cfg(all(test, feature = "test"))] mod tests { use crate as sentry; - use crate::test::with_captured_envelopes_options; - use crate::{ClientOptions, Envelope}; + use crate::Envelope; fn to_buf(envelope: &Envelope) -> Vec { let mut vec = Vec::new(); @@ -174,20 +173,26 @@ mod tests { fn to_str(envelope: &Envelope) -> String { String::from_utf8(to_buf(envelope)).unwrap() } - - #[test] - fn test_session_startstop() { - let envelopes = with_captured_envelopes_options( - || { - sentry::start_session(); - std::thread::sleep(std::time::Duration::from_millis(10)); - sentry::end_session(); - }, - ClientOptions { + fn capture_envelopes(f: F) -> Vec + where + F: FnOnce(), + { + crate::test::with_captured_envelopes_options( + f, + crate::ClientOptions { release: Some("some-release".into()), ..Default::default() }, - ); + ) + } + + #[test] + fn test_session_startstop() { + let envelopes = capture_envelopes(|| { + sentry::start_session(); + std::thread::sleep(std::time::Duration::from_millis(10)); + sentry::end_session(); + }); assert_eq!(envelopes.len(), 1); let body = to_str(&envelopes[0]); @@ -199,31 +204,105 @@ mod tests { #[test] fn test_session_error() { - let envelopes = with_captured_envelopes_options( - || { - sentry::start_session(); + let envelopes = capture_envelopes(|| { + sentry::start_session(); - let err = "NaN".parse::().unwrap_err(); - sentry::capture_error(&err); + let err = "NaN".parse::().unwrap_err(); + sentry::capture_error(&err); - sentry::end_session(); - }, - ClientOptions { - release: Some("some-release".into()), - ..Default::default() - }, - ); + sentry::end_session(); + }); assert_eq!(envelopes.len(), 2); let body = to_str(&envelopes[0]); - assert!(body.contains("{\"type\":\"session\",")); + assert!(body.contains(r#"{"type":"session","#)); assert!(body.contains(r#""attrs":{"release":"some-release"}"#)); assert!(body.contains(r#""status":"ok","errors":1"#)); assert!(body.contains(r#""init":true"#)); let body = to_str(&envelopes[1]); - assert!(body.contains("{\"type\":\"session\",")); + assert!(body.contains(r#"{"type":"session","#)); assert!(body.contains(r#""status":"exited","errors":1"#)); assert!(!body.contains(r#""init":true"#)); } + + /// For _user-mode_ sessions, we want to inherit the session for any _new_ + /// Hub that is spawned from the main thread Hub which already has a session + /// attached + #[test] + fn test_inherit_session_from_top() { + let envelopes = capture_envelopes(|| { + sentry::start_session(); + + let err = "NaN".parse::().unwrap_err(); + sentry::capture_error(&err); + + // create a new Hub which should have the same session + let hub = std::sync::Arc::new(sentry::Hub::new_from_top(sentry::Hub::current())); + + sentry::Hub::run(hub, || { + let err = "NaN".parse::().unwrap_err(); + sentry::capture_error(&err); + + sentry::with_scope( + |_| {}, + || { + let err = "NaN".parse::().unwrap_err(); + sentry::capture_error(&err); + }, + ); + }); + + sentry::end_session(); + }); + + assert_eq!(envelopes.len(), 4); // 3 errors and one session end + + let body = to_str(&envelopes[3]); + assert!(body.contains(r#"{"type":"session","#)); + assert!(body.contains(r#""status":"exited","errors":3"#)); + assert!(!body.contains(r#""init":true"#)); + } + + /// We want to forward-inherit sessions as the previous test asserted, but + /// not *backwards*. So any new session created in a derived Hub and scope + /// will only get updates from that particular scope. + #[test] + fn test_dont_inherit_session_backwards() { + let envelopes = capture_envelopes(|| { + let hub = std::sync::Arc::new(sentry::Hub::new_from_top(sentry::Hub::current())); + + sentry::Hub::run(hub, || { + sentry::with_scope( + |_| {}, + || { + sentry::start_session(); + + let err = "NaN".parse::().unwrap_err(); + sentry::capture_error(&err); + }, + ); + + let err = "NaN".parse::().unwrap_err(); + sentry::capture_error(&err); + }); + + let err = "NaN".parse::().unwrap_err(); + sentry::capture_error(&err); + }); + + assert_eq!(envelopes.len(), 3); // 3 errors, but no session end + + let body = to_str(&envelopes[0]); + assert!(body.contains(r#"{"type":"session","#)); + assert!(body.contains(r#""attrs":{"release":"some-release"}"#)); + assert!(body.contains(r#""status":"ok","errors":1"#)); + assert!(body.contains(r#""init":true"#)); + + // the other two events should not have session updates + let body = to_str(&envelopes[1]); + assert!(!body.contains(r#"{"type":"session","#)); + let body = to_str(&envelopes[2]); + assert!(!body.contains(r#"{"type":"session","#)); + } } From cb4f8c2c9f5e16a926a50cb4bc73e1bfbf76fcd0 Mon Sep 17 00:00:00 2001 From: Arpad Borsos Date: Mon, 31 Aug 2020 16:57:25 +0200 Subject: [PATCH 05/10] use a SessionGuard instead of explicit end_session calls --- sentry-core/src/api.rs | 25 ++++++++++++++----------- sentry-core/src/client.rs | 1 - sentry-core/src/clientoptions.rs | 8 ++++++++ sentry-core/src/hub.rs | 31 ++++--------------------------- sentry-core/src/lib.rs | 2 +- sentry-core/src/scope/noop.rs | 23 ++++++++++++++++++----- sentry-core/src/scope/real.rs | 31 ++++++++++++++++++++++++++++++- sentry-core/src/session.rs | 24 ++++++++++++------------ sentry/examples/health.rs | 13 ++++++++----- sentry/src/init.rs | 15 ++++++++++++--- 10 files changed, 107 insertions(+), 66 deletions(-) diff --git a/sentry-core/src/api.rs b/sentry-core/src/api.rs index 0cdde3402..c9c273fa4 100644 --- a/sentry-core/src/api.rs +++ b/sentry-core/src/api.rs @@ -1,6 +1,6 @@ use crate::protocol::{Event, Level}; use crate::types::Uuid; -use crate::{Hub, Integration, IntoBreadcrumbs, Scope}; +use crate::{Hub, Integration, IntoBreadcrumbs, Scope, SessionGuard}; /// Captures an event on the currently active client if any. /// @@ -264,15 +264,18 @@ pub fn last_event_id() -> Option { /// Start a new session for Release Health. /// -/// This implicitly closes any previous session and starts recording a new -/// session. -pub fn start_session() { - Hub::with_active(|hub| hub.start_session()) -} - -/// Stop the current Release Health session. +/// Returns a guard that ends the session on drop and thus determines the +/// lifetime of the session. +/// +/// # Examples /// -/// See [`start_session`](fn.start_session.html) for examples. -pub fn end_session() { - Hub::with_active(|hub| hub.end_session()) +/// ``` +/// let session = sentry::start_session(); +/// +/// // capturing any event / error here will update the sessions `errors` count. +/// +/// drop(session); +/// ``` +pub fn start_session() -> SessionGuard { + Hub::with_active(|hub| hub.start_session()) } diff --git a/sentry-core/src/client.rs b/sentry-core/src/client.rs index 3ce252918..8cbaf4663 100644 --- a/sentry-core/src/client.rs +++ b/sentry-core/src/client.rs @@ -253,7 +253,6 @@ impl Client { if let Some(session) = session { envelope.add(session.into()); } - //sentry_debug!("sending envelope {:#?}", envelope); transport.send_envelope(envelope); return event_id; } diff --git a/sentry-core/src/clientoptions.rs b/sentry-core/src/clientoptions.rs index 48aa5f161..0f6c43ab9 100644 --- a/sentry-core/src/clientoptions.rs +++ b/sentry-core/src/clientoptions.rs @@ -82,6 +82,12 @@ pub struct ClientOptions { /// The timeout on client drop for draining events on shutdown. pub shutdown_timeout: Duration, // Other options not documented in Unified API + /// Enable Release Health Session tracking. + /// + /// When automatic session tracking is enabled, as is the default, a new + /// "user-mode" session is started at the time of `sentry::init`, and will + /// persist for the application lifetime. + pub auto_session_tracking: bool, /// Border frames which indicate a border from a backtrace to /// useless internals. Some are automatically included. pub extra_border_frames: Vec<&'static str>, @@ -147,6 +153,7 @@ impl fmt::Debug for ClientOptions { .field("http_proxy", &self.http_proxy) .field("https_proxy", &self.https_proxy) .field("shutdown_timeout", &self.shutdown_timeout) + .field("auto_session_tracking", &self.auto_session_tracking) .field("extra_border_frames", &self.extra_border_frames) .field("trim_backtraces", &self.trim_backtraces) .field("user_agent", &self.user_agent) @@ -176,6 +183,7 @@ impl Default for ClientOptions { http_proxy: None, https_proxy: None, shutdown_timeout: Duration::from_secs(2), + auto_session_tracking: true, extra_border_frames: vec![], trim_backtraces: true, user_agent: Cow::Borrowed(&USER_AGENT), diff --git a/sentry-core/src/hub.rs b/sentry-core/src/hub.rs index 0a72cb544..26450e2a8 100644 --- a/sentry-core/src/hub.rs +++ b/sentry-core/src/hub.rs @@ -11,7 +11,7 @@ use std::time::Duration; use crate::protocol::{Breadcrumb, Event, Level}; use crate::session::{Session, SessionStatus}; use crate::types::Uuid; -use crate::{event_from_error, Integration, IntoBreadcrumbs, Scope, ScopeGuard}; +use crate::{event_from_error, Integration, IntoBreadcrumbs, Scope, ScopeGuard, SessionGuard}; #[cfg(feature = "client")] use crate::{scope::Stack, Client, Envelope}; @@ -314,13 +314,9 @@ impl Hub { /// Start a new session for Release Health. /// - /// This implicitly closes any previous session and starts recording a new - /// session. - /// /// See the global [`start_session`](fn.start_session.html) /// for more documentation. - pub fn start_session(&self) { - self.end_session(); + pub fn start_session(&self) -> SessionGuard { with_client_impl! {{ self.inner.with_mut(|stack| { let top = stack.top_mut(); @@ -330,27 +326,8 @@ impl Hub { let mut scope = Arc::make_mut(&mut top.scope); scope.session = Arc::new(Mutex::new(Some(session))); } - }); - }} - } - - /// Stop the current Release Health session. - /// - /// See the global [`end_session`](fn.end_session.html) - /// for more documentation. - pub fn end_session(&self) { - with_client_impl! {{ - let _ = self.inner.with(|stack| { - let top = stack.top(); - let mut session = top.scope.session.lock().unwrap().take()?; - let client = top.client.as_ref()?; - - session.close(); - let mut envelope = Envelope::new(); - envelope.add(session.into()); - client.capture_envelope(envelope); - None::<()> - }); + SessionGuard(top.client.clone().map(|client| (client, top.scope.clone()))) + }) }} } diff --git a/sentry-core/src/lib.rs b/sentry-core/src/lib.rs index 67060b962..215ca57ab 100644 --- a/sentry-core/src/lib.rs +++ b/sentry-core/src/lib.rs @@ -77,7 +77,7 @@ pub use crate::futures::{SentryFuture, SentryFutureExt}; pub use crate::hub::Hub; pub use crate::integration::Integration; pub use crate::intodsn::IntoDsn; -pub use crate::scope::{Scope, ScopeGuard}; +pub use crate::scope::{Scope, ScopeGuard, SessionGuard}; pub use crate::transport::{Transport, TransportFactory}; // deprecated exports diff --git a/sentry-core/src/scope/noop.rs b/sentry-core/src/scope/noop.rs index 0695a3b32..8200c43e9 100644 --- a/sentry-core/src/scope/noop.rs +++ b/sentry-core/src/scope/noop.rs @@ -2,12 +2,18 @@ use std::fmt; use crate::protocol::{Context, Event, Level, User, Value}; -/// The minimal scope. +/// A minimal API session guard. /// -/// In minimal API mode all modification functions are available as normally -/// just that generally calling them is impossible. -#[derive(Debug, Clone)] -pub struct Scope; +/// Doesn't do anything but can be debug formatted. +#[derive(Default)] +#[must_use = "The duration of the Session from start to end is defined by the lifetime of this guard."] +pub struct SessionGuard; + +impl fmt::Debug for SessionGuard { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "SessionGuard") + } +} /// A minimal API scope guard. /// @@ -21,6 +27,13 @@ impl fmt::Debug for ScopeGuard { } } +/// The minimal scope. +/// +/// In minimal API mode all modification functions are available as normally +/// just that generally calling them is impossible. +#[derive(Debug, Clone)] +pub struct Scope; + impl Scope { /// Clear the scope. /// diff --git a/sentry-core/src/scope/real.rs b/sentry-core/src/scope/real.rs index 19af77282..b20ebfcf6 100644 --- a/sentry-core/src/scope/real.rs +++ b/sentry-core/src/scope/real.rs @@ -4,7 +4,7 @@ use std::sync::{Arc, Mutex, PoisonError, RwLock}; use crate::protocol::{Breadcrumb, Context, Event, Level, User, Value}; use crate::session::{Session, SessionUpdate}; -use crate::Client; +use crate::{Client, Envelope}; #[derive(Debug)] pub struct Stack { @@ -118,6 +118,35 @@ impl Stack { } } +/// A session guard. +/// +/// This is returned from [`Hub::start_session`] and will automatically end the +/// newly created session on drop. +/// +/// [`Hub::start_session`]: struct.Hub.html#method.start_session +#[derive(Default)] +#[must_use = "The duration of the Session from start to end is defined by the lifetime of this guard."] +pub struct SessionGuard(pub(crate) Option<(Arc, Arc)>); + +impl fmt::Debug for SessionGuard { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "SessionGuard") + } +} + +impl Drop for SessionGuard { + fn drop(&mut self) { + if let Some((client, scope)) = self.0.take() { + if let Some(mut session) = scope.session.lock().unwrap().take() { + session.close(); + let mut envelope = Envelope::new(); + envelope.add(session.into()); + client.capture_envelope(envelope); + } + } + } +} + /// A scope guard. /// /// This is returned from [`Hub::push_scope`] and will automatically pop the diff --git a/sentry-core/src/session.rs b/sentry-core/src/session.rs index 08aeaca7f..cbfb9d688 100644 --- a/sentry-core/src/session.rs +++ b/sentry-core/src/session.rs @@ -189,9 +189,8 @@ mod tests { #[test] fn test_session_startstop() { let envelopes = capture_envelopes(|| { - sentry::start_session(); + let _session = sentry::start_session(); std::thread::sleep(std::time::Duration::from_millis(10)); - sentry::end_session(); }); assert_eq!(envelopes.len(), 1); @@ -205,12 +204,10 @@ mod tests { #[test] fn test_session_error() { let envelopes = capture_envelopes(|| { - sentry::start_session(); + let _session = sentry::start_session(); let err = "NaN".parse::().unwrap_err(); sentry::capture_error(&err); - - sentry::end_session(); }); assert_eq!(envelopes.len(), 2); @@ -232,7 +229,7 @@ mod tests { #[test] fn test_inherit_session_from_top() { let envelopes = capture_envelopes(|| { - sentry::start_session(); + let _session = sentry::start_session(); let err = "NaN".parse::().unwrap_err(); sentry::capture_error(&err); @@ -252,8 +249,6 @@ mod tests { }, ); }); - - sentry::end_session(); }); assert_eq!(envelopes.len(), 4); // 3 errors and one session end @@ -276,7 +271,7 @@ mod tests { sentry::with_scope( |_| {}, || { - sentry::start_session(); + let _session = sentry::start_session(); let err = "NaN".parse::().unwrap_err(); sentry::capture_error(&err); @@ -291,7 +286,7 @@ mod tests { sentry::capture_error(&err); }); - assert_eq!(envelopes.len(), 3); // 3 errors, but no session end + assert_eq!(envelopes.len(), 4); // 3 errors and one session end let body = to_str(&envelopes[0]); assert!(body.contains(r#"{"type":"session","#)); @@ -299,10 +294,15 @@ mod tests { assert!(body.contains(r#""status":"ok","errors":1"#)); assert!(body.contains(r#""init":true"#)); - // the other two events should not have session updates let body = to_str(&envelopes[1]); - assert!(!body.contains(r#"{"type":"session","#)); + assert!(body.starts_with("{}\n{\"type\":\"session\",")); + assert!(body.contains(r#""status":"exited","errors":1"#)); + assert!(!body.contains(r#""init":true"#)); + + // the other two events should not have session updates let body = to_str(&envelopes[2]); assert!(!body.contains(r#"{"type":"session","#)); + let body = to_str(&envelopes[3]); + assert!(!body.contains(r#"{"type":"session","#)); } } diff --git a/sentry/examples/health.rs b/sentry/examples/health.rs index bef3a9a9b..e65a79309 100644 --- a/sentry/examples/health.rs +++ b/sentry/examples/health.rs @@ -1,19 +1,22 @@ fn main() { let _sentry = sentry::init(sentry::ClientOptions { - // release health requires a session to be set + // release health requires a release to be set release: sentry::release_name!(), debug: true, + // session tracking is enabled by default, but we want to explicitly + // create the session + auto_session_tracking: false, ..Default::default() }); let handle = std::thread::spawn(|| { // this session will be set to crashed - sentry::start_session(); + let _session = sentry::start_session(); std::thread::sleep(std::time::Duration::from_secs(3)); panic!("oh no!"); }); - sentry::start_session(); + let session = sentry::start_session(); sentry::capture_message( "anything with a level >= Error will increase the error count", @@ -26,9 +29,9 @@ fn main() { std::thread::sleep(std::time::Duration::from_secs(2)); - // so this session will increase the errors count by 2, but otherwise has + // this session will have an error count of 2, but otherwise have // a clean exit. - sentry::end_session(); + drop(session); handle.join().ok(); } diff --git a/sentry/src/init.rs b/sentry/src/init.rs index 40ef93dce..3d271041e 100644 --- a/sentry/src/init.rs +++ b/sentry/src/init.rs @@ -2,14 +2,15 @@ use std::sync::Arc; use sentry_core::sentry_debug; -use crate::{defaults::apply_defaults, Client, ClientOptions, Hub}; +use crate::defaults::apply_defaults; +use crate::{Client, ClientOptions, Hub, SessionGuard}; /// Helper struct that is returned from `init`. /// /// When this is dropped events are drained with a 1 second timeout. #[must_use = "when the init guard is dropped the transport will be shut down and no further \ events can be sent. If you do want to ignore this use mem::forget on it."] -pub struct ClientInitGuard(Arc); +pub struct ClientInitGuard(Arc, Option); impl std::ops::Deref for ClientInitGuard { type Target = Client; @@ -32,6 +33,8 @@ impl Drop for ClientInitGuard { } else { sentry_debug!("dropping client guard (no client to dispose)"); } + // end any session that might be open before closing the client + drop(self.1.take()); self.0.close(None); } } @@ -88,6 +91,7 @@ where C: Into, { let opts = apply_defaults(opts.into()); + let auto_session_tracking = opts.auto_session_tracking; let client = Arc::new(Client::from(opts)); Hub::with(|hub| hub.bind_client(Some(client.clone()))); @@ -96,5 +100,10 @@ where } else { sentry_debug!("initialized disabled sentry client due to disabled or invalid DSN"); } - ClientInitGuard(client) + let session = if auto_session_tracking { + Some(crate::start_session()) + } else { + None + }; + ClientInitGuard(client, session) } From 2e3f46dc0d9d251ba07b309b2f11bf2e6f689fcf Mon Sep 17 00:00:00 2001 From: Arpad Borsos Date: Tue, 1 Sep 2020 10:24:06 +0200 Subject: [PATCH 06/10] do not send any more updates for already terminated sessions --- sentry-core/src/scope/real.rs | 11 ++++++----- sentry-core/src/session.rs | 12 ++++++++++-- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/sentry-core/src/scope/real.rs b/sentry-core/src/scope/real.rs index b20ebfcf6..ae248090e 100644 --- a/sentry-core/src/scope/real.rs +++ b/sentry-core/src/scope/real.rs @@ -137,11 +137,12 @@ impl fmt::Debug for SessionGuard { impl Drop for SessionGuard { fn drop(&mut self) { if let Some((client, scope)) = self.0.take() { - if let Some(mut session) = scope.session.lock().unwrap().take() { - session.close(); - let mut envelope = Envelope::new(); - envelope.add(session.into()); - client.capture_envelope(envelope); + if let Some(session) = scope.session.lock().unwrap().take() { + if let SessionUpdate::NeedsFlushing(session) = session.close() { + let mut envelope = Envelope::new(); + envelope.add(session.into()); + client.capture_envelope(envelope); + } } } } diff --git a/sentry-core/src/session.rs b/sentry-core/src/session.rs index cbfb9d688..5d5257229 100644 --- a/sentry-core/src/session.rs +++ b/sentry-core/src/session.rs @@ -59,6 +59,11 @@ impl Session { } pub(crate) fn update_from_event(&mut self, event: &Event<'static>) -> SessionUpdate { + if self.status != SessionStatus::Ok { + // a session that has already transitioned to a "terminal" state + // should not receive any more updates + return SessionUpdate::Unchanged; + } let mut has_error = event.level >= Level::Error; let mut is_crash = false; for exc in &event.exception.values { @@ -89,10 +94,13 @@ impl Session { } } - pub(crate) fn close(&mut self) { - self.duration = Some(self.started.elapsed().as_secs_f64()); + pub(crate) fn close(mut self) -> SessionUpdate { if self.status == SessionStatus::Ok { + self.duration = Some(self.started.elapsed().as_secs_f64()); self.status = SessionStatus::Exited; + SessionUpdate::NeedsFlushing(self) + } else { + SessionUpdate::Unchanged } } } From 0259d63773972169d16df4a82a2441bf9e0c8a3c Mon Sep 17 00:00:00 2001 From: Arpad Borsos Date: Wed, 2 Sep 2020 12:38:02 +0200 Subject: [PATCH 07/10] review --- sentry-core/src/client.rs | 2 +- sentry-core/src/envelope.rs | 7 +++++-- sentry-core/src/scope/real.rs | 2 +- sentry-core/src/session.rs | 8 ++++---- 4 files changed, 11 insertions(+), 8 deletions(-) diff --git a/sentry-core/src/client.rs b/sentry-core/src/client.rs index 8cbaf4663..4bcd74291 100644 --- a/sentry-core/src/client.rs +++ b/sentry-core/src/client.rs @@ -251,7 +251,7 @@ impl Client { }); let mut envelope: Envelope = event.into(); if let Some(session) = session { - envelope.add(session.into()); + envelope.add(session); } transport.send_envelope(envelope); return event_id; diff --git a/sentry-core/src/envelope.rs b/sentry-core/src/envelope.rs index 350feba32..307f85b76 100644 --- a/sentry-core/src/envelope.rs +++ b/sentry-core/src/envelope.rs @@ -46,8 +46,11 @@ impl Envelope { Default::default() } - pub(crate) fn add(&mut self, item: EnvelopeItem) { - self.items.push(item); + pub(crate) fn add(&mut self, item: I) + where + I: Into, + { + self.items.push(item.into()); } /// Returns the Envelopes Uuid, if any. diff --git a/sentry-core/src/scope/real.rs b/sentry-core/src/scope/real.rs index ae248090e..1922a23f2 100644 --- a/sentry-core/src/scope/real.rs +++ b/sentry-core/src/scope/real.rs @@ -140,7 +140,7 @@ impl Drop for SessionGuard { if let Some(session) = scope.session.lock().unwrap().take() { if let SessionUpdate::NeedsFlushing(session) = session.close() { let mut envelope = Envelope::new(); - envelope.add(session.into()); + envelope.add(session); client.capture_envelope(envelope); } } diff --git a/sentry-core/src/session.rs b/sentry-core/src/session.rs index 5d5257229..2fc297e99 100644 --- a/sentry-core/src/session.rs +++ b/sentry-core/src/session.rs @@ -4,7 +4,7 @@ use std::borrow::Cow; use std::sync::Arc; -use std::time::Instant; +use std::time::{Duration, Instant}; use crate::protocol::{Event, Level, User}; use crate::scope::StackLayer; @@ -35,7 +35,7 @@ pub struct Session { environment: Option>, started: Instant, started_utc: DateTime, - duration: Option, + duration: Option, init: bool, dirty: bool, } @@ -96,7 +96,7 @@ impl Session { pub(crate) fn close(mut self) -> SessionUpdate { if self.status == SessionStatus::Ok { - self.duration = Some(self.started.elapsed().as_secs_f64()); + self.duration = Some(self.started.elapsed()); self.status = SessionStatus::Exited; SessionUpdate::NeedsFlushing(self) } else { @@ -146,7 +146,7 @@ impl serde::Serialize for Session { session.serialize_field("started", &self.started_utc)?; if let Some(duration) = self.duration { - session.serialize_field("duration", &duration)?; + session.serialize_field("duration", &duration.as_secs_f64())?; } else { session.skip_field("duration")?; } From 3e18ce84e10c2c3e62848052349977bd27d75392 Mon Sep 17 00:00:00 2001 From: Arpad Borsos Date: Fri, 4 Sep 2020 11:41:29 +0200 Subject: [PATCH 08/10] increment session errors count for sampled events --- sentry-core/src/client.rs | 42 +++++++++++++----------- sentry-core/src/envelope.rs | 6 ---- sentry-core/src/scope/real.rs | 15 ++++----- sentry-core/src/session.rs | 60 +++++++++++++++++++++++------------ 4 files changed, 70 insertions(+), 53 deletions(-) diff --git a/sentry-core/src/client.rs b/sentry-core/src/client.rs index 4bcd74291..9e895d524 100644 --- a/sentry-core/src/client.rs +++ b/sentry-core/src/client.rs @@ -10,7 +10,6 @@ use rand::random; use crate::constants::SDK_INFO; use crate::protocol::{ClientSdkInfo, Event}; -use crate::session::SessionUpdate; use crate::types::{Dsn, Uuid}; use crate::{ClientOptions, Envelope, Hub, Integration, Scope, Transport}; @@ -143,6 +142,14 @@ impl Client { mut event: Event<'static>, scope: Option<&Scope>, ) -> Option> { + if let Some(scope) = scope { + scope.update_session_from_event(&event); + } + + if !self.sample_should_send() { + return None; + } + // event_id and sdk_info are set before the processors run so that the // processors can poke around in that data. if event.event_id.is_nil() { @@ -237,25 +244,22 @@ impl Client { /// Captures an event and sends it to sentry. pub fn capture_event(&self, event: Event<'static>, scope: Option<&Scope>) -> Uuid { if let Some(ref transport) = *self.transport.read().unwrap() { - if self.sample_should_send() { - if let Some(event) = self.prepare_event(event, scope) { - let event_id = event.event_id; - let session = scope.and_then(|scope| { - if let SessionUpdate::NeedsFlushing(session) = - scope.update_session_from_event(&event) - { - Some(session) - } else { - None - } - }); - let mut envelope: Envelope = event.into(); - if let Some(session) = session { - envelope.add(session); - } - transport.send_envelope(envelope); - return event_id; + if let Some(event) = self.prepare_event(event, scope) { + let event_id = event.event_id; + let mut envelope: Envelope = event.into(); + let session_item = scope.and_then(|scope| { + scope + .session + .lock() + .unwrap() + .as_mut() + .and_then(|session| session.to_envelope_item()) + }); + if let Some(session_item) = session_item { + envelope.add(session_item); } + transport.send_envelope(envelope); + return event_id; } } Default::default() diff --git a/sentry-core/src/envelope.rs b/sentry-core/src/envelope.rs index 307f85b76..5d74fd000 100644 --- a/sentry-core/src/envelope.rs +++ b/sentry-core/src/envelope.rs @@ -20,12 +20,6 @@ impl From> for EnvelopeItem { } } -impl From for EnvelopeItem { - fn from(session: Session) -> Self { - EnvelopeItem::Session(session) - } -} - /// A Sentry Envelope. /// /// An Envelope is the data format that Sentry uses for Ingestion. It can contain diff --git a/sentry-core/src/scope/real.rs b/sentry-core/src/scope/real.rs index 1922a23f2..1e55d325a 100644 --- a/sentry-core/src/scope/real.rs +++ b/sentry-core/src/scope/real.rs @@ -3,7 +3,7 @@ use std::fmt; use std::sync::{Arc, Mutex, PoisonError, RwLock}; use crate::protocol::{Breadcrumb, Context, Event, Level, User, Value}; -use crate::session::{Session, SessionUpdate}; +use crate::session::Session; use crate::{Client, Envelope}; #[derive(Debug)] @@ -137,10 +137,11 @@ impl fmt::Debug for SessionGuard { impl Drop for SessionGuard { fn drop(&mut self) { if let Some((client, scope)) = self.0.take() { - if let Some(session) = scope.session.lock().unwrap().take() { - if let SessionUpdate::NeedsFlushing(session) = session.close() { + if let Some(mut session) = scope.session.lock().unwrap().take() { + session.close(); + if let Some(item) = session.to_envelope_item() { let mut envelope = Envelope::new(); - envelope.add(session); + envelope.add(item); client.capture_envelope(envelope); } } @@ -292,11 +293,9 @@ impl Scope { Some(event) } - pub(crate) fn update_session_from_event(&self, event: &Event<'static>) -> SessionUpdate { + pub(crate) fn update_session_from_event(&self, event: &Event<'static>) { if let Some(session) = self.session.lock().unwrap().as_mut() { - session.update_from_event(event) - } else { - SessionUpdate::Unchanged + session.update_from_event(event); } } } diff --git a/sentry-core/src/session.rs b/sentry-core/src/session.rs index 2fc297e99..19f317f80 100644 --- a/sentry-core/src/session.rs +++ b/sentry-core/src/session.rs @@ -6,6 +6,7 @@ use std::borrow::Cow; use std::sync::Arc; use std::time::{Duration, Instant}; +use crate::envelope::EnvelopeItem; use crate::protocol::{Event, Level, User}; use crate::scope::StackLayer; use crate::types::{DateTime, Utc, Uuid}; @@ -20,11 +21,6 @@ pub enum SessionStatus { Exited, } -pub enum SessionUpdate { - NeedsFlushing(Session), - Unchanged, -} - #[derive(Clone, Debug, PartialEq)] pub struct Session { session_id: Uuid, @@ -58,11 +54,11 @@ impl Session { }) } - pub(crate) fn update_from_event(&mut self, event: &Event<'static>) -> SessionUpdate { + pub(crate) fn update_from_event(&mut self, event: &Event<'static>) { if self.status != SessionStatus::Ok { // a session that has already transitioned to a "terminal" state // should not receive any more updates - return SessionUpdate::Unchanged; + return; } let mut has_error = event.level >= Level::Error; let mut is_crash = false; @@ -83,26 +79,25 @@ impl Session { self.errors += 1; self.dirty = true; } - - if self.dirty { - self.dirty = false; - let session = self.clone(); - self.init = false; - SessionUpdate::NeedsFlushing(session) - } else { - SessionUpdate::Unchanged - } } - pub(crate) fn close(mut self) -> SessionUpdate { + pub(crate) fn close(&mut self) { if self.status == SessionStatus::Ok { self.duration = Some(self.started.elapsed()); self.status = SessionStatus::Exited; - SessionUpdate::NeedsFlushing(self) - } else { - SessionUpdate::Unchanged + self.dirty = true; } } + + pub(crate) fn to_envelope_item(&mut self) -> Option { + if self.dirty { + let item = EnvelopeItem::Session(self.clone()); + self.init = false; + self.dirty = false; + return Some(item); + } + None + } } #[derive(serde::Serialize)] @@ -231,6 +226,31 @@ mod tests { assert!(!body.contains(r#""init":true"#)); } + #[test] + fn test_session_sampled_errors() { + let mut envelopes = crate::test::with_captured_envelopes_options( + || { + let _session = sentry::start_session(); + + for _ in 0..100 { + let err = "NaN".parse::().unwrap_err(); + sentry::capture_error(&err); + } + }, + crate::ClientOptions { + release: Some("some-release".into()), + sample_rate: 0.5, + ..Default::default() + }, + ); + assert!(envelopes.len() > 25); + assert!(envelopes.len() < 75); + + let body = to_str(&envelopes.pop().unwrap()); + assert!(body.contains(r#"{"type":"session","#)); + assert!(body.contains(r#""status":"exited","errors":100"#)); + } + /// For _user-mode_ sessions, we want to inherit the session for any _new_ /// Hub that is spawned from the main thread Hub which already has a session /// attached From 7a61c362156db807bfa5d66f06adfcb19a3519a9 Mon Sep 17 00:00:00 2001 From: Arpad Borsos Date: Fri, 4 Sep 2020 12:03:39 +0200 Subject: [PATCH 09/10] give Session a Drop impl in favor of an explicit session guard, and add back the end_session fn --- sentry-core/src/api.rs | 19 +++++++++++-------- sentry-core/src/client.rs | 2 +- sentry-core/src/hub.rs | 27 ++++++++++++++++++++++++--- sentry-core/src/lib.rs | 2 +- sentry-core/src/scope/noop.rs | 13 ------------- sentry-core/src/scope/real.rs | 33 +-------------------------------- sentry-core/src/session.rs | 34 ++++++++++++++++++++++++++-------- sentry/examples/health.rs | 6 +++--- sentry/src/init.rs | 16 +++++++--------- 9 files changed, 74 insertions(+), 78 deletions(-) diff --git a/sentry-core/src/api.rs b/sentry-core/src/api.rs index c9c273fa4..f191a2416 100644 --- a/sentry-core/src/api.rs +++ b/sentry-core/src/api.rs @@ -1,6 +1,6 @@ use crate::protocol::{Event, Level}; use crate::types::Uuid; -use crate::{Hub, Integration, IntoBreadcrumbs, Scope, SessionGuard}; +use crate::{Hub, Integration, IntoBreadcrumbs, Scope}; /// Captures an event on the currently active client if any. /// @@ -264,18 +264,21 @@ pub fn last_event_id() -> Option { /// Start a new session for Release Health. /// -/// Returns a guard that ends the session on drop and thus determines the -/// lifetime of the session. -/// /// # Examples /// /// ``` -/// let session = sentry::start_session(); +/// sentry::start_session(); /// -/// // capturing any event / error here will update the sessions `errors` count. +/// // capturing any event / error here will update the sessions `errors` count, +/// // up until we call `sentry::end_session`. /// -/// drop(session); +/// sentry::end_session(); /// ``` -pub fn start_session() -> SessionGuard { +pub fn start_session() { Hub::with_active(|hub| hub.start_session()) } + +/// End the current Release Health Session. +pub fn end_session() { + Hub::with_active(|hub| hub.end_session()) +} diff --git a/sentry-core/src/client.rs b/sentry-core/src/client.rs index 9e895d524..e1dc75ce6 100644 --- a/sentry-core/src/client.rs +++ b/sentry-core/src/client.rs @@ -253,7 +253,7 @@ impl Client { .lock() .unwrap() .as_mut() - .and_then(|session| session.to_envelope_item()) + .and_then(|session| session.into_envelope_item()) }); if let Some(session_item) = session_item { envelope.add(session_item); diff --git a/sentry-core/src/hub.rs b/sentry-core/src/hub.rs index 26450e2a8..62cfd1152 100644 --- a/sentry-core/src/hub.rs +++ b/sentry-core/src/hub.rs @@ -11,7 +11,7 @@ use std::time::Duration; use crate::protocol::{Breadcrumb, Event, Level}; use crate::session::{Session, SessionStatus}; use crate::types::Uuid; -use crate::{event_from_error, Integration, IntoBreadcrumbs, Scope, ScopeGuard, SessionGuard}; +use crate::{event_from_error, Integration, IntoBreadcrumbs, Scope, ScopeGuard}; #[cfg(feature = "client")] use crate::{scope::Stack, Client, Envelope}; @@ -316,7 +316,7 @@ impl Hub { /// /// See the global [`start_session`](fn.start_session.html) /// for more documentation. - pub fn start_session(&self) -> SessionGuard { + pub fn start_session(&self) { with_client_impl! {{ self.inner.with_mut(|stack| { let top = stack.top_mut(); @@ -326,7 +326,28 @@ impl Hub { let mut scope = Arc::make_mut(&mut top.scope); scope.session = Arc::new(Mutex::new(Some(session))); } - SessionGuard(top.client.clone().map(|client| (client, top.scope.clone()))) + }) + }} + } + + /// End the current Release Health Session. + /// + /// See the global [`end_session`](fn.end_session.html) + /// for more documentation. + pub fn end_session(&self) { + with_client_impl! {{ + self.inner.with_mut(|stack| { + let top = stack.top_mut(); + if let Some(mut session) = top.scope.session.lock().unwrap().take() { + session.close(); + if let Some(item) = session.into_envelope_item() { + let mut envelope = Envelope::new(); + envelope.add(item); + if let Some(ref client) = top.client { + client.capture_envelope(envelope); + } + } + } }) }} } diff --git a/sentry-core/src/lib.rs b/sentry-core/src/lib.rs index 215ca57ab..67060b962 100644 --- a/sentry-core/src/lib.rs +++ b/sentry-core/src/lib.rs @@ -77,7 +77,7 @@ pub use crate::futures::{SentryFuture, SentryFutureExt}; pub use crate::hub::Hub; pub use crate::integration::Integration; pub use crate::intodsn::IntoDsn; -pub use crate::scope::{Scope, ScopeGuard, SessionGuard}; +pub use crate::scope::{Scope, ScopeGuard}; pub use crate::transport::{Transport, TransportFactory}; // deprecated exports diff --git a/sentry-core/src/scope/noop.rs b/sentry-core/src/scope/noop.rs index 8200c43e9..82c426a30 100644 --- a/sentry-core/src/scope/noop.rs +++ b/sentry-core/src/scope/noop.rs @@ -2,19 +2,6 @@ use std::fmt; use crate::protocol::{Context, Event, Level, User, Value}; -/// A minimal API session guard. -/// -/// Doesn't do anything but can be debug formatted. -#[derive(Default)] -#[must_use = "The duration of the Session from start to end is defined by the lifetime of this guard."] -pub struct SessionGuard; - -impl fmt::Debug for SessionGuard { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "SessionGuard") - } -} - /// A minimal API scope guard. /// /// Doesn't do anything but can be debug formatted. diff --git a/sentry-core/src/scope/real.rs b/sentry-core/src/scope/real.rs index 1e55d325a..d322cfc11 100644 --- a/sentry-core/src/scope/real.rs +++ b/sentry-core/src/scope/real.rs @@ -4,7 +4,7 @@ use std::sync::{Arc, Mutex, PoisonError, RwLock}; use crate::protocol::{Breadcrumb, Context, Event, Level, User, Value}; use crate::session::Session; -use crate::{Client, Envelope}; +use crate::Client; #[derive(Debug)] pub struct Stack { @@ -118,37 +118,6 @@ impl Stack { } } -/// A session guard. -/// -/// This is returned from [`Hub::start_session`] and will automatically end the -/// newly created session on drop. -/// -/// [`Hub::start_session`]: struct.Hub.html#method.start_session -#[derive(Default)] -#[must_use = "The duration of the Session from start to end is defined by the lifetime of this guard."] -pub struct SessionGuard(pub(crate) Option<(Arc, Arc)>); - -impl fmt::Debug for SessionGuard { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "SessionGuard") - } -} - -impl Drop for SessionGuard { - fn drop(&mut self) { - if let Some((client, scope)) = self.0.take() { - if let Some(mut session) = scope.session.lock().unwrap().take() { - session.close(); - if let Some(item) = session.to_envelope_item() { - let mut envelope = Envelope::new(); - envelope.add(item); - client.capture_envelope(envelope); - } - } - } - } -} - /// A scope guard. /// /// This is returned from [`Hub::push_scope`] and will automatically pop the diff --git a/sentry-core/src/session.rs b/sentry-core/src/session.rs index 19f317f80..7f626815e 100644 --- a/sentry-core/src/session.rs +++ b/sentry-core/src/session.rs @@ -10,6 +10,7 @@ use crate::envelope::EnvelopeItem; use crate::protocol::{Event, Level, User}; use crate::scope::StackLayer; use crate::types::{DateTime, Utc, Uuid}; +use crate::{Client, Envelope}; /// Represents the status of a session. #[derive(Copy, Clone, Debug, PartialEq)] @@ -21,8 +22,12 @@ pub enum SessionStatus { Exited, } -#[derive(Clone, Debug, PartialEq)] +// TODO: make this a true POD type and move it to `sentry-types`, +// and split out the client, user, and dirty flag into a separate guard struct +// that lives on the scope. +#[derive(Clone, Debug)] pub struct Session { + client: Arc, session_id: Uuid, status: SessionStatus, errors: usize, @@ -36,10 +41,23 @@ pub struct Session { dirty: bool, } +impl Drop for Session { + fn drop(&mut self) { + self.close(); + if let Some(item) = self.into_envelope_item() { + let mut envelope = Envelope::new(); + envelope.add(item); + self.client.capture_envelope(envelope); + } + } +} + impl Session { pub fn from_stack(stack: &StackLayer) -> Option { - let options = stack.client.as_ref()?.options(); + let client = stack.client.as_ref()?; + let options = client.options(); Some(Self { + client: client.clone(), session_id: Uuid::new_v4(), status: SessionStatus::Ok, errors: 0, @@ -89,7 +107,7 @@ impl Session { } } - pub(crate) fn to_envelope_item(&mut self) -> Option { + pub(crate) fn into_envelope_item(&mut self) -> Option { if self.dirty { let item = EnvelopeItem::Session(self.clone()); self.init = false; @@ -192,7 +210,7 @@ mod tests { #[test] fn test_session_startstop() { let envelopes = capture_envelopes(|| { - let _session = sentry::start_session(); + sentry::start_session(); std::thread::sleep(std::time::Duration::from_millis(10)); }); assert_eq!(envelopes.len(), 1); @@ -207,7 +225,7 @@ mod tests { #[test] fn test_session_error() { let envelopes = capture_envelopes(|| { - let _session = sentry::start_session(); + sentry::start_session(); let err = "NaN".parse::().unwrap_err(); sentry::capture_error(&err); @@ -230,7 +248,7 @@ mod tests { fn test_session_sampled_errors() { let mut envelopes = crate::test::with_captured_envelopes_options( || { - let _session = sentry::start_session(); + sentry::start_session(); for _ in 0..100 { let err = "NaN".parse::().unwrap_err(); @@ -257,7 +275,7 @@ mod tests { #[test] fn test_inherit_session_from_top() { let envelopes = capture_envelopes(|| { - let _session = sentry::start_session(); + sentry::start_session(); let err = "NaN".parse::().unwrap_err(); sentry::capture_error(&err); @@ -299,7 +317,7 @@ mod tests { sentry::with_scope( |_| {}, || { - let _session = sentry::start_session(); + sentry::start_session(); let err = "NaN".parse::().unwrap_err(); sentry::capture_error(&err); diff --git a/sentry/examples/health.rs b/sentry/examples/health.rs index e65a79309..a88a79f45 100644 --- a/sentry/examples/health.rs +++ b/sentry/examples/health.rs @@ -11,12 +11,12 @@ fn main() { let handle = std::thread::spawn(|| { // this session will be set to crashed - let _session = sentry::start_session(); + sentry::start_session(); std::thread::sleep(std::time::Duration::from_secs(3)); panic!("oh no!"); }); - let session = sentry::start_session(); + sentry::start_session(); sentry::capture_message( "anything with a level >= Error will increase the error count", @@ -31,7 +31,7 @@ fn main() { // this session will have an error count of 2, but otherwise have // a clean exit. - drop(session); + sentry::end_session(); handle.join().ok(); } diff --git a/sentry/src/init.rs b/sentry/src/init.rs index 3d271041e..8925a34b1 100644 --- a/sentry/src/init.rs +++ b/sentry/src/init.rs @@ -3,14 +3,14 @@ use std::sync::Arc; use sentry_core::sentry_debug; use crate::defaults::apply_defaults; -use crate::{Client, ClientOptions, Hub, SessionGuard}; +use crate::{Client, ClientOptions, Hub}; /// Helper struct that is returned from `init`. /// /// When this is dropped events are drained with a 1 second timeout. #[must_use = "when the init guard is dropped the transport will be shut down and no further \ events can be sent. If you do want to ignore this use mem::forget on it."] -pub struct ClientInitGuard(Arc, Option); +pub struct ClientInitGuard(Arc); impl std::ops::Deref for ClientInitGuard { type Target = Client; @@ -34,7 +34,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 - drop(self.1.take()); + crate::end_session(); self.0.close(None); } } @@ -100,10 +100,8 @@ where } else { sentry_debug!("initialized disabled sentry client due to disabled or invalid DSN"); } - let session = if auto_session_tracking { - Some(crate::start_session()) - } else { - None - }; - ClientInitGuard(client, session) + if auto_session_tracking { + crate::start_session() + } + ClientInitGuard(client) } From 21fa45d2d89220bbb12d22776b463f5b505d16f1 Mon Sep 17 00:00:00 2001 From: Arpad Borsos Date: Fri, 4 Sep 2020 14:53:16 +0200 Subject: [PATCH 10/10] note sessions are still experimental and disable by default --- sentry-core/src/api.rs | 3 +++ sentry-core/src/client.rs | 2 +- sentry-core/src/clientoptions.rs | 8 ++++---- sentry-core/src/hub.rs | 2 +- sentry-core/src/session.rs | 4 ++-- 5 files changed, 11 insertions(+), 8 deletions(-) diff --git a/sentry-core/src/api.rs b/sentry-core/src/api.rs index f191a2416..af05cf2f7 100644 --- a/sentry-core/src/api.rs +++ b/sentry-core/src/api.rs @@ -264,6 +264,9 @@ pub fn last_event_id() -> Option { /// Start a new session for Release Health. /// +/// This is still **experimental** for the moment and is not recommended to be +/// used with a very high volume of sessions (_request-mode_ sessions). +/// /// # Examples /// /// ``` diff --git a/sentry-core/src/client.rs b/sentry-core/src/client.rs index e1dc75ce6..8d2b3878c 100644 --- a/sentry-core/src/client.rs +++ b/sentry-core/src/client.rs @@ -253,7 +253,7 @@ impl Client { .lock() .unwrap() .as_mut() - .and_then(|session| session.into_envelope_item()) + .and_then(|session| session.create_envelope_item()) }); if let Some(session_item) = session_item { envelope.add(session_item); diff --git a/sentry-core/src/clientoptions.rs b/sentry-core/src/clientoptions.rs index 0f6c43ab9..c53e3ee0c 100644 --- a/sentry-core/src/clientoptions.rs +++ b/sentry-core/src/clientoptions.rs @@ -84,9 +84,9 @@ pub struct ClientOptions { // Other options not documented in Unified API /// Enable Release Health Session tracking. /// - /// When automatic session tracking is enabled, as is the default, a new - /// "user-mode" session is started at the time of `sentry::init`, and will - /// persist for the application lifetime. + /// 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. pub auto_session_tracking: bool, /// Border frames which indicate a border from a backtrace to /// useless internals. Some are automatically included. @@ -183,7 +183,7 @@ impl Default for ClientOptions { http_proxy: None, https_proxy: None, shutdown_timeout: Duration::from_secs(2), - auto_session_tracking: true, + auto_session_tracking: false, extra_border_frames: vec![], trim_backtraces: true, user_agent: Cow::Borrowed(&USER_AGENT), diff --git a/sentry-core/src/hub.rs b/sentry-core/src/hub.rs index 62cfd1152..9317e8e2a 100644 --- a/sentry-core/src/hub.rs +++ b/sentry-core/src/hub.rs @@ -340,7 +340,7 @@ impl Hub { let top = stack.top_mut(); if let Some(mut session) = top.scope.session.lock().unwrap().take() { session.close(); - if let Some(item) = session.into_envelope_item() { + if let Some(item) = session.create_envelope_item() { let mut envelope = Envelope::new(); envelope.add(item); if let Some(ref client) = top.client { diff --git a/sentry-core/src/session.rs b/sentry-core/src/session.rs index 7f626815e..849909e63 100644 --- a/sentry-core/src/session.rs +++ b/sentry-core/src/session.rs @@ -44,7 +44,7 @@ pub struct Session { impl Drop for Session { fn drop(&mut self) { self.close(); - if let Some(item) = self.into_envelope_item() { + if let Some(item) = self.create_envelope_item() { let mut envelope = Envelope::new(); envelope.add(item); self.client.capture_envelope(envelope); @@ -107,7 +107,7 @@ impl Session { } } - pub(crate) fn into_envelope_item(&mut self) -> Option { + pub(crate) fn create_envelope_item(&mut self) -> Option { if self.dirty { let item = EnvelopeItem::Session(self.clone()); self.init = false;