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 let hub = Hub::current();
192 if hub.is_active_and_usage_safe() {
193 hub.with_scope(scope_config, callback)
194 } else {
195 callback()
196 }
197 }
198 #[cfg(not(feature = "client"))]
199 {
200 let _scope_config = scope_config;
201 callback()
202 }
203}
204
205/// Looks up an integration on the current Hub.
206///
207/// Calls the given function with the requested integration instance when it
208/// is active on the currently active client. When multiple instances of the
209/// same integration are added, the function will be called with the first one.
210///
211/// # Examples
212///
213/// ```
214/// use sentry::{ClientOptions, Integration};
215///
216/// struct MyIntegration(usize);
217/// impl Integration for MyIntegration {}
218///
219/// let options = ClientOptions::default()
220/// .add_integration(MyIntegration(10))
221/// .add_integration(MyIntegration(20));
222/// # let _options = options.clone();
223///
224/// let _sentry = sentry::init(options);
225///
226/// # sentry::test::with_captured_events_options(|| {
227/// let value = sentry::with_integration(|integration: &MyIntegration, _| integration.0);
228/// assert_eq!(value, 10);
229/// # }, _options);
230/// ```
231pub fn with_integration<I, F, R>(f: F) -> R
232where
233 I: Integration,
234 F: FnOnce(&I, &Hub) -> R,
235 R: Default,
236{
237 Hub::with_active(|hub| hub.with_integration(|i| f(i, hub)))
238}
239
240/// Returns the last event ID captured.
241///
242/// This uses the current thread local [`Hub`], and will return `None` if no
243/// event has been captured yet on this [`Hub`].
244///
245/// # Examples
246///
247/// ```
248/// use sentry::protocol::Level;
249/// use sentry::types::Uuid;
250///
251/// # sentry::test::with_captured_events(|| {
252/// assert_eq!(sentry::last_event_id(), None);
253///
254/// sentry::capture_message("some message", Level::Info);
255///
256/// assert!(sentry::last_event_id().is_some());
257/// # });
258/// ```
259///
260/// [`Hub`]: struct.Hub.html
261pub fn last_event_id() -> Option<Uuid> {
262 with_client_impl! {{
263 Hub::with_current(|hub| hub.last_event_id())
264 }}
265}
266
267/// Start a new session for Release Health.
268///
269/// This is still **experimental** for the moment and is not recommended to be
270/// used with a very high volume of sessions (_request-mode_ sessions).
271///
272/// # Examples
273///
274/// ```
275/// sentry::start_session();
276///
277/// // capturing any event / error here will update the sessions `errors` count,
278/// // up until we call `sentry::end_session`.
279///
280/// sentry::end_session();
281/// ```
282#[cfg(feature = "release-health")]
283pub fn start_session() {
284 Hub::with_active(|hub| hub.start_session())
285}
286
287/// End the current Release Health Session.
288#[cfg(feature = "release-health")]
289pub fn end_session() {
290 end_session_with_status(SessionStatus::Exited)
291}
292
293/// End the current Release Health Session with the given [`SessionStatus`].
294///
295/// By default, the SDK will only consider the `Exited` and `Crashed` status
296/// based on the type of events that were captured during the session.
297///
298/// When an `Abnormal` session should be captured, it has to be done explicitly
299/// using this function.
300#[cfg(feature = "release-health")]
301pub fn end_session_with_status(status: SessionStatus) {
302 Hub::with_active(|hub| hub.end_session_with_status(status))
303}