Skip to content

feat(logs): add macro-based API #827

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 55 commits into from
Jun 6, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
55 commits
Select commit Hold shift + click to select a range
33f85f6
feat(logs): add log protocol types
lcian May 26, 2025
fc0dbed
remove unnecessary type
lcian May 26, 2025
a1c5800
lints
lcian May 26, 2025
e57ccab
remove debug prints
lcian May 26, 2025
3c903a0
details
lcian May 26, 2025
f44b001
docstring
lcian May 26, 2025
85bfe34
Merge branch 'master' into lcian/feat/logs-types
lcian May 26, 2025
ad09fab
add full envelope test
lcian May 27, 2025
89b0e16
cargo fmt
lcian May 27, 2025
046f5e4
cargo fmt
lcian May 27, 2025
7a33e36
changelog
lcian May 27, 2025
f67fa67
wip
lcian May 28, 2025
58098ae
refactor
lcian May 28, 2025
8ed9e15
refactor
lcian May 28, 2025
4d8e557
remove file
lcian May 28, 2025
d1fa386
Merge branch 'master' into lcian/feat/logs-types
lcian May 28, 2025
a6ba441
add from
lcian May 28, 2025
51b2470
severity number changes
lcian May 28, 2025
7ef7157
changelog
lcian May 28, 2025
bd6e293
improve
lcian May 28, 2025
2cff09d
improve
lcian May 28, 2025
1a3a088
improve
lcian May 28, 2025
e915747
improve
lcian May 28, 2025
5c4fde5
feat(logs): add basic API
lcian May 28, 2025
855bd1c
honor enable_logs
lcian May 28, 2025
dce7c0a
nit
lcian May 28, 2025
81ac086
move feature to UNSTABLE_logs
lcian May 28, 2025
48c25ec
changelog
lcian May 28, 2025
de8a0df
non-exhaustive
lcian Jun 3, 2025
d851cb8
generic from value
lcian Jun 3, 2025
a36c769
severity_number optional
lcian Jun 3, 2025
b4070c1
simplify test
lcian Jun 4, 2025
85704c0
link to docs in code
lcian Jun 4, 2025
9ce8c2f
merge base and updates
lcian Jun 4, 2025
00af37d
changelog
lcian Jun 4, 2025
31037fe
doc string
lcian Jun 4, 2025
a2c39a9
doc string
lcian Jun 4, 2025
b6f2556
Merge branch 'lcian/feat/logs-types' into lcian/feat/logs-api
lcian Jun 4, 2025
bd5ab3f
update test
lcian Jun 4, 2025
914a414
update test
lcian Jun 4, 2025
ba64c78
changelog
lcian Jun 4, 2025
50fed43
feat(logs): add macro-based API
lcian Jun 4, 2025
d8be41a
improve
lcian Jun 4, 2025
0429018
changelog
lcian Jun 4, 2025
d7ba0ff
docs
lcian Jun 4, 2025
adc35ab
docs
lcian Jun 4, 2025
f394837
address codecov
lcian Jun 4, 2025
27547c4
Update CHANGELOG.md
lcian Jun 5, 2025
1206c07
rename to _logger and doc(hide) logger_log, change doc strings
lcian Jun 5, 2025
690fa9d
refactor dots handling
lcian Jun 5, 2025
7df3699
allow some lints
lcian Jun 5, 2025
30c3d82
allow one more lint
lcian Jun 5, 2025
04ef81a
fix
lcian Jun 5, 2025
e6aa844
Merge branch 'master' into lcian/feat/logs-macros
lcian Jun 6, 2025
d0abc4a
Update logger.rs
lcian Jun 6, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,11 @@
### Features

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

## 0.38.1
Expand Down
2 changes: 2 additions & 0 deletions sentry-core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,8 @@ pub use crate::intodsn::IntoDsn;
pub use crate::performance::*;
pub use crate::scope::{Scope, ScopeGuard};
pub use crate::transport::{Transport, TransportFactory};
#[cfg(feature = "UNSTABLE_logs")]
mod logger; // structured logging macros exported with `#[macro_export]`

// client feature
#[cfg(feature = "client")]
Expand Down
333 changes: 333 additions & 0 deletions sentry-core/src/logger.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,333 @@
//! Macros for Sentry [structured logging](https://docs.sentry.io/product/explore/logs/).

