Skip to content

Commit be2e04e

Browse files
feat(logs): add macro-based API (getsentry#827)
Co-authored-by: Daniel Szoke <7881302+szokeasaurusrex@users.noreply.github.com>
1 parent d668e7d commit be2e04e

File tree

4 files changed

+605
-5
lines changed

4 files changed

+605
-5
lines changed

CHANGELOG.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,11 @@
1717
### Features
1818

1919
- feat(logs): add log protocol types (#821) by @lcian
20-
- Basic types for [Sentry structured logs](https://docs.sentry.io/product/explore/logs/) have been added.
21-
- feat(logs): add ability to capture and send logs (#823) by @lcian
22-
- A method `capture_log` has been added to the `Hub` to enable sending logs.
23-
- This is gated behind the `UNSTABLE_logs` feature flag (disabled by default).
24-
- Additionally, the new client option `enable_logs` needs to be enabled for logs to be sent to Sentry.
20+
- feat(logs): add ability to capture and send logs (#823) by @lcian & @Swatinem
21+
- feat(logs): add macro-based API (#827) by @lcian & @szokeasaurusrex
22+
- Support for [Sentry structured logs](https://docs.sentry.io/product/explore/logs/) has been added.
23+
- To enable logs, enable the `UNSTABLE_logs` feature of the `sentry` crate and set `enable_logs` to `true` in your client options.
24+
- Then, use the `logger_trace!`, `logger_debug!`, `logger_info!`, `logger_warn!`, `logger_error!` and `logger_fatal!` macros to capture logs.
2525
- Please note that breaking changes could occur until the API is finalized.
2626

2727
## 0.38.1

sentry-core/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,8 @@ pub use crate::intodsn::IntoDsn;
132132
pub use crate::performance::*;
133133
pub use crate::scope::{Scope, ScopeGuard};
134134
pub use crate::transport::{Transport, TransportFactory};
135+
#[cfg(feature = "UNSTABLE_logs")]
136+
mod logger; // structured logging macros exported with `#[macro_export]`
135137

136138
// client feature
137139
#[cfg(feature = "client")]

sentry-core/src/logger.rs

Lines changed: 333 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,333 @@
1+
//! Macros for Sentry [structured logging](https://docs.sentry.io/product/explore/logs/).
2+
3+
// Helper macro to capture a log at the given level. Should not be used directly.
4+
#[doc(hidden)]
5+
#[macro_export]
6+
macro_rules! logger_log {
7+
// Simple message
8+
($level:expr, $msg:literal) => {{
9+
let log = $crate::protocol::Log {
10+
level: $level,
11+
body: $msg.to_owned(),
12+
trace_id: None,
13+
timestamp: ::std::time::SystemTime::now(),
14+
severity_number: None,
15+
attributes: $crate::protocol::Map::new(),
16+
};
17+
$crate::Hub::current().capture_log(log)
18+
}};
19+
20+
// Message with format string and args
21+
($level:expr, $fmt:literal, $($arg:expr),+) => {{
22+
let mut attributes = $crate::protocol::Map::new();
23+
24+
attributes.insert(
25+
"sentry.message.template".to_owned(),
26+
$crate::protocol::LogAttribute($crate::protocol::Value::from($fmt))
27+
);
28+
let mut i = 0;
29+
$(
30+
attributes.insert(
31+
format!("sentry.message.parameter.{}", i),
32+
$crate::protocol::LogAttribute($crate::protocol::Value::from($arg))
33+
);
34+
i += 1;
35+
)*
36+
let _ = i; // avoid triggering the `unused_assignments` lint
37+
38+
let log = $crate::protocol::Log {
39+
level: $level,
40+
body: format!($fmt, $($arg),*),
41+
trace_id: None,
42+
timestamp: ::std::time::SystemTime::now(),
43+
severity_number: None,
44+
attributes,
45+
};
46+
$crate::Hub::current().capture_log(log)
47+
}};
48+
49+
// Attributes entrypoint
50+
($level:expr, $($rest:tt)+) => {{
51+
let mut attributes = $crate::protocol::Map::new();
52+
$crate::logger_log!(@internal attributes, $level, $($rest)+)
53+
}};
54+
55+
// Attributes base case: no more attributes, simple message
56+
(@internal $attrs:ident, $level:expr, $msg:literal) => {{
57+
let log = $crate::protocol::Log {
58+
level: $level,
59+
body: $msg.to_owned(),
60+
trace_id: None,
61+
timestamp: ::std::time::SystemTime::now(),
62+
severity_number: None,
63+
#[allow(clippy::redundant_field_names)]
64+
attributes: $attrs,
65+
};
66+
$crate::Hub::current().capture_log(log)
67+
}};
68+
69+
// Attributes base case: no more attributes, message with format string and args
70+
(@internal $attrs:ident, $level:expr, $fmt:literal, $($arg:expr),+) => {{
71+
$attrs.insert(
72+
"sentry.message.template".to_owned(),
73+
$crate::protocol::LogAttribute($crate::protocol::Value::from($fmt))
74+
);
75+
let mut i = 0;
76+
$(
77+
$attrs.insert(
78+
format!("sentry.message.parameter.{}", i),
79+
$crate::protocol::LogAttribute($crate::protocol::Value::from($arg))
80+
);
81+
i += 1;
82+
)*
83+
let _ = i; // avoid triggering the `unused_assignments` lint
84+
85+
let log = $crate::protocol::Log {
86+
level: $level,
87+
body: format!($fmt, $($arg),*),
88+
trace_id: None,
89+
timestamp: ::std::time::SystemTime::now(),
90+
severity_number: None,
91+
#[allow(clippy::redundant_field_names)]
92+
attributes: $attrs,
93+
};
94+
$crate::Hub::current().capture_log(log)
95+
}};
96+
97+
// Attributes recursive case
98+
(@internal $attrs:ident, $level:expr, $($key:ident).+ = $value:expr, $($rest:tt)+) => {{
99+
$attrs.insert(
100+
stringify!($($key).+).to_owned(),
101+
$crate::protocol::LogAttribute($crate::protocol::Value::from($value))
102+
);
103+
$crate::logger_log!(@internal $attrs, $level, $($rest)+)
104+
}};
105+
}
106+
107+
/// Captures a log at the trace level, with the given message and attributes.
108+
///
109+
/// To attach attributes to a log, pass them with the `key = value` syntax before the message.
110+
/// The message can be a simple string or a format string with its arguments.
111+
///
112+
/// The supported attribute keys are all valid Rust identifiers with up to 8 dots.
113+
/// Using dots will nest multiple attributes under their common prefix in the UI.
114+
///
115+
/// The supported attribute values are simple types, such as string, numbers, and boolean.
116+
///
117+
/// # Examples
118+
///
119+
/// ```
120+
/// use sentry::logger_trace;
121+
///
122+
/// // Simple message
123+
/// logger_trace!("Hello world");
124+
///
125+
/// // Message with format args
126+
/// logger_trace!("Value is {}", 42);
127+
///
128+
/// // Message with format args and attributes
129+
/// logger_trace!(
130+
/// error_code = 500,
131+
/// user.id = "12345",
132+
/// user.email = "test@test.com",
133+
/// success = false,
134+
/// "Error occurred: {}",
135+
/// "bad input"
136+
/// );
137+
/// ```
138+
#[macro_export]
139+
macro_rules! logger_trace {
140+
($($arg:tt)+) => {
141+
$crate::logger_log!($crate::protocol::LogLevel::Trace, $($arg)+)
142+
};
143+
}
144+
145+
/// Captures a log at the debug level, with the given message and attributes.
146+
///
147+
/// To attach attributes to a log, pass them with the `key = value` syntax before the message.
148+
/// The message can be a simple string or a format string with its arguments.
149+
///
150+
/// The supported attribute keys are all valid Rust identifiers with up to 8 dots.
151+
/// Using dots will nest multiple attributes under their common prefix in the UI.
152+
///
153+
/// The supported attribute values are simple types, such as string, numbers, and boolean.
154+
///
155+
/// # Examples
156+
///
157+
/// ```
158+
/// use sentry::logger_debug;
159+
///
160+
/// // Simple message
161+
/// logger_debug!("Hello world");
162+
///
163+
/// // Message with format args
164+
/// logger_debug!("Value is {}", 42);
165+
///
166+
/// // Message with format args and attributes
167+
/// logger_debug!(
168+
/// error_code = 500,
169+
/// user.id = "12345",
170+
/// user.email = "test@test.com",
171+
/// success = false,
172+
/// "Error occurred: {}",
173+
/// "bad input"
174+
/// );
175+
/// ```
176+
#[macro_export]
177+
macro_rules! logger_debug {
178+
($($arg:tt)+) => {
179+
$crate::logger_log!($crate::protocol::LogLevel::Debug, $($arg)+)
180+
};
181+
}
182+
183+
/// Captures a log at the info level, with the given message and attributes.
184+
///
185+
/// To attach attributes to a log, pass them with the `key = value` syntax before the message.
186+
/// The message can be a simple string or a format string with its arguments.
187+
///
188+
/// The supported attribute keys are all valid Rust identifiers with up to 8 dots.
189+
/// Using dots will nest multiple attributes under their common prefix in the UI.
190+
///
191+
/// The supported attribute values are simple types, such as string, numbers, and boolean.
192+
///
193+
/// # Examples
194+
///
195+
/// ```
196+
/// use sentry::logger_info;
197+
///
198+
/// // Simple message
199+
/// logger_info!("Hello world");
200+
///
201+
/// // Message with format args
202+
/// logger_info!("Value is {}", 42);
203+
///
204+
/// // Message with format args and attributes
205+
/// logger_info!(
206+
/// error_code = 500,
207+
/// user.id = "12345",
208+
/// user.email = "test@test.com",
209+
/// success = false,
210+
/// "Error occurred: {}",
211+
/// "bad input"
212+
/// );
213+
/// ```
214+
#[macro_export]
215+
macro_rules! logger_info {
216+
($($arg:tt)+) => {
217+
$crate::logger_log!($crate::protocol::LogLevel::Info, $($arg)+)
218+
};
219+
}
220+
221+
/// Captures a log at the warn level, with the given message and attributes.
222+
///
223+
/// To attach attributes to a log, pass them with the `key = value` syntax before the message.
224+
/// The message can be a simple string or a format string with its arguments.
225+
///
226+
/// The supported attribute keys are all valid Rust identifiers with up to 8 dots.
227+
/// Using dots will nest multiple attributes under their common prefix in the UI.
228+
///
229+
/// The supported attribute values are simple types, such as string, numbers, and boolean.
230+
///
231+
/// # Examples
232+
///
233+
/// ```
234+
/// use sentry::logger_warn;
235+
///
236+
/// // Simple message
237+
/// logger_warn!("Hello world");
238+
///
239+
/// // Message with format args
240+
/// logger_warn!("Value is {}", 42);
241+
///
242+
/// // Message with format args and attributes
243+
/// logger_warn!(
244+
/// error_code = 500,
245+
/// user.id = "12345",
246+
/// user.email = "test@test.com",
247+
/// success = false,
248+
/// "Error occurred: {}",
249+
/// "bad input"
250+
/// );
251+
/// ```
252+
#[macro_export]
253+
macro_rules! logger_warn {
254+
($($arg:tt)+) => {
255+
$crate::logger_log!($crate::protocol::LogLevel::Warn, $($arg)+)
256+
};
257+
}
258+
259+
/// Captures a log at the error level, with the given message and attributes.
260+
///
261+
/// To attach attributes to a log, pass them with the `key = value` syntax before the message.
262+
/// The message can be a simple string or a format string with its arguments.
263+
///
264+
/// The supported attribute keys are all valid Rust identifiers with up to 8 dots.
265+
/// Using dots will nest multiple attributes under their common prefix in the UI.
266+
///
267+
/// The supported attribute values are simple types, such as string, numbers, and boolean.
268+
///
269+
/// # Examples
270+
///
271+
/// ```
272+
/// use sentry::logger_error;
273+
///
274+
/// // Simple message
275+
/// logger_error!("Hello world");
276+
///
277+
/// // Message with format args
278+
/// logger_error!("Value is {}", 42);
279+
///
280+
/// // Message with format args and attributes
281+
/// logger_error!(
282+
/// error_code = 500,
283+
/// user.id = "12345",
284+
/// user.email = "test@test.com",
285+
/// success = false,
286+
/// "Error occurred: {}",
287+
/// "bad input"
288+
/// );
289+
/// ```
290+
#[macro_export]
291+
macro_rules! logger_error {
292+
($($arg:tt)+) => {
293+
$crate::logger_log!($crate::protocol::LogLevel::Error, $($arg)+)
294+
};
295+
}
296+
297+
/// Captures a log at the fatal level, with the given message and attributes.
298+
///
299+
/// To attach attributes to a log, pass them with the `key = value` syntax before the message.
300+
/// The message can be a simple string or a format string with its arguments.
301+
///
302+
/// The supported attribute keys are all valid Rust identifiers with up to 8 dots.
303+
/// Using dots will nest multiple attributes under their common prefix in the UI.
304+
///
305+
/// The supported attribute values are simple types, such as string, numbers, and boolean.
306+
///
307+
/// # Examples
308+
///
309+
/// ```
310+
/// use sentry::logger_fatal;
311+
///
312+
/// // Simple message
313+
/// logger_fatal!("Hello world");
314+
///
315+
/// // Message with format args
316+
/// logger_fatal!("Value is {}", 42);
317+
///
318+
/// // Message with format args and attributes
319+
/// logger_fatal!(
320+
/// error_code = 500,
321+
/// user.id = "12345",
322+
/// user.email = "test@test.com",
323+
/// success = false,
324+
/// "Error occurred: {}",
325+
/// "bad input"
326+
/// );
327+
/// ```
328+
#[macro_export]
329+
macro_rules! logger_fatal {
330+
($($arg:tt)+) => {
331+
$crate::logger_log!($crate::protocol::LogLevel::Fatal, $($arg)+)
332+
};
333+
}

0 commit comments

Comments
 (0)