sentry_types/protocol/
session.rs

1use std::borrow::Cow;
2use std::fmt;
3use std::net::IpAddr;
4use std::str;
5use std::time::SystemTime;
6
7use serde::{Deserialize, Serialize};
8use thiserror::Error;
9use uuid::Uuid;
10
11use crate::utils::{ts_rfc3339, ts_rfc3339_opt};
12
13/// The Status of a Release Health Session.
14#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, Deserialize, Serialize)]
15#[serde(rename_all = "snake_case")]
16#[derive(Default)]
17pub enum SessionStatus {
18    /// The session is healthy.
19    ///
20    /// This does not necessarily indicate that the session is still active.
21    #[default]
22    Ok,
23    /// The session terminated normally.
24    Exited,
25    /// The session resulted in an application crash.
26    Crashed,
27    /// The session had an unexpected abrupt termination (not crashing).
28    Abnormal,
29}
30
31/// An error used when parsing `SessionStatus`.
32#[derive(Debug, Error)]
33#[error("invalid session status")]
34pub struct ParseSessionStatusError;
35
36impl str::FromStr for SessionStatus {
37    type Err = ParseSessionStatusError;
38
39    fn from_str(string: &str) -> Result<Self, Self::Err> {
40        Ok(match string {
41            "ok" => SessionStatus::Ok,
42            "crashed" => SessionStatus::Crashed,
43            "abnormal" => SessionStatus::Abnormal,
44            "exited" => SessionStatus::Exited,
45            _ => return Err(ParseSessionStatusError),
46        })
47    }
48}
49
50impl fmt::Display for SessionStatus {
51    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
52        match *self {
53            SessionStatus::Ok => write!(f, "ok"),
54            SessionStatus::Crashed => write!(f, "crashed"),
55            SessionStatus::Abnormal => write!(f, "abnormal"),
56            SessionStatus::Exited => write!(f, "exited"),
57        }
58    }
59}
60
61/// Additional attributes for Sessions.
62#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
63pub struct SessionAttributes<'a> {
64    /// The release version string.
65    pub release: Cow<'a, str>,
66
67    /// The environment identifier.
68    #[serde(default, skip_serializing_if = "Option::is_none")]
69    pub environment: Option<Cow<'a, str>>,
70
71    /// The ip address of the user. This data is not persisted but used for filtering.
72    #[serde(default, skip_serializing_if = "Option::is_none")]
73    pub ip_address: Option<IpAddr>,
74
75    /// The user agent of the user. This data is not persisted but used for filtering.
76    #[serde(default, skip_serializing_if = "Option::is_none")]
77    pub user_agent: Option<String>,
78}
79
80fn is_false(val: &bool) -> bool {
81    !val
82}
83
84/// A Release Health Session.
85///
86/// Refer to the [Sessions](https://develop.sentry.dev/sdk/sessions/) documentation
87/// for more details.
88#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
89pub struct SessionUpdate<'a> {
90    /// The session identifier.
91    #[serde(rename = "sid", default = "crate::random_uuid")]
92    pub session_id: Uuid,
93
94    /// The distinct identifier. Should be device or user ID.
95    #[serde(rename = "did", default)]
96    pub distinct_id: Option<String>,
97
98    /// An optional logical clock.
99    #[serde(rename = "seq", default, skip_serializing_if = "Option::is_none")]
100    pub sequence: Option<u64>,
101
102    /// The timestamp of when the session change event was created.
103    #[serde(
104        default,
105        skip_serializing_if = "Option::is_none",
106        with = "ts_rfc3339_opt"
107    )]
108    pub timestamp: Option<SystemTime>,
109
110    /// The timestamp of when the session itself started.
111    #[serde(default = "SystemTime::now", with = "ts_rfc3339")]
112    pub started: SystemTime,
113
114    /// A flag that indicates that this is the initial transmission of the session.
115    #[serde(default, skip_serializing_if = "is_false")]
116    pub init: bool,
117
118    /// An optional duration of the session so far.
119    #[serde(default, skip_serializing_if = "Option::is_none")]
120    pub duration: Option<f64>,
121
122    /// The status of the session.
123    #[serde(default)]
124    pub status: SessionStatus,
125
126    /// The number of errors that occurred.
127    #[serde(default)]
128    pub errors: u64,
129
130    /// The session event attributes.
131    #[serde(rename = "attrs")]
132    pub attributes: SessionAttributes<'a>,
133}
134
135#[allow(clippy::trivially_copy_pass_by_ref)]
136fn is_zero(val: &u32) -> bool {
137    *val == 0
138}
139
140/// An aggregation grouped by `started` and `distinct_id`.
141#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
142pub struct SessionAggregateItem {
143    /// The timestamp of when the session itself started.
144    #[serde(with = "ts_rfc3339")]
145    pub started: SystemTime,
146    /// The distinct identifier.
147    #[serde(rename = "did", default, skip_serializing_if = "Option::is_none")]
148    pub distinct_id: Option<String>,
149    /// The number of exited sessions that occurred.
150    #[serde(default, skip_serializing_if = "is_zero")]
151    pub exited: u32,
152    /// The number of errored sessions that occurred, not including the abnormal and crashed ones.
153    #[serde(default, skip_serializing_if = "is_zero")]
154    pub errored: u32,
155    /// The number of abnormal sessions that occurred.
156    #[serde(default, skip_serializing_if = "is_zero")]
157    pub abnormal: u32,
158    /// The number of crashed sessions that occurred.
159    #[serde(default, skip_serializing_if = "is_zero")]
160    pub crashed: u32,
161}
162
163/// An Aggregation of Release Health Sessions
164///
165/// For *request-mode* sessions, sessions will be aggregated instead of being
166/// sent as individual updates.
167#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
168pub struct SessionAggregates<'a> {
169    /// A batch of sessions that were started.
170    #[serde(default)]
171    pub aggregates: Vec<SessionAggregateItem>,
172    /// The shared session event attributes.
173    #[serde(rename = "attrs")]
174    pub attributes: SessionAttributes<'a>,
175}