// Helper macro to capture a log at the given level. Should not be used directly.
#[doc(hidden)]
#[macro_export]
macro_rules! logger_log {
// Simple message
($level:expr, $msg:literal) => {{
let log = $crate::protocol::Log {
level: $level,
body: $msg.to_owned(),
trace_id: None,
timestamp: ::std::time::SystemTime::now(),
severity_number: None,
attributes: $crate::protocol::Map::new(),
};
$crate::Hub::current().capture_log(log)
}};

// Message with format string and args
($level:expr, $fmt:literal, $($arg:expr),+) => {{
let mut attributes = $crate::protocol::Map::new();

attributes.insert(
"sentry.message.template".to_owned(),
$crate::protocol::LogAttribute($crate::protocol::Value::from($fmt))
);
let mut i = 0;
$(
attributes.insert(
format!("sentry.message.parameter.{}", i),
$crate::protocol::LogAttribute($crate::protocol::Value::from($arg))
);
i += 1;
)*
let _ = i; // avoid triggering the `unused_assignments` lint

let log = $crate::protocol::Log {
level: $level,
body: format!($fmt, $($arg),*),
trace_id: None,
timestamp: ::std::time::SystemTime::now(),
severity_number: None,
attributes,
};
$crate::Hub::current().capture_log(log)
}};

// Attributes entrypoint
($level:expr, $($rest:tt)+) => {{
let mut attributes = $crate::protocol::Map::new();
$crate::logger_log!(@internal attributes, $level, $($rest)+)
}};

// Attributes base case: no more attributes, simple message
(@internal $attrs:ident, $level:expr, $msg:literal) => {{
let log = $crate::protocol::Log {
level: $level,
body: $msg.to_owned(),
trace_id: None,
timestamp: ::std::time::SystemTime::now(),
severity_number: None,
#[allow(clippy::redundant_field_names)]
attributes: $attrs,
};
$crate::Hub::current().capture_log(log)
}};

// Attributes base case: no more attributes, message with format string and args
(@internal $attrs:ident, $level:expr, $fmt:literal, $($arg:expr),+) => {{
$attrs.insert(
"sentry.message.template".to_owned(),
$crate::protocol::LogAttribute($crate::protocol::Value::from($fmt))
);
let mut i = 0;
$(
$attrs.insert(
format!("sentry.message.parameter.{}", i),
$crate::protocol::LogAttribute($crate::protocol::Value::from($arg))
);
i += 1;
)*
let _ = i; // avoid triggering the `unused_assignments` lint

let log = $crate::protocol::Log {
level: $level,
body: format!($fmt, $($arg),*),
trace_id: None,
timestamp: ::std::time::SystemTime::now(),
severity_number: None,
#[allow(clippy::redundant_field_names)]
attributes: $attrs,
};
$crate::Hub::current().capture_log(log)
}};

// Attributes recursive case
(@internal $attrs:ident, $level:expr, $($key:ident).+ = $value:expr, $($rest:tt)+) => {{
$attrs.insert(
stringify!($($key).+).to_owned(),
$crate::protocol::LogAttribute($crate::protocol::Value::from($value))
);
$crate::logger_log!(@internal $attrs, $level, $($rest)+)
}};
}

/// Captures a log at the trace level, with the given message and attributes.
///
/// To attach attributes to a log, pass them with the `key = value` syntax before the message.
/// The message can be a simple string or a format string with its arguments.
///
/// The supported attribute keys are all valid Rust identifiers with up to 8 dots.
/// Using dots will nest multiple attributes under their common prefix in the UI.
///
/// The supported attribute values are simple types, such as string, numbers, and boolean.
///
/// # Examples
///
/// ```
/// use sentry::logger_trace;
///
/// // Simple message
/// logger_trace!("Hello world");
///
/// // Message with format args
/// logger_trace!("Value is {}", 42);
///
/// // Message with format args and attributes
/// logger_trace!(
/// error_code = 500,
/// user.id = "12345",
/// user.email = "test@test.com",
/// success = false,
/// "Error occurred: {}",
/// "bad input"
/// );
/// ```
#[macro_export]
macro_rules! logger_trace {
($($arg:tt)+) => {
$crate::logger_log!($crate::protocol::LogLevel::Trace, $($arg)+)
};
}

/// Captures a log at the debug level, with the given message and attributes.
///
/// To attach attributes to a log, pass them with the `key = value` syntax before the message.
/// The message can be a simple string or a format string with its arguments.
///
/// The supported attribute keys are all valid Rust identifiers with up to 8 dots.
/// Using dots will nest multiple attributes under their common prefix in the UI.
///
/// The supported attribute values are simple types, such as string, numbers, and boolean.
///
/// # Examples
///
/// ```
/// use sentry::logger_debug;
///
/// // Simple message
/// logger_debug!("Hello world");
///
/// // Message with format args
/// logger_debug!("Value is {}", 42);
///
/// // Message with format args and attributes
/// logger_debug!(
/// error_code = 500,
/// user.id = "12345",
/// user.email = "test@test.com",
/// success = false,
/// "Error occurred: {}",
/// "bad input"
/// );
/// ```
#[macro_export]
macro_rules! logger_debug {
($($arg:tt)+) => {
$crate::logger_log!($crate::protocol::LogLevel::Debug, $($arg)+)
};
}

