ckb_sentry_core/api.rs
1use sentry_types::protocol::v7::SessionStatus;
2
3use crate::protocol::{Event, Level};
4use crate::types::Uuid;
5use crate::{Hub, Integration, IntoBreadcrumbs, Scope};
6
7/// Captures an event on the currently active client if any.
8///
9/// The event must already be assembled. Typically code would instead use
10/// the utility methods like [`capture_message`], [`capture_error`], or an
11/// integration specific function.
12///
13/// The return value is the event ID. If the event was discarded for any reason,
14/// return value will be the nil UUID (`Uuid::nil`).
15///
16/// # Examples
17///
18/// ```
19/// use sentry::protocol::{Event, Level};
20/// use sentry::types::Uuid;
21///
22/// let uuid = Uuid::new_v4();
23/// let event = Event {
24/// event_id: uuid,
25/// message: Some("Hello World!".into()),
26/// level: Level::Info,
27/// ..Default::default()
28/// };
29///
30/// assert_eq!(sentry::capture_event(event.clone()), Uuid::nil());
31///
32/// let events = sentry::test::with_captured_events(|| {
33/// assert_eq!(sentry::capture_event(event), uuid);
34/// });
35/// assert_eq!(events.len(), 1);
36/// ```
37///
38/// [`capture_message`]: fn.capture_message.html
39/// [`capture_error`]: fn.capture_error.html
40pub fn capture_event(event: Event<'static>) -> Uuid {
41 Hub::with_active(|hub| hub.capture_event(event))
42}
43
44/// Captures an arbitrary message.
45///
46/// This creates an event from the given message and sends it via
47/// [`capture_event`](fn.capture_event.html).
48///
49/// # Examples
50///
51/// ```
52/// use sentry::protocol::Level;
53///
54/// # let events = sentry::test::with_captured_events(|| {
55/// sentry::capture_message("some message", Level::Info);
56/// # });
57/// # let captured_event = events.into_iter().next().unwrap();
58///
59/// assert_eq!(captured_event.message.as_deref(), Some("some message"));
60/// ```
61pub fn capture_message(msg: &str, level: Level) -> Uuid {
62 Hub::with_active(|hub| hub.capture_message(msg, level))
63}
64
65/// Records a breadcrumb by calling a function.
66///
67/// The total number of breadcrumbs that can be recorded are limited by the
68/// configuration on the client. This function accepts any object that
69/// implements [`IntoBreadcrumbs`], which is implemented for a varienty of
70/// common types. For efficiency reasons you can also pass a closure returning
71/// a breadcrumb in which case the closure is only called if the client is
72/// enabled.
73///
74/// The most common implementations that can be passed:
75///
76/// * `Breadcrumb`: to record a breadcrumb
77/// * `Vec<Breadcrumb>`: to record more than one breadcrumb in one go.
78/// * `Option<Breadcrumb>`: to record a breadcrumb or not
79/// * additionally all of these can also be returned from an `FnOnce()`
80///
81/// # Examples
82///
83/// ```
84/// use sentry::protocol::{Breadcrumb, Level, Map};
85///
86/// let breadcrumb = Breadcrumb {
87/// ty: "http".into(),
88/// category: Some("request".into()),
89/// data: {
90/// let mut map = Map::new();
91/// map.insert("method".into(), "GET".into());
92/// map.insert("url".into(), "https://example.com/".into());
93/// map
94/// },
95/// ..Default::default()
96/// };
97///
98/// # let events = sentry::test::with_captured_events(|| {
99/// sentry::add_breadcrumb(breadcrumb.clone());
100///
101/// sentry::capture_message("some message", Level::Info);
102/// # });
103/// # let captured_event = events.into_iter().next().unwrap();
104///
105/// assert_eq!(captured_event.breadcrumbs.values, vec![breadcrumb]);
106/// ```
107///
108/// [`IntoBreadcrumbs`]: trait.IntoBreadcrumbs.html
109pub fn add_breadcrumb<B: IntoBreadcrumbs>(breadcrumb: B) {
110 Hub::with_active(|hub| hub.add_breadcrumb(breadcrumb))
111}
112
113/// Invokes a function that can modify the current scope.
114///
115/// The function is passed a mutable reference to the [`Scope`] so that modifications
116/// can be performed. Because there might currently not be a scope or client active
117/// it's possible that the callback might not be called at all. As a result of this
118/// the return value of this closure must have a default that is returned in such
119/// cases.
120///
121/// # Examples
122///
123/// ```
124/// use sentry::protocol::{Level, User};
125///
126/// let user = Some(User {
127/// username: Some("john_doe".into()),
128/// ..Default::default()
129/// });
130///
131/// # let events = sentry::test::with_captured_events(|| {
132/// sentry::configure_scope(|scope| {
133/// scope.set_user(user.clone());
134/// });
135///
136/// sentry::capture_message("some message", Level::Info);
137/// # });
138/// # let captured_event = events.into_iter().next().unwrap();
139///
140/// assert_eq!(captured_event.user, user);
141/// ```
142///
143/// # Panics
144///
145/// While the scope is being configured accessing scope related functionality is
146/// not permitted. In this case a wide range of panics will be raised. It's
147/// unsafe to call into `sentry::bind_client` or similar functions from within
148/// the callback as a result of this.
149///
150/// [`Scope`]: struct.Scope.html
151pub fn configure_scope<F, R>(f: F) -> R
152where
153 R: Default,
154 F: FnOnce(&mut Scope) -> R,
155{
156 Hub::with_active(|hub| hub.configure_scope(f))
157}
158
159/// Temporarily pushes a scope for a single call optionally reconfiguring it.
160///
161/// This function takes two arguments: the first is a callback that is passed
162/// a scope and can reconfigure it. The second is callback that then executes
163/// in the context of that scope.
164///
165/// This is useful when extra data should be send with a single capture call
166/// for instance a different level or tags:
167///
168/// # Examples
169///
170/// ```
171/// use sentry::protocol::Level;
172///
173/// # let events = sentry::test::with_captured_events(|| {
174/// sentry::with_scope(
175/// |scope| scope.set_level(Some(Level::Warning)),
176/// || sentry::capture_message("some message", Level::Info),
177/// );
178/// # });
179/// # let captured_event = events.into_iter().next().unwrap();
180///
181/// assert_eq!(captured_event.level, Level::Warning);
182/// ```
183pub fn with_scope<C, F, R>(scope_config: C, callback: F) -> R
184where
185 C: FnOnce(&mut Scope),
186 F: FnOnce() -> R,
187{
188 #[cfg(feature = "client")]
189 {
190 Hub::with(|hub| {
191 if hub.is_active_and_usage_safe() {
192 hub.with_scope(scope_config, callback)
193 } else {
194 callback()
195 }
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(|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/// ```
282pub fn start_session() {
283 Hub::with_active(|hub| hub.start_session())
284}
285
286/// End the current Release Health Session.
287pub fn end_session() {
288 end_session_with_status(SessionStatus::Exited)
289}
290
291/// End the current Release Health Session with the given [`SessionStatus`].
292///
293/// By default, the SDK will only consider the `Exited` and `Crashed` status
294/// based on the type of events that were captured during the session.
295///
296/// When an `Abnormal` session should be captured, it has to be done explicitly
297/// using this function.
298pub fn end_session_with_status(status: SessionStatus) {
299 Hub::with_active(|hub| hub.end_session_with_status(status))
300}