From f41a98430c10aa2ee5f679b1a23b761af0217970 Mon Sep 17 00:00:00 2001 From: Arpad Borsos Date: Wed, 23 Sep 2020 18:03:54 +0200 Subject: [PATCH 1/6] ref: Rearchitect the log and slog Integrations --- sentry-log/src/integration.rs | 92 ++++++++++++++++++++++--- sentry-log/src/logger.rs | 2 + sentry-slog/src/converters.rs | 12 ++-- sentry-slog/src/drain.rs | 108 ++++++++++++++++++++++++++--- sentry-slog/src/integration.rs | 121 +-------------------------------- sentry-slog/src/lib.rs | 33 ++++----- 6 files changed, 208 insertions(+), 160 deletions(-) diff --git a/sentry-log/src/integration.rs b/sentry-log/src/integration.rs index b4562d044..dc6062b83 100644 --- a/sentry-log/src/integration.rs +++ b/sentry-log/src/integration.rs @@ -1,28 +1,65 @@ +#![allow(deprecated)] + use std::sync::Once; -use log::{Level, LevelFilter, Record}; +use log::Record; +use sentry_core::protocol::{Breadcrumb, Event}; use sentry_core::{ClientOptions, Integration}; use crate::logger::Logger; +/// The Action that Sentry should perform for a [`log::Metadata`]. +#[derive(Debug)] +pub enum LevelFilter { + /// Ignore the [`Record`]. + Ignore, + /// Create a [`Breadcrumb`] from this [`Record`]. + Breadcrumb, + /// Create a message [`Event`] from this [`Record`]. + Event, + /// Create an exception [`Event`] from this [`Record`]. + Exception, +} + +/// The type of Data Sentry should ingest for a [`log::Record`]. +#[allow(clippy::large_enum_variant)] +pub enum RecordMapping { + /// Ignore the [`Record`] + Ignore, + /// Adds the [`Breadcrumb`] to the Sentry scope. + Breadcrumb(Breadcrumb), + /// Captures the [`Event`] to Sentry. + Event(Event<'static>), +} + /// Logger specific options. pub struct LogIntegration { /// The global filter that should be used (also used before dispatching /// to the nested logger). - pub global_filter: Option, + #[deprecated = "use the [`filter()`] function instead"] + pub global_filter: Option, /// The sentry specific log level filter (defaults to `Info`) - pub filter: LevelFilter, + #[deprecated = "use the [`filter()`] function instead"] + pub filter: log::LevelFilter, /// If set to `true`, breadcrumbs will be emitted. (defaults to `true`) + #[deprecated = "use the [`filter()`] function instead"] pub emit_breadcrumbs: bool, /// If set to `true` error events will be sent for errors in the log. (defaults to `true`) + #[deprecated = "use the [`filter()`] function instead"] pub emit_error_events: bool, /// If set to `true` warning events will be sent for warnings in the log. (defaults to `false`) + #[deprecated = "use the [`filter()`] function instead"] pub emit_warning_events: bool, /// If set to `true` current stacktrace will be resolved and attached /// to each event. (expensive, defaults to `true`) + #[deprecated = "use builder functions instead; direct field access will be removed soon"] pub attach_stacktraces: bool, /// The destination log. + #[deprecated = "use builder functions instead; direct field access will be removed soon"] pub dest_log: Option>, + + sentry_filter: Option) -> LevelFilter + Send + Sync>>, + mapper: Option) -> RecordMapping + Send + Sync>>, } static INIT: Once = Once::new(); @@ -52,12 +89,14 @@ impl Default for LogIntegration { fn default() -> Self { Self { global_filter: None, - filter: LevelFilter::Info, + filter: log::LevelFilter::Info, emit_breadcrumbs: true, emit_error_events: true, emit_warning_events: false, attach_stacktraces: true, dest_log: None, + sentry_filter: None, + mapper: None, } } } @@ -81,6 +120,11 @@ impl std::fmt::Debug for LogIntegration { } impl LogIntegration { + /// Creates a new `log` Integration. + pub fn new() -> Self { + Self::default() + } + /// Initializes an env logger as destination target. #[cfg(feature = "env_logger")] pub fn with_env_logger_dest(mut self, logger: Option) -> Self { @@ -94,13 +138,37 @@ impl LogIntegration { self } + /// Sets a custom filter function. + /// + /// The filter classifies how sentry should handle [`Record`]s based on + /// their [`log::Metadata`]. + pub fn filter(mut self, filter: F) -> Self + where + F: Fn(&log::Metadata<'_>) -> LevelFilter + Send + Sync + 'static, + { + self.sentry_filter = Some(Box::new(filter)); + self + } + + /// Sets a custom mapper function. + /// + /// The mapper is responsible for creating either breadcrumbs or events + /// from [`Record`]s. + pub fn mapper(mut self, mapper: M) -> Self + where + M: Fn(&Record<'_>) -> RecordMapping + Send + Sync + 'static, + { + self.mapper = Some(Box::new(mapper)); + self + } + /// Returns the effective global filter. /// /// This is what is set for these logger options when the log level /// needs to be set globally. This is the greater of `global_filter` /// and `filter`. #[inline(always)] - pub(crate) fn effective_global_filter(&self) -> LevelFilter { + pub(crate) fn effective_global_filter(&self) -> log::LevelFilter { let filter = if let Some(filter) = self.global_filter { if filter < self.filter { self.filter @@ -117,21 +185,21 @@ impl LogIntegration { /// /// This is controlled by `emit_error_events` and `emit_warning_events`. #[inline(always)] - fn issue_filter(&self) -> LevelFilter { + fn issue_filter(&self) -> log::LevelFilter { if self.emit_warning_events { - LevelFilter::Warn + log::LevelFilter::Warn } else if self.emit_error_events { - LevelFilter::Error + log::LevelFilter::Error } else { - LevelFilter::Off + log::LevelFilter::Off } } /// Checks if an issue should be created. pub(crate) fn create_issue_for_record(&self, record: &Record<'_>) -> bool { match record.level() { - Level::Warn => self.emit_warning_events, - Level::Error => self.emit_error_events, + log::Level::Warn => self.emit_warning_events, + log::Level::Error => self.emit_error_events, _ => false, } } @@ -139,6 +207,8 @@ impl LogIntegration { #[test] fn test_filters() { + use log::LevelFilter; + let opt_warn = LogIntegration { filter: LevelFilter::Warn, ..Default::default() diff --git a/sentry-log/src/logger.rs b/sentry-log/src/logger.rs index 6e3155de1..148a7c669 100644 --- a/sentry-log/src/logger.rs +++ b/sentry-log/src/logger.rs @@ -1,3 +1,5 @@ +#![allow(deprecated)] + use crate::converters::{breadcrumb_from_record, event_from_record}; use crate::LogIntegration; diff --git a/sentry-slog/src/converters.rs b/sentry-slog/src/converters.rs index 3af2dd074..ce51410b5 100644 --- a/sentry-slog/src/converters.rs +++ b/sentry-slog/src/converters.rs @@ -1,7 +1,7 @@ use sentry_core::protocol::{Breadcrumb, Event, Exception, Frame, Level, Map, Stacktrace, Value}; use slog::{OwnedKVList, Record, KV}; -/// Converts a `slog::Level` to a sentry `Level` +/// Converts a [`slog::Level`] to a Sentry [`Level`] pub fn convert_log_level(level: slog::Level) -> Level { match level { slog::Level::Trace | slog::Level::Debug => Level::Debug, @@ -11,13 +11,13 @@ pub fn convert_log_level(level: slog::Level) -> Level { } } -/// Adds the data from a `slog::KV` into a sentry `Map`. +/// Adds the data from a [`slog::KV`] into a Sentry [`Map`]. fn add_kv_to_map(map: &mut Map, kv: &impl KV) { let _ = (map, kv); // TODO: actually implement this ;-) } -/// Creates a sentry `Breadcrumb` from the `slog::Record`. +/// Creates a Sentry [`Breadcrumb`] from the [`Record`]. pub fn breadcrumb_from_record(record: &Record, values: &OwnedKVList) -> Breadcrumb { let mut data = Map::new(); add_kv_to_map(&mut data, &record.kv()); @@ -32,7 +32,7 @@ pub fn breadcrumb_from_record(record: &Record, values: &OwnedKVList) -> Breadcru } } -/// Creates a simple message `Event` from the `slog::Record`. +/// Creates a simple message [`Event`] from the [`Record`]. pub fn event_from_record(record: &Record, values: &OwnedKVList) -> Event<'static> { let mut extra = Map::new(); add_kv_to_map(&mut extra, &record.kv()); @@ -44,10 +44,10 @@ pub fn event_from_record(record: &Record, values: &OwnedKVList) -> Event<'static } } -/// Creates an exception `Event` from the `slog::Record`. +/// Creates an exception [`Event`] from the [`Record`]. /// /// The exception will have a stacktrace that corresponds to the location -/// information contained in the `slog::Record`. +/// information contained in the [`Record`]. /// /// # Examples /// diff --git a/sentry-slog/src/drain.rs b/sentry-slog/src/drain.rs index f2325b847..869344bd9 100644 --- a/sentry-slog/src/drain.rs +++ b/sentry-slog/src/drain.rs @@ -1,15 +1,84 @@ -use crate::SlogIntegration; +use sentry_core::protocol::{Breadcrumb, Event}; use slog::{Drain, OwnedKVList, Record}; -/// A Drain which passes all Records to sentry. +use crate::{breadcrumb_from_record, event_from_record, exception_from_record}; + +/// The Action that Sentry should perform for a [`slog::Level`]. +#[derive(Debug)] +pub enum LevelFilter { + /// Ignore the [`Record`]. + Ignore, + /// Create a [`Breadcrumb`] from this [`Record`]. + Breadcrumb, + /// Create a message [`Event`] from this [`Record`]. + Event, + /// Create an exception [`Event`] from this [`Record`]. + Exception, +} + +/// The type of Data Sentry should ingest for a [`slog::Record`]. +#[allow(clippy::large_enum_variant)] +pub enum RecordMapping { + /// Ignore the [`Record`] + Ignore, + /// Adds the [`Breadcrumb`] to the Sentry scope. + Breadcrumb(Breadcrumb), + /// Captures the [`Event`] to Sentry. + Event(Event<'static>), +} + +/// The default slog filter. +/// +/// By default, an exception event is captured for `critical` logs, +/// a regular event for `error` and `warning` logs, and breadcrumbs for +/// everything else. +pub fn default_filter(level: slog::Level) -> LevelFilter { + match level { + slog::Level::Critical => LevelFilter::Exception, + slog::Level::Error | slog::Level::Warning => LevelFilter::Event, + slog::Level::Info | slog::Level::Debug | slog::Level::Trace => LevelFilter::Breadcrumb, + } +} + +/// A Drain which passes all [`Record`]s to Sentry. pub struct SentryDrain { drain: D, + filter: Box LevelFilter + Send + Sync>, + mapper: Option RecordMapping + Send + Sync>>, } impl SentryDrain { /// Creates a new `SentryDrain`, wrapping a `slog::Drain`. pub fn new(drain: D) -> Self { - Self { drain } + Self { + drain, + filter: Box::new(default_filter), + mapper: None, + } + } + + /// Sets a custom filter function. + /// + /// The filter classifies how sentry should handle [`Record`]s based on + /// their [`slog::Level`]. + pub fn filter(mut self, filter: F) -> Self + where + F: Fn(slog::Level) -> LevelFilter + Send + Sync + 'static, + { + self.filter = Box::new(filter); + self + } + + /// Sets a custom mapper function. + /// + /// The mapper is responsible for creating either breadcrumbs or events + /// from [`Record`]s. + pub fn mapper(mut self, mapper: M) -> Self + where + M: Fn(&Record, &OwnedKVList) -> RecordMapping + Send + Sync + 'static, + { + self.mapper = Some(Box::new(mapper)); + self } } @@ -18,15 +87,36 @@ impl slog::Drain for SentryDrain { type Err = D::Err; fn log(&self, record: &Record, values: &OwnedKVList) -> Result { - sentry_core::with_integration(|integration: &SlogIntegration, hub| { - integration.log(hub, record, values) - }); + let item: RecordMapping = match &self.mapper { + Some(mapper) => mapper(record, values), + None => match (self.filter)(record.level()) { + LevelFilter::Ignore => RecordMapping::Ignore, + LevelFilter::Breadcrumb => { + RecordMapping::Breadcrumb(breadcrumb_from_record(record, values)) + } + LevelFilter::Event => RecordMapping::Event(event_from_record(record, values)), + LevelFilter::Exception => { + RecordMapping::Event(exception_from_record(record, values)) + } + }, + }; + + match item { + RecordMapping::Ignore => {} + RecordMapping::Breadcrumb(b) => sentry_core::add_breadcrumb(b), + RecordMapping::Event(e) => { + sentry_core::capture_event(e); + } + } + self.drain.log(record, values) } fn is_enabled(&self, level: slog::Level) -> bool { - sentry_core::with_integration(|integration: &SlogIntegration, _| { - integration.is_enabled(level) - }) || self.drain.is_enabled(level) + self.drain.is_enabled(level) + || match (self.filter)(level) { + LevelFilter::Ignore => false, + _ => true, + } } } diff --git a/sentry-slog/src/integration.rs b/sentry-slog/src/integration.rs index 6d27b62e1..9f530e4b7 100644 --- a/sentry-slog/src/integration.rs +++ b/sentry-slog/src/integration.rs @@ -1,129 +1,14 @@ -use sentry_core::protocol::{Breadcrumb, Event}; -use sentry_core::{Hub, Integration}; -use slog::{OwnedKVList, Record}; - -use crate::{breadcrumb_from_record, event_from_record, exception_from_record}; - -/// The Action that Sentry should perform for a `slog::Level`. -pub enum LevelFilter { - /// Ignore the `Record`. - Ignore, - /// Create a `Breadcrumb` from this `Record`. - Breadcrumb, - /// Create a message `Event` from this `Record`. - Event, - /// Create an exception `Event` from this `Record`. - Exception, -} - -/// Custom Mappers -#[allow(clippy::large_enum_variant)] -pub enum RecordMapping { - /// Adds the `Breadcrumb` to the sentry scope. - Breadcrumb(Breadcrumb), - /// Captures the `Event` to sentry. - Event(Event<'static>), -} - -/// The default slog filter. -/// -/// By default, an exception event is captured for `critical` logs, -/// a regular event for `error` and `warning` logs, and breadcrumbs for -/// everything else. -pub fn default_filter(level: slog::Level) -> LevelFilter { - match level { - slog::Level::Critical => LevelFilter::Exception, - slog::Level::Error | slog::Level::Warning => LevelFilter::Event, - slog::Level::Info | slog::Level::Debug | slog::Level::Trace => LevelFilter::Breadcrumb, - } -} +use sentry_core::Integration; /// The Sentry `slog` Integration. -/// -/// Can be configured with a custom filter and mapper. -pub struct SlogIntegration { - filter: Box LevelFilter + Send + Sync>, - mapper: Option RecordMapping + Send + Sync>>, -} - -impl Default for SlogIntegration { - fn default() -> Self { - Self { - filter: Box::new(default_filter), - mapper: None, - } - } -} - -impl std::fmt::Debug for SlogIntegration { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - #[derive(Debug)] - struct SlogMapper; - let mapper = self.mapper.as_ref().map(|_| SlogMapper); - f.debug_struct("SlogIntegration") - .field("mapper", &mapper) - .finish() - } -} +#[derive(Debug, Default)] +pub struct SlogIntegration {} impl SlogIntegration { /// Create a new `slog` Integration. pub fn new() -> Self { Self::default() } - - /// Sets a custom filter function. - /// - /// The filter classifies how sentry should handle `slog::Record`s based on - /// their level. - pub fn filter(mut self, filter: F) -> Self - where - F: Fn(slog::Level) -> LevelFilter + Send + Sync + 'static, - { - self.filter = Box::new(filter); - self - } - - /// Sets a custom mapper function. - /// - /// The mapper is responsible for creating either breadcrumbs or events - /// from `slog::Record`s. - pub fn mapper(mut self, mapper: M) -> Self - where - M: Fn(&Record, &OwnedKVList) -> RecordMapping + Send + Sync + 'static, - { - self.mapper = Some(Box::new(mapper)); - self - } - - pub(crate) fn log(&self, hub: &Hub, record: &Record, values: &OwnedKVList) { - let item: RecordMapping = match &self.mapper { - Some(mapper) => mapper(record, values), - None => match (self.filter)(record.level()) { - LevelFilter::Ignore => return, - LevelFilter::Breadcrumb => { - RecordMapping::Breadcrumb(breadcrumb_from_record(record, values)) - } - LevelFilter::Event => RecordMapping::Event(event_from_record(record, values)), - LevelFilter::Exception => { - RecordMapping::Event(exception_from_record(record, values)) - } - }, - }; - match item { - RecordMapping::Breadcrumb(b) => hub.add_breadcrumb(b), - RecordMapping::Event(e) => { - hub.capture_event(e); - } - } - } - - pub(crate) fn is_enabled(&self, level: slog::Level) -> bool { - match (self.filter)(level) { - LevelFilter::Ignore => false, - _ => true, - } - } } impl Integration for SlogIntegration { diff --git a/sentry-slog/src/lib.rs b/sentry-slog/src/lib.rs index 33e23e70f..74f1514a4 100644 --- a/sentry-slog/src/lib.rs +++ b/sentry-slog/src/lib.rs @@ -1,9 +1,8 @@ //! Sentry `slog` Integration. //! -//! The sentry `slog` integration consists of two parts, the -//! [`SlogIntegration`] which configures how sentry should treat -//! `slog::Record`s, and the [`SentryDrain`], which can be used to create a -//! `slog::Logger`. +//! This mainly provides the [`SentryDrain`], which wraps another [`slog::Drain`] +//! and can be configured to forward [`slog::Record`]s to Sentry. +//! The [`SentryDrain`] can be used to create a `slog::Logger`. //! //! *NOTE*: This integration currently does not process any `slog::KV` pairs, //! but support for this will be added in the future. @@ -14,14 +13,13 @@ //! use sentry::{init, ClientOptions}; //! use sentry_slog::{SentryDrain, SlogIntegration}; //! -//! let integration = SlogIntegration::default(); -//! let options = ClientOptions::default().add_integration(integration); +//! let options = ClientOptions::new().add_integration(SlogIntegration::new()); //! let _sentry = sentry::init(options); //! //! let drain = SentryDrain::new(slog::Discard); //! let root = slog::Logger::root(drain, slog::o!()); //! -//! # let options = ClientOptions::default().add_integration(SlogIntegration::default()); +//! # let options = ClientOptions::new().add_integration(SlogIntegration::new()); //! # let events = sentry::test::with_captured_events_options(|| { //! slog::info!(root, "recorded as breadcrumb"); //! slog::warn!(root, "recorded as regular event"); @@ -45,23 +43,26 @@ //! assert_eq!(captured_event.exception.len(), 1); //! ``` //! -//! The integration can also be customized with a `filter`, and a `mapper`: +//! The Drain can also be customized with a `filter`, and a `mapper`: //! //! ``` -//! use sentry_slog::{exception_from_record, LevelFilter, RecordMapping, SlogIntegration}; +//! use sentry_slog::{exception_from_record, LevelFilter, RecordMapping, SentryDrain}; //! -//! let integration = SlogIntegration::default() +//! let drain = SentryDrain::new(slog::Discard) //! .filter(|level| match level { //! slog::Level::Critical | slog::Level::Error => LevelFilter::Event, //! _ => LevelFilter::Ignore, //! }) -//! .mapper(|record, kv| RecordMapping::Event(exception_from_record(record, kv))); +//! .mapper(|record, kv| match record.level() { +//! slog::Level::Critical | slog::Level::Error => +//! RecordMapping::Event(exception_from_record(record, kv))), +//! _ => RecordMapping::Ignore, +//! }); //! ``` //! -//! Please not that the `mapper` can override any classification from the -//! previous `filter`. +//! When a `mapper` is specified, a corresponding `filter` should also be +//! provided. //! -//! [`SlogIntegration`]: struct.SlogIntegration.html //! [`SentryDrain`]: struct.SentryDrain.html #![doc(html_favicon_url = "https://sentry-brand.storage.googleapis.com/favicon.ico")] @@ -74,5 +75,5 @@ mod drain; mod integration; pub use converters::*; -pub use drain::SentryDrain; -pub use integration::{default_filter, LevelFilter, RecordMapping, SlogIntegration}; +pub use drain::{default_filter, LevelFilter, RecordMapping, SentryDrain}; +pub use integration::SlogIntegration; From 59c91456aab9cc4ccd5d124781bbf4b47540d6ed Mon Sep 17 00:00:00 2001 From: Arpad Borsos Date: Thu, 24 Sep 2020 11:33:25 +0200 Subject: [PATCH 2/6] completely rework the log integrations --- sentry-log/Cargo.toml | 5 - sentry-log/src/converters.rs | 49 ++++--- sentry-log/src/integration.rs | 225 +-------------------------------- sentry-log/src/lib.rs | 26 ++-- sentry-log/src/logger.rs | 179 +++++++++++++++++++++----- sentry-slog/src/drain.rs | 7 +- sentry-slog/src/lib.rs | 2 +- sentry/Cargo.toml | 5 +- sentry/examples/log-demo.rs | 20 ++- sentry/examples/thread-demo.rs | 19 ++- sentry/tests/test_log.rs | 35 ++++- 11 files changed, 256 insertions(+), 316 deletions(-) diff --git a/sentry-log/Cargo.toml b/sentry-log/Cargo.toml index 129c04fee..4d7cad08f 100644 --- a/sentry-log/Cargo.toml +++ b/sentry-log/Cargo.toml @@ -11,16 +11,11 @@ Sentry integration for log and env_logger crates. """ edition = "2018" -[package.metadata.docs.rs] -all-features = true - [dependencies] sentry-core = { version = "0.20.1", path = "../sentry-core" } sentry-backtrace = { version = "0.20.1", path = "../sentry-backtrace" } log = { version = "0.4.8", features = ["std"] } -env_logger = { version = "0.7.1", optional = true } [dev-dependencies] sentry = { version = "0.20.1", path = "../sentry", default-features = false, features = ["test"] } pretty_env_logger = "0.4.0" -env_logger = "0.7.1" diff --git a/sentry-log/src/converters.rs b/sentry-log/src/converters.rs index 3fd30aea9..cfd31474c 100644 --- a/sentry-log/src/converters.rs +++ b/sentry-log/src/converters.rs @@ -1,8 +1,8 @@ -use sentry_backtrace::current_stacktrace; -use sentry_core::protocol::{Event, Exception}; +use sentry_core::protocol::{Event, Exception, Frame, Stacktrace}; use sentry_core::{Breadcrumb, Level}; -fn convert_log_level(level: log::Level) -> Level { +/// Converts a [`log::Level`] to a Sentry [`Level`] +pub fn convert_log_level(level: log::Level) -> Level { match level { log::Level::Error => Level::Error, log::Level::Warn => Level::Warning, @@ -11,7 +11,7 @@ fn convert_log_level(level: log::Level) -> Level { } } -/// Creates a breadcrumb from a given log record. +/// Creates a [`Breadcrumb`] from a given [`log::Record`]. pub fn breadcrumb_from_record(record: &log::Record<'_>) -> Breadcrumb { Breadcrumb { ty: "log".into(), @@ -22,25 +22,34 @@ pub fn breadcrumb_from_record(record: &log::Record<'_>) -> Breadcrumb { } } -/// Creates an event from a given log record. -/// -/// If `with_stacktrace` is set to `true` then a stacktrace is attached -/// from the current frame. -pub fn event_from_record(record: &log::Record<'_>, with_stacktrace: bool) -> Event<'static> { +/// Creates an [`Event`] from a given [`log::Record`]. +pub fn event_from_record(record: &log::Record<'_>) -> Event<'static> { Event { logger: Some(record.target().into()), level: convert_log_level(record.level()), - exception: vec![Exception { - ty: record.target().into(), - value: Some(format!("{}", record.args())), - stacktrace: if with_stacktrace { - current_stacktrace() - } else { - None - }, - ..Default::default() - }] - .into(), + message: Some(format!("{}", record.args())), ..Default::default() } } + +/// Creates an exception [`Event`] from a given [`log::Record`]. +pub fn exception_from_record(record: &log::Record<'_>) -> Event<'static> { + let mut event = event_from_record(record); + let frame = Frame { + module: record.module_path().map(ToOwned::to_owned), + filename: record.file().map(ToOwned::to_owned), + lineno: record.line().map(Into::into), + ..Default::default() + }; + let exception = Exception { + ty: record.target().into(), + value: event.message.clone(), + stacktrace: Some(Stacktrace { + frames: vec![frame], + ..Default::default() + }), + ..Default::default() + }; + event.exception = vec![exception].into(); + event +} diff --git a/sentry-log/src/integration.rs b/sentry-log/src/integration.rs index dc6062b83..055cf157d 100644 --- a/sentry-log/src/integration.rs +++ b/sentry-log/src/integration.rs @@ -1,68 +1,8 @@ -#![allow(deprecated)] - -use std::sync::Once; - -use log::Record; -use sentry_core::protocol::{Breadcrumb, Event}; use sentry_core::{ClientOptions, Integration}; -use crate::logger::Logger; - -/// The Action that Sentry should perform for a [`log::Metadata`]. -#[derive(Debug)] -pub enum LevelFilter { - /// Ignore the [`Record`]. - Ignore, - /// Create a [`Breadcrumb`] from this [`Record`]. - Breadcrumb, - /// Create a message [`Event`] from this [`Record`]. - Event, - /// Create an exception [`Event`] from this [`Record`]. - Exception, -} - -/// The type of Data Sentry should ingest for a [`log::Record`]. -#[allow(clippy::large_enum_variant)] -pub enum RecordMapping { - /// Ignore the [`Record`] - Ignore, - /// Adds the [`Breadcrumb`] to the Sentry scope. - Breadcrumb(Breadcrumb), - /// Captures the [`Event`] to Sentry. - Event(Event<'static>), -} - -/// Logger specific options. -pub struct LogIntegration { - /// The global filter that should be used (also used before dispatching - /// to the nested logger). - #[deprecated = "use the [`filter()`] function instead"] - pub global_filter: Option, - /// The sentry specific log level filter (defaults to `Info`) - #[deprecated = "use the [`filter()`] function instead"] - pub filter: log::LevelFilter, - /// If set to `true`, breadcrumbs will be emitted. (defaults to `true`) - #[deprecated = "use the [`filter()`] function instead"] - pub emit_breadcrumbs: bool, - /// If set to `true` error events will be sent for errors in the log. (defaults to `true`) - #[deprecated = "use the [`filter()`] function instead"] - pub emit_error_events: bool, - /// If set to `true` warning events will be sent for warnings in the log. (defaults to `false`) - #[deprecated = "use the [`filter()`] function instead"] - pub emit_warning_events: bool, - /// If set to `true` current stacktrace will be resolved and attached - /// to each event. (expensive, defaults to `true`) - #[deprecated = "use builder functions instead; direct field access will be removed soon"] - pub attach_stacktraces: bool, - /// The destination log. - #[deprecated = "use builder functions instead; direct field access will be removed soon"] - pub dest_log: Option>, - - sentry_filter: Option) -> LevelFilter + Send + Sync>>, - mapper: Option) -> RecordMapping + Send + Sync>>, -} - -static INIT: Once = Once::new(); +/// The Sentry [`log`] Integration. +#[derive(Default)] +pub struct LogIntegration; impl Integration for LogIntegration { fn name(&self) -> &'static str { @@ -73,49 +13,6 @@ impl Integration for LogIntegration { cfg.in_app_exclude.push("log::"); cfg.extra_border_frames .push("::log"); - - let filter = self.effective_global_filter(); - if filter > log::max_level() { - log::set_max_level(filter); - } - - INIT.call_once(|| { - log::set_boxed_logger(Box::new(Logger::default())).ok(); - }); - } -} - -impl Default for LogIntegration { - fn default() -> Self { - Self { - global_filter: None, - filter: log::LevelFilter::Info, - emit_breadcrumbs: true, - emit_error_events: true, - emit_warning_events: false, - attach_stacktraces: true, - dest_log: None, - sentry_filter: None, - mapper: None, - } - } -} - -impl std::fmt::Debug for LogIntegration { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - #[derive(Debug)] - struct DestLog; - let dest_log = self.dest_log.as_ref().map(|_| DestLog); - - f.debug_struct("LogIntegration") - .field("global_filter", &self.global_filter) - .field("filter", &self.filter) - .field("emit_breadcrumbs", &self.emit_breadcrumbs) - .field("emit_error_events", &self.emit_error_events) - .field("emit_warning_events", &self.emit_warning_events) - .field("attach_stacktraces", &self.attach_stacktraces) - .field("dest_log", &dest_log) - .finish() } } @@ -124,120 +21,4 @@ impl LogIntegration { pub fn new() -> Self { Self::default() } - - /// Initializes an env logger as destination target. - #[cfg(feature = "env_logger")] - pub fn with_env_logger_dest(mut self, logger: Option) -> Self { - let logger = logger - .unwrap_or_else(|| env_logger::Builder::from_env(env_logger::Env::default()).build()); - let filter = logger.filter(); - if self.global_filter.is_none() { - self.global_filter = Some(filter); - } - self.dest_log = Some(Box::new(logger)); - self - } - - /// Sets a custom filter function. - /// - /// The filter classifies how sentry should handle [`Record`]s based on - /// their [`log::Metadata`]. - pub fn filter(mut self, filter: F) -> Self - where - F: Fn(&log::Metadata<'_>) -> LevelFilter + Send + Sync + 'static, - { - self.sentry_filter = Some(Box::new(filter)); - self - } - - /// Sets a custom mapper function. - /// - /// The mapper is responsible for creating either breadcrumbs or events - /// from [`Record`]s. - pub fn mapper(mut self, mapper: M) -> Self - where - M: Fn(&Record<'_>) -> RecordMapping + Send + Sync + 'static, - { - self.mapper = Some(Box::new(mapper)); - self - } - - /// Returns the effective global filter. - /// - /// This is what is set for these logger options when the log level - /// needs to be set globally. This is the greater of `global_filter` - /// and `filter`. - #[inline(always)] - pub(crate) fn effective_global_filter(&self) -> log::LevelFilter { - let filter = if let Some(filter) = self.global_filter { - if filter < self.filter { - self.filter - } else { - filter - } - } else { - self.filter - }; - std::cmp::max(filter, self.issue_filter()) - } - - /// Returns the level for which issues should be created. - /// - /// This is controlled by `emit_error_events` and `emit_warning_events`. - #[inline(always)] - fn issue_filter(&self) -> log::LevelFilter { - if self.emit_warning_events { - log::LevelFilter::Warn - } else if self.emit_error_events { - log::LevelFilter::Error - } else { - log::LevelFilter::Off - } - } - - /// Checks if an issue should be created. - pub(crate) fn create_issue_for_record(&self, record: &Record<'_>) -> bool { - match record.level() { - log::Level::Warn => self.emit_warning_events, - log::Level::Error => self.emit_error_events, - _ => false, - } - } -} - -#[test] -fn test_filters() { - use log::LevelFilter; - - let opt_warn = LogIntegration { - filter: LevelFilter::Warn, - ..Default::default() - }; - assert_eq!(opt_warn.effective_global_filter(), LevelFilter::Warn); - assert_eq!(opt_warn.issue_filter(), LevelFilter::Error); - - let opt_debug = LogIntegration { - global_filter: Some(LevelFilter::Debug), - filter: LevelFilter::Warn, - ..Default::default() - }; - assert_eq!(opt_debug.effective_global_filter(), LevelFilter::Debug); - - let opt_debug_inverse = LogIntegration { - global_filter: Some(LevelFilter::Warn), - filter: LevelFilter::Debug, - ..Default::default() - }; - assert_eq!( - opt_debug_inverse.effective_global_filter(), - LevelFilter::Debug - ); - - let opt_weird = LogIntegration { - filter: LevelFilter::Error, - emit_warning_events: true, - ..Default::default() - }; - assert_eq!(opt_weird.issue_filter(), LevelFilter::Warn); - assert_eq!(opt_weird.effective_global_filter(), LevelFilter::Warn); } diff --git a/sentry-log/src/lib.rs b/sentry-log/src/lib.rs index b5cd0727c..e1431d54a 100644 --- a/sentry-log/src/lib.rs +++ b/sentry-log/src/lib.rs @@ -8,21 +8,19 @@ //! # Examples //! //! ``` -//! let log_integration = sentry_log::LogIntegration::default(); -//! let _sentry = sentry::init(sentry::ClientOptions::default().add_integration(log_integration)); -//! -//! log::info!("Generates a breadcrumb"); -//! ``` -//! -//! Or optionally with env_logger support: -//! -//! ``` //! let mut log_builder = pretty_env_logger::formatted_builder(); //! log_builder.parse_filters("info"); -//! let log_integration = -//! sentry_log::LogIntegration::default().with_env_logger_dest(Some(log_builder.build())); -//! let _sentry = sentry::init(sentry::ClientOptions::default().add_integration(log_integration)); +//! let logger = sentry_log::SentryLogger::with_dest(log_builder.build()); +//! +//! log::set_boxed_logger(Box::new(logger)) +//! .map(|()| log::set_max_level(log::LevelFilter::Info)) +//! .unwrap(); //! +//! let log_integration = sentry_log::LogIntegration::default(); +//! let _sentry = sentry::init(sentry::ClientOptions::new() +//! .add_integration(sentry_log::LogIntegration::new())); +//! +//! log::info!("Generates a breadcrumb"); //! log::error!("Generates an event"); //! ``` @@ -34,6 +32,6 @@ mod converters; mod integration; mod logger; -pub use converters::{breadcrumb_from_record, event_from_record}; +pub use converters::*; pub use integration::LogIntegration; -pub use logger::Logger; +pub use logger::{NoopLogger, SentryLogger}; diff --git a/sentry-log/src/logger.rs b/sentry-log/src/logger.rs index 148a7c669..f019c4f40 100644 --- a/sentry-log/src/logger.rs +++ b/sentry-log/src/logger.rs @@ -1,49 +1,158 @@ -#![allow(deprecated)] +use log::Record; +use sentry_core::protocol::{Breadcrumb, Event}; -use crate::converters::{breadcrumb_from_record, event_from_record}; -use crate::LogIntegration; +use crate::converters::{breadcrumb_from_record, event_from_record, exception_from_record}; + +/// The Action that Sentry should perform for a [`log::Metadata`]. +#[derive(Debug)] +pub enum LevelFilter { + /// Ignore the [`Record`]. + Ignore, + /// Create a [`Breadcrumb`] from this [`Record`]. + Breadcrumb, + /// Create a message [`Event`] from this [`Record`]. + Event, + /// Create an exception [`Event`] from this [`Record`]. + Exception, +} + +/// The type of Data Sentry should ingest for a [`log::Record`]. +#[derive(Debug)] +#[allow(clippy::large_enum_variant)] +pub enum RecordMapping { + /// Ignore the [`Record`] + Ignore, + /// Adds the [`Breadcrumb`] to the Sentry scope. + Breadcrumb(Breadcrumb), + /// Captures the [`Event`] to Sentry. + Event(Event<'static>), +} + +/// The default log filter. +/// +/// By default, an exception event is captured for `error`, a breadcrumb for +/// `warning` and `info`, and `debug` and `trace` logs are ignored. +pub fn default_filter(metadata: &log::Metadata) -> LevelFilter { + match metadata.level() { + log::Level::Error => LevelFilter::Exception, + log::Level::Warn | log::Level::Info => LevelFilter::Breadcrumb, + log::Level::Debug | log::Level::Trace => LevelFilter::Ignore, + } +} + +/// A noop [`log::Log`] that just ignores everything. +#[derive(Debug, Default)] +pub struct NoopLogger; + +impl log::Log for NoopLogger { + fn enabled(&self, metadata: &log::Metadata) -> bool { + let _ = metadata; + false + } + + fn log(&self, record: &log::Record) { + let _ = record; + } + + fn flush(&self) { + todo!() + } +} /// Provides a dispatching logger. -#[derive(Default)] -pub struct Logger; - -impl log::Log for Logger { - fn enabled(&self, md: &log::Metadata<'_>) -> bool { - sentry_core::with_integration(|integration: &LogIntegration, _| { - if let Some(global_filter) = integration.global_filter { - if md.level() > global_filter { - return false; - } +//#[derive(Debug)] +pub struct SentryLogger { + dest: L, + filter: Box) -> LevelFilter + Send + Sync>, + mapper: Option) -> RecordMapping + Send + Sync>>, +} + +impl Default for SentryLogger { + fn default() -> Self { + Self { + dest: NoopLogger, + filter: Box::new(default_filter), + mapper: None, + } + } +} + +impl SentryLogger { + /// Create a new SentryLogger with a [`NoopLogger`] as destination. + pub fn new() -> Self { + Default::default() + } +} + +impl SentryLogger { + /// Create a new SentryLogger wrapping a destination [`log::Log`]. + pub fn with_dest(dest: L) -> Self { + Self { + dest, + filter: Box::new(default_filter), + mapper: None, + } + } + + /// Sets a custom filter function. + /// + /// The filter classifies how sentry should handle [`Record`]s based on + /// their [`log::Metadata`]. + pub fn filter(mut self, filter: F) -> Self + where + F: Fn(&log::Metadata<'_>) -> LevelFilter + Send + Sync + 'static, + { + self.filter = Box::new(filter); + self + } + + /// Sets a custom mapper function. + /// + /// The mapper is responsible for creating either breadcrumbs or events + /// from [`Record`]s. + pub fn mapper(mut self, mapper: M) -> Self + where + M: Fn(&Record<'_>) -> RecordMapping + Send + Sync + 'static, + { + self.mapper = Some(Box::new(mapper)); + self + } +} + +impl log::Log for SentryLogger { + fn enabled(&self, metadata: &log::Metadata<'_>) -> bool { + self.dest.enabled(metadata) + || match (self.filter)(metadata) { + LevelFilter::Ignore => false, + _ => true, } - md.level() <= integration.filter - || integration - .dest_log - .as_ref() - .map_or(false, |x| x.enabled(md)) - }) } fn log(&self, record: &log::Record<'_>) { - sentry_core::with_integration(|integration: &LogIntegration, hub| { - if integration.create_issue_for_record(record) { - hub.capture_event(event_from_record(record, integration.attach_stacktraces)); - } - if integration.emit_breadcrumbs && record.level() <= integration.filter { - sentry_core::add_breadcrumb(|| breadcrumb_from_record(record)); - } - if let Some(ref log) = integration.dest_log { - if log.enabled(record.metadata()) { - log.log(record); + let item: RecordMapping = match &self.mapper { + Some(mapper) => mapper(record), + None => match (self.filter)(record.metadata()) { + LevelFilter::Ignore => RecordMapping::Ignore, + LevelFilter::Breadcrumb => { + RecordMapping::Breadcrumb(breadcrumb_from_record(record)) } + LevelFilter::Event => RecordMapping::Event(event_from_record(record)), + LevelFilter::Exception => RecordMapping::Event(exception_from_record(record)), + }, + }; + + match item { + RecordMapping::Ignore => {} + RecordMapping::Breadcrumb(b) => sentry_core::add_breadcrumb(b), + RecordMapping::Event(e) => { + sentry_core::capture_event(e); } - }) + } + + self.dest.log(record) } fn flush(&self) { - sentry_core::with_integration(|integration: &LogIntegration, _| { - if let Some(ref log) = integration.dest_log { - log.flush(); - } - }) + self.dest.flush() } } diff --git a/sentry-slog/src/drain.rs b/sentry-slog/src/drain.rs index 869344bd9..4ffca5e61 100644 --- a/sentry-slog/src/drain.rs +++ b/sentry-slog/src/drain.rs @@ -30,8 +30,8 @@ pub enum RecordMapping { /// The default slog filter. /// /// By default, an exception event is captured for `critical` logs, -/// a regular event for `error` and `warning` logs, and breadcrumbs for -/// everything else. +/// a regular event for `error`, a breadcrumb for `warning` and `info`, and +/// `debug` and `trace` logs are ignored. pub fn default_filter(level: slog::Level) -> LevelFilter { match level { slog::Level::Critical => LevelFilter::Exception, @@ -47,6 +47,9 @@ pub struct SentryDrain { mapper: Option RecordMapping + Send + Sync>>, } +impl std::panic::RefUnwindSafe for SentryDrain {} +impl std::panic::UnwindSafe for SentryDrain {} + impl SentryDrain { /// Creates a new `SentryDrain`, wrapping a `slog::Drain`. pub fn new(drain: D) -> Self { diff --git a/sentry-slog/src/lib.rs b/sentry-slog/src/lib.rs index 74f1514a4..95e566f8c 100644 --- a/sentry-slog/src/lib.rs +++ b/sentry-slog/src/lib.rs @@ -55,7 +55,7 @@ //! }) //! .mapper(|record, kv| match record.level() { //! slog::Level::Critical | slog::Level::Error => -//! RecordMapping::Event(exception_from_record(record, kv))), +//! RecordMapping::Event(exception_from_record(record, kv)), //! _ => RecordMapping::Ignore, //! }); //! ``` diff --git a/sentry/Cargo.toml b/sentry/Cargo.toml index c5c460b80..9290ff812 100644 --- a/sentry/Cargo.toml +++ b/sentry/Cargo.toml @@ -28,7 +28,6 @@ debug-images = ["sentry-debug-images"] error-chain = ["sentry-error-chain"] failure = ["sentry-failure"] log = ["sentry-log"] -env_logger = ["sentry-log", "sentry-log/env_logger"] slog = ["sentry-slog"] # other features test = ["sentry-core/test"] @@ -65,8 +64,10 @@ serde_json = { version = "1.0.48", optional = true } sentry-anyhow = { version = "0.20.1", path = "../sentry-anyhow" } sentry-error-chain = { version = "0.20.1", path = "../sentry-error-chain" } sentry-failure = { version = "0.20.1", path = "../sentry-failure" } -sentry-log = { version = "0.20.1", path = "../sentry-log", features = ["env_logger"] } +sentry-log = { version = "0.20.1", path = "../sentry-log" } +sentry-slog = { version = "0.20.1", path = "../sentry-slog" } log_ = { package = "log", version = "0.4.8", features = ["std"] } +slog_ = { package = "slog", version = "2.5.2" } failure_derive = "0.1.6" actix-web = { version = "0.7.19", default-features = false } tokio = { version = "0.2", features = ["macros"] } diff --git a/sentry/examples/log-demo.rs b/sentry/examples/log-demo.rs index 80e450200..e54fee32b 100644 --- a/sentry/examples/log-demo.rs +++ b/sentry/examples/log-demo.rs @@ -1,17 +1,15 @@ -use log_::{debug, error, info, warn}; +use log::{debug, error, info, warn}; +use log_ as log; fn main() { - let mut log_builder = pretty_env_logger::formatted_builder(); - log_builder.parse_filters("info"); - let log_integration = - sentry_log::LogIntegration::default().with_env_logger_dest(Some(log_builder.build())); + init_log(); let _sentry = sentry::init( sentry::ClientOptions { release: sentry::release_name!(), ..Default::default() } - .add_integration(log_integration), + .add_integration(sentry_log::LogIntegration::new()), ); debug!("System is booting"); @@ -19,3 +17,13 @@ fn main() { warn!("System is warning"); error!("Holy shit everything is on fire!"); } + +fn init_log() { + let mut log_builder = pretty_env_logger::formatted_builder(); + log_builder.parse_filters("info"); + let logger = sentry_log::SentryLogger::with_dest(log_builder.build()); + + log::set_boxed_logger(Box::new(logger)) + .map(|()| log::set_max_level(log::LevelFilter::Info)) + .unwrap(); +} diff --git a/sentry/examples/thread-demo.rs b/sentry/examples/thread-demo.rs index 7b3d35916..642acbc8f 100644 --- a/sentry/examples/thread-demo.rs +++ b/sentry/examples/thread-demo.rs @@ -3,12 +3,7 @@ use std::sync::Arc; use std::thread; fn main() { - let mut log_builder = pretty_env_logger::formatted_builder(); - log_builder.parse_filters("info"); - let log_integration = sentry_log::LogIntegration { - dest_log: Some(Box::new(log_builder.build())), - ..Default::default() - }; + init_log(); // this initializes sentry. It also gives the thread that calls this some // special behavior in that all other threads spawned will get a hub based on @@ -18,7 +13,7 @@ fn main() { release: sentry::release_name!(), ..Default::default() } - .add_integration(log_integration), + .add_integration(sentry_log::LogIntegration::new()), ); // the log integration sends to Hub::current() @@ -53,3 +48,13 @@ fn main() { .join() .unwrap(); } + +fn init_log() { + let mut log_builder = pretty_env_logger::formatted_builder(); + log_builder.parse_filters("info"); + let logger = sentry_log::SentryLogger::with_dest(log_builder.build()); + + log::set_boxed_logger(Box::new(logger)) + .map(|()| log::set_max_level(log::LevelFilter::Info)) + .unwrap(); +} diff --git a/sentry/tests/test_log.rs b/sentry/tests/test_log.rs index fa755c3e6..186f47e5f 100644 --- a/sentry/tests/test_log.rs +++ b/sentry/tests/test_log.rs @@ -1,10 +1,15 @@ #![cfg(feature = "test")] use log_ as log; +use slog_ as slog; #[test] fn test_log() { - let log_integration = sentry_log::LogIntegration::default(); + let logger = sentry_log::SentryLogger::new(); + + log::set_boxed_logger(Box::new(logger)) + .map(|()| log::set_max_level(log::LevelFilter::Info)) + .unwrap(); let events = sentry::test::with_captured_events_options( || { @@ -15,7 +20,33 @@ fn test_log() { log::info!("Hello World!"); log::error!("Shit's on fire yo"); }, - sentry::ClientOptions::default().add_integration(log_integration), + sentry::ClientOptions::new().add_integration(sentry_log::LogIntegration::new()), + ); + + assert_eq!(events.len(), 1); + let event = events.into_iter().next().unwrap(); + + assert_eq!(event.tags["worker"], "worker1"); + assert_eq!(event.level, sentry::Level::Error); + assert_eq!(event.breadcrumbs[0].level, sentry::Level::Info); + assert_eq!(event.breadcrumbs[0].message, Some("Hello World!".into())); +} + +#[test] +fn test_slog() { + let drain = sentry_slog::SentryDrain::new(slog::Discard); + let root = slog::Logger::root(drain, slog::o!()); + + let events = sentry::test::with_captured_events_options( + || { + sentry::configure_scope(|scope| { + scope.set_tag("worker", "worker1"); + }); + + slog::info!(root, "Hello World!"); + slog::error!(root, "Shit's on fire yo"); + }, + sentry::ClientOptions::new().add_integration(sentry_slog::SlogIntegration::new()), ); assert_eq!(events.len(), 1); From fa243f7ff82fab45173af90c948aa792bee923e3 Mon Sep 17 00:00:00 2001 From: Arpad Borsos Date: Thu, 24 Sep 2020 11:37:14 +0200 Subject: [PATCH 3/6] rm backtrace dependency --- sentry-log/Cargo.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/sentry-log/Cargo.toml b/sentry-log/Cargo.toml index 4d7cad08f..397aa0d10 100644 --- a/sentry-log/Cargo.toml +++ b/sentry-log/Cargo.toml @@ -13,7 +13,6 @@ edition = "2018" [dependencies] sentry-core = { version = "0.20.1", path = "../sentry-core" } -sentry-backtrace = { version = "0.20.1", path = "../sentry-backtrace" } log = { version = "0.4.8", features = ["std"] } [dev-dependencies] From b8e91f70f7c8e534c362461873fe4c82688f04cc Mon Sep 17 00:00:00 2001 From: Arpad Borsos Date: Fri, 2 Oct 2020 14:38:22 +0200 Subject: [PATCH 4/6] review --- CHANGELOG.md | 2 ++ sentry-log/src/lib.rs | 16 +++++++++++++++- sentry-log/src/logger.rs | 26 ++++++++++++-------------- 3 files changed, 29 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 00e690d4d..59d172332 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,8 @@ - Removed all deprecated exports and deprecated feature flags. - The `failure` integration / feature is now off-by-default along with its deprecation. +- The `log` and `slog` integrations were re-designed, they now offer types that wrap a `log::Log` or `slog::Drain` and forward log events to the currently active sentry `Hub` based on an optional filter and an optional mapper. +- The new `log` integration will not implicitly call `log::set_max_level_filter` anymore, and users need to do so manually. **Deprecations**: diff --git a/sentry-log/src/lib.rs b/sentry-log/src/lib.rs index e1431d54a..87ab711f4 100644 --- a/sentry-log/src/lib.rs +++ b/sentry-log/src/lib.rs @@ -23,6 +23,20 @@ //! log::info!("Generates a breadcrumb"); //! log::error!("Generates an event"); //! ``` +//! +//! Or one might also set an explicit filter, to customize how to treat log +//! records: +//! +//! ``` +//! use sentry_log::LogFilter; +//! +//! let logger = sentry_log::SentryLogger::new().filter(|md| { +//! match md.level() { +//! log::Level::Error => LogFilter::Event, +//! _ => LogFilter::Ignore, +//! } +//! }); +//! ``` #![doc(html_favicon_url = "https://sentry-brand.storage.googleapis.com/favicon.ico")] #![doc(html_logo_url = "https://sentry-brand.storage.googleapis.com/sentry-glyph-black.png")] @@ -34,4 +48,4 @@ mod logger; pub use converters::*; pub use integration::LogIntegration; -pub use logger::{NoopLogger, SentryLogger}; +pub use logger::*; diff --git a/sentry-log/src/logger.rs b/sentry-log/src/logger.rs index f019c4f40..1cc2f9ffd 100644 --- a/sentry-log/src/logger.rs +++ b/sentry-log/src/logger.rs @@ -5,7 +5,7 @@ use crate::converters::{breadcrumb_from_record, event_from_record, exception_fro /// The Action that Sentry should perform for a [`log::Metadata`]. #[derive(Debug)] -pub enum LevelFilter { +pub enum LogFilter { /// Ignore the [`Record`]. Ignore, /// Create a [`Breadcrumb`] from this [`Record`]. @@ -32,11 +32,11 @@ pub enum RecordMapping { /// /// By default, an exception event is captured for `error`, a breadcrumb for /// `warning` and `info`, and `debug` and `trace` logs are ignored. -pub fn default_filter(metadata: &log::Metadata) -> LevelFilter { +pub fn default_filter(metadata: &log::Metadata) -> LogFilter { match metadata.level() { - log::Level::Error => LevelFilter::Exception, - log::Level::Warn | log::Level::Info => LevelFilter::Breadcrumb, - log::Level::Debug | log::Level::Trace => LevelFilter::Ignore, + log::Level::Error => LogFilter::Exception, + log::Level::Warn | log::Level::Info => LogFilter::Breadcrumb, + log::Level::Debug | log::Level::Trace => LogFilter::Ignore, } } @@ -63,7 +63,7 @@ impl log::Log for NoopLogger { //#[derive(Debug)] pub struct SentryLogger { dest: L, - filter: Box) -> LevelFilter + Send + Sync>, + filter: Box) -> LogFilter + Send + Sync>, mapper: Option) -> RecordMapping + Send + Sync>>, } @@ -100,7 +100,7 @@ impl SentryLogger { /// their [`log::Metadata`]. pub fn filter(mut self, filter: F) -> Self where - F: Fn(&log::Metadata<'_>) -> LevelFilter + Send + Sync + 'static, + F: Fn(&log::Metadata<'_>) -> LogFilter + Send + Sync + 'static, { self.filter = Box::new(filter); self @@ -123,7 +123,7 @@ impl log::Log for SentryLogger { fn enabled(&self, metadata: &log::Metadata<'_>) -> bool { self.dest.enabled(metadata) || match (self.filter)(metadata) { - LevelFilter::Ignore => false, + LogFilter::Ignore => false, _ => true, } } @@ -132,12 +132,10 @@ impl log::Log for SentryLogger { let item: RecordMapping = match &self.mapper { Some(mapper) => mapper(record), None => match (self.filter)(record.metadata()) { - LevelFilter::Ignore => RecordMapping::Ignore, - LevelFilter::Breadcrumb => { - RecordMapping::Breadcrumb(breadcrumb_from_record(record)) - } - LevelFilter::Event => RecordMapping::Event(event_from_record(record)), - LevelFilter::Exception => RecordMapping::Event(exception_from_record(record)), + LogFilter::Ignore => RecordMapping::Ignore, + LogFilter::Breadcrumb => RecordMapping::Breadcrumb(breadcrumb_from_record(record)), + LogFilter::Event => RecordMapping::Event(event_from_record(record)), + LogFilter::Exception => RecordMapping::Event(exception_from_record(record)), }, }; From 00afd06ece81a821c7d2554ae6255bbab92c7474 Mon Sep 17 00:00:00 2001 From: Arpad Borsos Date: Fri, 9 Oct 2020 12:34:21 +0200 Subject: [PATCH 5/6] allow clippy lint --- sentry-log/src/lib.rs | 1 + sentry-slog/src/lib.rs | 1 + 2 files changed, 2 insertions(+) diff --git a/sentry-log/src/lib.rs b/sentry-log/src/lib.rs index 87ab711f4..1e50e2d1e 100644 --- a/sentry-log/src/lib.rs +++ b/sentry-log/src/lib.rs @@ -41,6 +41,7 @@ #![doc(html_favicon_url = "https://sentry-brand.storage.googleapis.com/favicon.ico")] #![doc(html_logo_url = "https://sentry-brand.storage.googleapis.com/sentry-glyph-black.png")] #![warn(missing_docs)] +#![allow(clippy::match_like_matches_macro)] mod converters; mod integration; diff --git a/sentry-slog/src/lib.rs b/sentry-slog/src/lib.rs index 95e566f8c..4f4bdef15 100644 --- a/sentry-slog/src/lib.rs +++ b/sentry-slog/src/lib.rs @@ -69,6 +69,7 @@ #![doc(html_logo_url = "https://sentry-brand.storage.googleapis.com/sentry-glyph-black.png")] #![warn(missing_docs)] #![deny(unsafe_code)] +#![allow(clippy::match_like_matches_macro)] mod converters; mod drain; From b5dac05f53cb85b9624335647f7308c68044317e Mon Sep 17 00:00:00 2001 From: Arpad Borsos Date: Fri, 16 Oct 2020 12:03:26 +0200 Subject: [PATCH 6/6] review --- sentry-anyhow/src/lib.rs | 3 +-- sentry-log/src/lib.rs | 18 ++++++++---------- sentry-log/src/logger.rs | 4 ++-- sentry-slog/src/drain.rs | 15 +++++++++++++-- sentry-slog/src/lib.rs | 5 +++-- sentry-types/src/protocol/envelope.rs | 6 +++--- 6 files changed, 30 insertions(+), 21 deletions(-) diff --git a/sentry-anyhow/src/lib.rs b/sentry-anyhow/src/lib.rs index 140d99f9f..d9e17ce2e 100644 --- a/sentry-anyhow/src/lib.rs +++ b/sentry-anyhow/src/lib.rs @@ -9,8 +9,7 @@ //! Err(anyhow::anyhow!("some kind of error")) //! } //! -//! let _sentry = -//! sentry::init(sentry::ClientOptions::new().add_integration(AnyhowIntegration)); +//! let _sentry = sentry::init(sentry::ClientOptions::new().add_integration(AnyhowIntegration)); //! //! if let Err(err) = function_that_might_fail() { //! capture_anyhow(&err); diff --git a/sentry-log/src/lib.rs b/sentry-log/src/lib.rs index 1e50e2d1e..b8c87dc31 100644 --- a/sentry-log/src/lib.rs +++ b/sentry-log/src/lib.rs @@ -12,13 +12,13 @@ //! log_builder.parse_filters("info"); //! let logger = sentry_log::SentryLogger::with_dest(log_builder.build()); //! -//! log::set_boxed_logger(Box::new(logger)) -//! .map(|()| log::set_max_level(log::LevelFilter::Info)) -//! .unwrap(); +//! log::set_boxed_logger(Box::new(logger)).unwrap(); +//! log::set_max_level(log::LevelFilter::Info); //! //! let log_integration = sentry_log::LogIntegration::default(); -//! let _sentry = sentry::init(sentry::ClientOptions::new() -//! .add_integration(sentry_log::LogIntegration::new())); +//! let _sentry = sentry::init( +//! sentry::ClientOptions::new().add_integration(sentry_log::LogIntegration::new()), +//! ); //! //! log::info!("Generates a breadcrumb"); //! log::error!("Generates an event"); @@ -30,11 +30,9 @@ //! ``` //! use sentry_log::LogFilter; //! -//! let logger = sentry_log::SentryLogger::new().filter(|md| { -//! match md.level() { -//! log::Level::Error => LogFilter::Event, -//! _ => LogFilter::Ignore, -//! } +//! let logger = sentry_log::SentryLogger::new().filter(|md| match md.level() { +//! log::Level::Error => LogFilter::Event, +//! _ => LogFilter::Ignore, //! }); //! ``` diff --git a/sentry-log/src/logger.rs b/sentry-log/src/logger.rs index 1cc2f9ffd..fd9071fe2 100644 --- a/sentry-log/src/logger.rs +++ b/sentry-log/src/logger.rs @@ -3,7 +3,7 @@ use sentry_core::protocol::{Breadcrumb, Event}; use crate::converters::{breadcrumb_from_record, event_from_record, exception_from_record}; -/// The Action that Sentry should perform for a [`log::Metadata`]. +/// The action that Sentry should perform for a [`log::Metadata`]. #[derive(Debug)] pub enum LogFilter { /// Ignore the [`Record`]. @@ -20,7 +20,7 @@ pub enum LogFilter { #[derive(Debug)] #[allow(clippy::large_enum_variant)] pub enum RecordMapping { - /// Ignore the [`Record`] + /// Ignore the [`Record`]. Ignore, /// Adds the [`Breadcrumb`] to the Sentry scope. Breadcrumb(Breadcrumb), diff --git a/sentry-slog/src/drain.rs b/sentry-slog/src/drain.rs index 4ffca5e61..c64ff8c87 100644 --- a/sentry-slog/src/drain.rs +++ b/sentry-slog/src/drain.rs @@ -3,7 +3,7 @@ use slog::{Drain, OwnedKVList, Record}; use crate::{breadcrumb_from_record, event_from_record, exception_from_record}; -/// The Action that Sentry should perform for a [`slog::Level`]. +/// The action that Sentry should perform for a [`slog::Level`]. #[derive(Debug)] pub enum LevelFilter { /// Ignore the [`Record`]. @@ -19,7 +19,7 @@ pub enum LevelFilter { /// The type of Data Sentry should ingest for a [`slog::Record`]. #[allow(clippy::large_enum_variant)] pub enum RecordMapping { - /// Ignore the [`Record`] + /// Ignore the [`Record`]. Ignore, /// Adds the [`Breadcrumb`] to the Sentry scope. Breadcrumb(Breadcrumb), @@ -76,6 +76,17 @@ impl SentryDrain { /// /// The mapper is responsible for creating either breadcrumbs or events /// from [`Record`]s. + /// + /// # Examples + /// + /// ``` + /// use sentry_slog::{breadcrumb_from_record, RecordMapping, SentryDrain}; + /// + /// let drain = SentryDrain::new(slog::Discard).mapper(|record, kv| match record.level() { + /// slog::Level::Trace => RecordMapping::Ignore, + /// _ => RecordMapping::Breadcrumb(breadcrumb_from_record(record, kv)), + /// }); + /// ``` pub fn mapper(mut self, mapper: M) -> Self where M: Fn(&Record, &OwnedKVList) -> RecordMapping + Send + Sync + 'static, diff --git a/sentry-slog/src/lib.rs b/sentry-slog/src/lib.rs index 4f4bdef15..10ed86eb9 100644 --- a/sentry-slog/src/lib.rs +++ b/sentry-slog/src/lib.rs @@ -54,8 +54,9 @@ //! _ => LevelFilter::Ignore, //! }) //! .mapper(|record, kv| match record.level() { -//! slog::Level::Critical | slog::Level::Error => -//! RecordMapping::Event(exception_from_record(record, kv)), +//! slog::Level::Critical | slog::Level::Error => { +//! RecordMapping::Event(exception_from_record(record, kv)) +//! } //! _ => RecordMapping::Ignore, //! }); //! ``` diff --git a/sentry-types/src/protocol/envelope.rs b/sentry-types/src/protocol/envelope.rs index 3e0dc012a..782265a25 100644 --- a/sentry-types/src/protocol/envelope.rs +++ b/sentry-types/src/protocol/envelope.rs @@ -22,9 +22,9 @@ pub enum EnvelopeItem { /// See the [Session Item documentation](https://develop.sentry.dev/sdk/envelopes/#session) /// for more details. SessionUpdate(SessionUpdate<'static>), - // TODO: - // * Attachment, - // etc… + /* TODO: + * * Attachment, + * etc… */ } impl From> for EnvelopeItem {