sentry_core/api.rs
1#[cfg(feature = "release-health")]
2use sentry_types::protocol::v7::SessionStatus;
3
4use crate::protocol::{Event, Level};
5use crate::types::Uuid;
6use crate::{Hub, Integration, IntoBreadcrumbs, Scope};
7
8/// Captures an event on the currently active client if any.
9///
10/// The event must already be assembled. Typically code would instead use
11/// the utility methods like [`capture_message`], [`capture_error`], or an
12/// integration specific function.
13///
14/// The return value is the event ID. If the event was discarded for any reason,
15/// return value will be the nil UUID (`Uuid::nil`).
16///
17/// # Examples
18///
19/// ```
20/// use sentry::protocol::{Event, Level};
21/// use sentry::types::{Uuid, random_uuid};
22///
23/// let uuid = random_uuid();
24/// let event = Event {
25/// event_id: uuid,
26/// message: Some("Hello World!".into()),
27/// level: Level::Info,
28/// ..Default::default()
29/// };
30///
31/// assert_eq!(sentry::capture_event(event.clone()), Uuid::nil());
32///
33/// let events = sentry::test::with_captured_events(|| {
34/// assert_eq!(sentry::capture_event(event), uuid);
35/// });
36/// assert_eq!(events.len(), 1);
37/// ```
38///
39/// [`capture_message`]: fn.capture_message.html
40/// [`capture_error`]: fn.capture_error.html
41pub fn capture_event(event: Event<'static>) -> Uuid {
42 Hub::with_active(|hub| hub.capture_event(event))
43}
44
45/// Captures an arbitrary message.
46///
47/// This creates an event from the given message and sends it via
48/// [`capture_event`](fn.capture_event.html).
49///
50/// # Examples
51///
52/// ```
53/// use sentry::protocol::Level;
54///
55/// # let events = sentry::test::with_captured_events(|| {
56/// sentry::capture_message("some message", Level::Info);
57/// # });
58/// # let captured_event = events.into_iter().next().unwrap();
59///
60/// assert_eq!(captured_event.message.as_deref(), Some("some message"));
61/// ```
62pub fn capture_message(msg: &str, level: Level) -> Uuid {
63 Hub::with_active(|hub| hub.capture_message(msg, level))
64}
65
66/// Records a breadcrumb by calling a function.
67///
68/// The total number of breadcrumbs that can be recorded are limited by the
69/// configuration on the client. This function accepts any object that
70/// implements [`IntoBreadcrumbs`], which is implemented for a varienty of
71/// common types. For efficiency reasons you can also pass a closure returning
72/// a breadcrumb in which case the closure is only called if the client is
73/// enabled.
74///
75/// The most common implementations that can be passed:
76///
77/// * `Breadcrumb`: to record a breadcrumb
78/// * `Vec<Breadcrumb>`: to record more than one breadcrumb in one go.
79/// * `Option<Breadcrumb>`: to record a breadcrumb or not
80/// * additionally all of these can also be returned from an `FnOnce()`
81///
82/// # Examples
83///
84/// ```
85/// use sentry::protocol::{Breadcrumb, Level, Map};
86///
87/// let breadcrumb = Breadcrumb {
88/// ty: "http".into(),
89/// category: Some("request".into()),
90/// data: {
91/// let mut map = Map::new();
92/// map.insert("method".into(), "GET".into());
93/// map.insert("url".into(), "https://example.com/".into());
94/// map
95/// },
96/// ..Default::default()
97/// };
98///
99/// # let events = sentry::test::with_captured_events(|| {
100/// sentry::add_breadcrumb(breadcrumb.clone());
101///
102/// sentry::capture_message("some message", Level::Info);
103/// # });
104/// # let captured_event = events.into_iter().next().unwrap();
105///
106/// assert_eq!(captured_event.breadcrumbs.values, vec![breadcrumb]);
107/// ```
108///
109/// [`IntoBreadcrumbs`]: trait.IntoBreadcrumbs.html
110pub fn add_breadcrumb<B: IntoBreadcrumbs>(breadcrumb: B) {
111 Hub::with_active(|hub| hub.add_breadcrumb(breadcrumb))
112}
113
114/// Invokes a function that can modify the current scope.
115///
116/// The function is passed a mutable reference to the [`Scope`] so that modifications
117/// can be performed. Because there might currently not be a scope or client active
118/// it's possible that the callback might not be called at all. As a result of this
119/// the return value of this closure must have a default that is returned in such
120/// cases.
121///
122/// # Examples
123///
124/// ```
125/// use sentry::protocol::{Level, User};
126///
127/// let user = Some(User {
128/// username: Some("john_doe".into()),
129/// ..Default::default()
130/// });
131///
132/// # let events = sentry::test::with_captured_events(|| {
133/// sentry::configure_scope(|scope| {
134/// scope.set_user(user.clone());
135/// });
136///
137/// sentry::capture_message("some message", Level::Info);
138/// # });
139/// # let captured_event = events.into_iter().next().unwrap();
140///
141/// assert_eq!(captured_event.user, user);
142/// ```
143///
144/// # Panics
145///
146/// While the scope is being configured accessing scope related functionality is
147/// not permitted. In this case a wide range of panics will be raised. It's
148/// unsafe to call into `sentry::bind_client` or similar functions from within
149/// the callback as a result of this.
150///
151/// [`Scope`]: struct.Scope.html
152pub fn configure_scope<F, R>(f: F) -> R
153where
154 R: Default,
155 F: FnOnce(&mut Scope) -> R,
156{
157 Hub::with_active(|hub| hub.configure_scope(f))
158}
159
160/// Temporarily pushes a scope for a single call optionally reconfiguring it.
161///
162/// This function takes two arguments: the first is a callback that is passed
163/// a scope and can reconfigure it. The second is callback that then executes
164/// in the context of that scope.
165///
166/// This is useful when extra data should be send with a single capture call
167/// for instance a different level or tags:
168///
169/// # Examples
170///
171/// ```
172/// use sentry::protocol::Level;
173///
174/// # let events = sentry::test::with_captured_events(|| {
175/// sentry::with_scope(
176/// |scope| scope.set_level(Some(Level::Warning)),
177/// || sentry::capture_message("some message", Level::Info),
178/// );
179/// # });
180/// # let captured_event = events.into_iter().next().unwrap();
181///
182/// assert_eq!(captured_event.level, Level::Warning);
183/// ```
184pub fn with_scope<C, F, R>(scope_config: C, callback: F) -> R
185where
186 C: FnOnce(&mut Scope),
187 F: FnOnce() -> R,
188{
189 #[cfg(feature = "client")]
190 {
191 Hub::with(|hub| {
192 if hub.is_active_and_usage_safe() {
193 hub.with_scope(scope_config, callback)
194 } else {
195 callback()
196 }
197 })
198 }
199 #[cfg(not(feature = "client"))]
200 {
201 let _scope_config = scope_config;
202 callback()
203 }
204}
205
206/// Looks up an integration on the current Hub.
207///
208/// Calls the given function with the requested integration instance when it
209/// is active on the currently active client. When multiple instances of the
210/// same integration are added, the function will be called with the first one.
211///
212/// # Examples
213///
214/// ```
215/// use sentry::{ClientOptions, Integration};
216///
217/// struct MyIntegration(usize);
218/// impl Integration for MyIntegration {}
219///
220/// let options = ClientOptions::default()
221/// .add_integration(MyIntegration(10))
222/// .add_integration(MyIntegration(20));
223/// # let _options = options.clone();
224///
225/// let _sentry = sentry::init(options);
226///
227/// # sentry::test::with_captured_events_options(|| {
228/// let value = sentry::with_integration(|integration: &MyIntegration, _| integration.0);
229/// assert_eq!(value, 10);
230/// # }, _options);
231/// ```
232pub fn with_integration<I, F, R>(f: F) -> R
233where
234 I: Integration,
235 F: FnOnce(&I, &Hub) -> R,
236 R: Default,
237{
238 Hub::with_active(|hub| hub.with_integration(|i| f(i, hub)))
239}
240
241/// Returns the last event ID captured.
242///
243/// This uses the current thread local [`Hub`], and will return `None` if no
244/// event has been captured yet on this [`Hub`].
245///
246/// # Examples
247///
248/// ```
249/// use sentry::protocol::Level;
250/// use sentry::types::Uuid;
251///
252/// # sentry::test::with_captured_events(|| {
253/// assert_eq!(sentry::last_event_id(), None);
254///
255/// sentry::capture_message("some message", Level::Info);
256///
257/// assert!(sentry::last_event_id().is_some());
258/// # });
259/// ```
260///
261/// [`Hub`]: struct.Hub.html
262pub fn last_event_id() -> Option<Uuid> {
263 with_client_impl! {{
264 Hub::with(|hub| hub.last_event_id())
265 }}
266}
267
268/// Start a new session for Release Health.
269///
270/// This is still **experimental** for the moment and is not recommended to be
271/// used with a very high volume of sessions (_request-mode_ sessions).
272///
273/// # Examples
274///
275/// ```
276/// sentry::start_session();
277///
278/// // capturing any event / error here will update the sessions `errors` count,
279/// // up until we call `sentry::end_session`.
280///
281/// sentry::end_session();
282/// ```
283#[cfg(feature = "release-health")]
284pub fn start_session() {
285 Hub::with_active(|hub| hub.start_session())
286}
287
288/// End the current Release Health Session.
289#[cfg(feature = "release-health")]
290pub fn end_session() {
291 end_session_with_status(SessionStatus::Exited)
292}
293
294/// End the current Release Health Session with the given [`SessionStatus`].
295///
296/// By default, the SDK will only consider the `Exited` and `Crashed` status
297/// based on the type of events that were captured during the session.
298///
299/// When an `Abnormal` session should be captured, it has to be done explicitly
300/// using this function.
301#[cfg(feature = "release-health")]
302pub fn end_session_with_status(status: SessionStatus) {
303 Hub::with_active(|hub| hub.end_session_with_status(status))
304}