/// Captures a log at the info level, with the given message and attributes.
///
/// To attach attributes to a log, pass them with the `key = value` syntax before the message.
/// The message can be a simple string or a format string with its arguments.
///
/// The supported attribute keys are all valid Rust identifiers with up to 8 dots.
/// Using dots will nest multiple attributes under their common prefix in the UI.
///
/// The supported attribute values are simple types, such as string, numbers, and boolean.
///
/// # Examples
///
/// ```
/// use sentry::logger_info;
///
/// // Simple message
/// logger_info!("Hello world");
///
/// // Message with format args
/// logger_info!("Value is {}", 42);
///
/// // Message with format args and attributes
/// logger_info!(
/// error_code = 500,
/// user.id = "12345",
/// user.email = "test@test.com",
/// success = false,
/// "Error occurred: {}",
/// "bad input"
/// );
/// ```
#[macro_export]
macro_rules! logger_info {
($($arg:tt)+) => {
$crate::logger_log!($crate::protocol::LogLevel::Info, $($arg)+)
};
}

/// Captures a log at the warn level, with the given message and attributes.
///
/// To attach attributes to a log, pass them with the `key = value` syntax before the message.
/// The message can be a simple string or a format string with its arguments.
///
/// The supported attribute keys are all valid Rust identifiers with up to 8 dots.
/// Using dots will nest multiple attributes under their common prefix in the UI.
///
/// The supported attribute values are simple types, such as string, numbers, and boolean.
///
/// # Examples
///
/// ```
/// use sentry::logger_warn;
///
/// // Simple message
/// logger_warn!("Hello world");
///
/// // Message with format args
/// logger_warn!("Value is {}", 42);
///
/// // Message with format args and attributes
/// logger_warn!(
/// error_code = 500,
/// user.id = "12345",
/// user.email = "test@test.com",
/// success = false,
/// "Error occurred: {}",
/// "bad input"
/// );
/// ```
#[macro_export]
macro_rules! logger_warn {
($($arg:tt)+) => {
$crate::logger_log!($crate::protocol::LogLevel::Warn, $($arg)+)
};
}

/// Captures a log at the error level, with the given message and attributes.
///
/// To attach attributes to a log, pass them with the `key = value` syntax before the message.
/// The message can be a simple string or a format string with its arguments.
///
/// The supported attribute keys are all valid Rust identifiers with up to 8 dots.
/// Using dots will nest multiple attributes under their common prefix in the UI.
///
/// The supported attribute values are simple types, such as string, numbers, and boolean.
///
/// # Examples
///
/// ```
/// use sentry::logger_error;
///
/// // Simple message
/// logger_error!("Hello world");
///
/// // Message with format args
/// logger_error!("Value is {}", 42);
///
/// // Message with format args and attributes
/// logger_error!(
/// error_code = 500,
/// user.id = "12345",
/// user.email = "test@test.com",
/// success = false,
/// "Error occurred: {}",
/// "bad input"
/// );
/// ```
#[macro_export]
macro_rules! logger_error {
($($arg:tt)+) => {
$crate::logger_log!($crate::protocol::LogLevel::Error, $($arg)+)
};
}

/// Captures a log at the fatal level, with the given message and attributes.
///
/// To attach attributes to a log, pass them with the `key = value` syntax before the message.
/// The message can be a simple string or a format string with its arguments.
///
/// The supported attribute keys are all valid Rust identifiers with up to 8 dots.
/// Using dots will nest multiple attributes under their common prefix in the UI.
///
/// The supported attribute values are simple types, such as string, numbers, and boolean.
///
/// # Examples
///
/// ```
/// use sentry::logger_fatal;
///
/// // Simple message
/// logger_fatal!("Hello world");
///
/// // Message with format args
/// logger_fatal!("Value is {}", 42);
///
/// // Message with format args and attributes
/// logger_fatal!(
/// error_code = 500,
/// user.id = "12345",
/// user.email = "test@test.com",
/// success = false,
/// "Error occurred: {}",
/// "bad input"
/// );
/// ```
#[macro_export]
macro_rules! logger_fatal {
($($arg:tt)+) => {
$crate::logger_log!($crate::protocol::LogLevel::Fatal, $($arg)+)
};
}
Loading