11use log:: Record ;
22use sentry_core:: protocol:: { Breadcrumb , Event } ;
33
4+ use bitflags:: bitflags;
5+
46#[ cfg( feature = "logs" ) ]
57use crate :: converters:: log_from_record;
68use crate :: converters:: { breadcrumb_from_record, event_from_record, exception_from_record} ;
79
8- /// The action that Sentry should perform for a [`log::Metadata`].
9- #[ derive( Debug ) ]
10- pub enum LogFilter {
11- /// Ignore the [`Record`].
12- Ignore ,
13- /// Create a [`Breadcrumb`] from this [`Record`].
14- Breadcrumb ,
15- /// Create a message [`Event`] from this [`Record`].
16- Event ,
17- /// Create an exception [`Event`] from this [`Record`].
18- Exception ,
19- /// Create a [`sentry_core::protocol::Log`] from this [`Record`].
20- #[ cfg( feature = "logs" ) ]
21- Log ,
10+ bitflags ! {
11+ /// The action that Sentry should perform for a [`log::Metadata`].
12+ #[ derive( Debug , Clone , Copy , PartialEq , Eq ) ]
13+ pub struct LogFilter : u32 {
14+ /// Ignore the [`Record`].
15+ const Ignore = 0b0000 ;
16+ /// Create a [`Breadcrumb`] from this [`Record`].
17+ const Breadcrumb = 0b0001 ;
18+ /// Create a message [`Event`] from this [`Record`].
19+ const Event = 0b0010 ;
20+ /// Create an exception [`Event`] from this [`Record`].
21+ const Exception = 0b0100 ;
22+ /// Create a [`sentry_core::protocol::Log`] from this [`Record`].
23+ #[ cfg( feature = "logs" ) ]
24+ const Log = 0b1000 ;
25+ }
2226}
2327
2428/// The type of Data Sentry should ingest for a [`log::Record`].
@@ -36,6 +40,12 @@ pub enum RecordMapping {
3640 Log ( sentry_core:: protocol:: Log ) ,
3741}
3842
43+ impl From < RecordMapping > for Vec < RecordMapping > {
44+ fn from ( mapping : RecordMapping ) -> Self {
45+ vec ! [ mapping]
46+ }
47+ }
48+
3949/// The default log filter.
4050///
4151/// By default, an exception event is captured for `error`, a breadcrumb for
@@ -73,7 +83,7 @@ pub struct SentryLogger<L: log::Log> {
7383 dest : L ,
7484 filter : Box < dyn Fn ( & log:: Metadata < ' _ > ) -> LogFilter + Send + Sync > ,
7585 #[ allow( clippy:: type_complexity) ]
76- mapper : Option < Box < dyn Fn ( & Record < ' _ > ) -> RecordMapping + Send + Sync > > ,
86+ mapper : Option < Box < dyn Fn ( & Record < ' _ > ) -> Vec < RecordMapping > + Send + Sync > > ,
7787}
7888
7989impl Default for SentryLogger < NoopLogger > {
@@ -119,43 +129,59 @@ impl<L: log::Log> SentryLogger<L> {
119129 /// Sets a custom mapper function.
120130 ///
121131 /// The mapper is responsible for creating either breadcrumbs or events
122- /// from [`Record`]s.
132+ /// from [`Record`]s. It can return either a single [`RecordMapping`] or
133+ /// a `Vec<RecordMapping>` to send multiple items to Sentry from one log record.
123134 #[ must_use]
124- pub fn mapper < M > ( mut self , mapper : M ) -> Self
135+ pub fn mapper < M , T > ( mut self , mapper : M ) -> Self
125136 where
126- M : Fn ( & Record < ' _ > ) -> RecordMapping + Send + Sync + ' static ,
137+ M : Fn ( & Record < ' _ > ) -> T + Send + Sync + ' static ,
138+ T : Into < Vec < RecordMapping > > ,
127139 {
128- self . mapper = Some ( Box :: new ( mapper) ) ;
140+ self . mapper = Some ( Box :: new ( move |record| mapper ( record ) . into ( ) ) ) ;
129141 self
130142 }
131143}
132144
133145impl < L : log:: Log > log:: Log for SentryLogger < L > {
134146 fn enabled ( & self , metadata : & log:: Metadata < ' _ > ) -> bool {
135- self . dest . enabled ( metadata) || !matches ! ( ( self . filter) ( metadata) , LogFilter :: Ignore )
147+ self . dest . enabled ( metadata) || !( ( self . filter ) ( metadata) == LogFilter :: Ignore )
136148 }
137149
138150 fn log ( & self , record : & log:: Record < ' _ > ) {
139- let item : RecordMapping = match & self . mapper {
151+ let items = match & self . mapper {
140152 Some ( mapper) => mapper ( record) ,
141- None => match ( self . filter ) ( record. metadata ( ) ) {
142- LogFilter :: Ignore => RecordMapping :: Ignore ,
143- LogFilter :: Breadcrumb => RecordMapping :: Breadcrumb ( breadcrumb_from_record ( record) ) ,
144- LogFilter :: Event => RecordMapping :: Event ( event_from_record ( record) ) ,
145- LogFilter :: Exception => RecordMapping :: Event ( exception_from_record ( record) ) ,
153+ None => {
154+ let filter = ( self . filter ) ( record. metadata ( ) ) ;
155+ let mut items = vec ! [ ] ;
156+ if filter. contains ( LogFilter :: Breadcrumb ) {
157+ items. push ( RecordMapping :: Breadcrumb ( breadcrumb_from_record ( record) ) ) ;
158+ }
159+ if filter. contains ( LogFilter :: Event ) {
160+ items. push ( RecordMapping :: Event ( event_from_record ( record) ) ) ;
161+ }
162+ if filter. contains ( LogFilter :: Exception ) {
163+ items. push ( RecordMapping :: Event ( exception_from_record ( record) ) ) ;
164+ }
146165 #[ cfg( feature = "logs" ) ]
147- LogFilter :: Log => RecordMapping :: Log ( log_from_record ( record) ) ,
148- } ,
166+ if filter. contains ( LogFilter :: Log ) {
167+ items. push ( RecordMapping :: Log ( log_from_record ( record) ) ) ;
168+ }
169+ items
170+ }
149171 } ;
150172
151- match item {
152- RecordMapping :: Ignore => { }
153- RecordMapping :: Breadcrumb ( b) => sentry_core:: add_breadcrumb ( b) ,
154- RecordMapping :: Event ( e) => {
155- sentry_core:: capture_event ( e) ;
173+ for mapping in items {
174+ match mapping {
175+ RecordMapping :: Ignore => { }
176+ RecordMapping :: Breadcrumb ( breadcrumb) => sentry_core:: add_breadcrumb ( breadcrumb) ,
177+ RecordMapping :: Event ( event) => {
178+ sentry_core:: capture_event ( event) ;
179+ }
180+ #[ cfg( feature = "logs" ) ]
181+ RecordMapping :: Log ( log) => {
182+ sentry_core:: Hub :: with_active ( |hub| hub. capture_log ( log) )
183+ }
156184 }
157- #[ cfg( feature = "logs" ) ]
158- RecordMapping :: Log ( log) => sentry_core:: Hub :: with_active ( |hub| hub. capture_log ( log) ) ,
159185 }
160186
161187 self . dest . log ( record)
0 commit comments