11use std:: collections:: BTreeMap ;
22use std:: error:: Error ;
33
4- use sentry_core:: protocol:: { Event , Exception , Value } ;
4+ use sentry_core:: protocol:: { Event , Exception , Mechanism , Thread , Value } ;
55use sentry_core:: { event_from_error, Breadcrumb , Level } ;
66use tracing_core:: field:: { Field , Visit } ;
77use tracing_core:: { span, Subscriber } ;
@@ -18,6 +18,16 @@ fn convert_tracing_level(level: &tracing_core::Level) -> Level {
1818 }
1919}
2020
21+ fn level_to_exception_type ( level : & tracing_core:: Level ) -> & ' static str {
22+ match * level {
23+ tracing_core:: Level :: TRACE => "tracing::trace!" ,
24+ tracing_core:: Level :: DEBUG => "tracing::debug!" ,
25+ tracing_core:: Level :: INFO => "tracing::info!" ,
26+ tracing_core:: Level :: WARN => "tracing::warn!" ,
27+ tracing_core:: Level :: ERROR => "tracing::error!" ,
28+ }
29+ }
30+
2131/// Extracts the message and metadata from an event
2232fn extract_event_data ( event : & tracing_core:: Event ) -> ( Option < String > , FieldVisitor ) {
2333 // Find message of the event, if any
@@ -106,9 +116,32 @@ pub fn breadcrumb_from_event(event: &tracing_core::Event) -> Breadcrumb {
106116 }
107117}
108118
119+ fn tags_from_event ( fields : & mut BTreeMap < String , Value > ) -> BTreeMap < String , String > {
120+ let mut tags = BTreeMap :: new ( ) ;
121+
122+ fields. retain ( |key, value| {
123+ let Some ( key) = key. strip_prefix ( "tags." ) else { return true } ;
124+ let string = match value {
125+ Value :: Bool ( b) => b. to_string ( ) ,
126+ Value :: Number ( n) => n. to_string ( ) ,
127+ Value :: String ( s) => std:: mem:: take ( s) ,
128+ // remove null entries since empty tags are not allowed
129+ Value :: Null => return false ,
130+ // keep entries that cannot be represented as simple string
131+ Value :: Array ( _) | Value :: Object ( _) => return true ,
132+ } ;
133+
134+ tags. insert ( key. to_owned ( ) , string) ;
135+
136+ false
137+ } ) ;
138+
139+ tags
140+ }
141+
109142fn contexts_from_event (
110143 event : & tracing_core:: Event ,
111- event_tags : BTreeMap < String , Value > ,
144+ fields : BTreeMap < String , Value > ,
112145) -> BTreeMap < String , sentry_core:: protocol:: Context > {
113146 let event_meta = event. metadata ( ) ;
114147 let mut location_map = BTreeMap :: new ( ) ;
@@ -123,10 +156,10 @@ fn contexts_from_event(
123156 }
124157
125158 let mut context = BTreeMap :: new ( ) ;
126- if !event_tags . is_empty ( ) {
159+ if !fields . is_empty ( ) {
127160 context. insert (
128- "Rust Tracing Tags " . to_string ( ) ,
129- sentry_core:: protocol:: Context :: Other ( event_tags ) ,
161+ "Rust Tracing Fields " . to_string ( ) ,
162+ sentry_core:: protocol:: Context :: Other ( fields ) ,
130163 ) ;
131164 }
132165 if !location_map. is_empty ( ) {
@@ -143,12 +176,13 @@ pub fn event_from_event<S>(event: &tracing_core::Event, _ctx: Context<S>) -> Eve
143176where
144177 S : Subscriber + for < ' a > LookupSpan < ' a > ,
145178{
146- let ( message, visitor) = extract_event_data ( event) ;
179+ let ( message, mut visitor) = extract_event_data ( event) ;
147180
148181 Event {
149182 logger : Some ( event. metadata ( ) . target ( ) . to_owned ( ) ) ,
150183 level : convert_tracing_level ( event. metadata ( ) . level ( ) ) ,
151184 message,
185+ tags : tags_from_event ( & mut visitor. json_values ) ,
152186 contexts : contexts_from_event ( event, visitor. json_values ) ,
153187 ..Default :: default ( )
154188 }
@@ -159,17 +193,61 @@ pub fn exception_from_event<S>(event: &tracing_core::Event, _ctx: Context<S>) ->
159193where
160194 S : Subscriber + for < ' a > LookupSpan < ' a > ,
161195{
162- // TODO: Exception records in Sentry need a valid type, value and full stack trace to support
196+ // Exception records in Sentry need a valid type, value and full stack trace to support
163197 // proper grouping and issue metadata generation. tracing_core::Record does not contain sufficient
164198 // information for this. However, it may contain a serialized error which we can parse to emit
165199 // an exception record.
166- let ( message, visitor) = extract_event_data ( event) ;
200+ let ( mut message, visitor) = extract_event_data ( event) ;
201+ let FieldVisitor {
202+ mut exceptions,
203+ mut json_values,
204+ } = visitor;
205+
206+ // If there are both a message and an exception, then add the message as synthetic wrapper
207+ // around the exception to support proper grouping. If configured, also add the current stack
208+ // trace to this exception directly, since it points to the place where the exception is
209+ // captured.
210+ if !exceptions. is_empty ( ) && message. is_some ( ) {
211+ #[ allow( unused_mut) ]
212+ let mut thread = Thread :: default ( ) ;
213+
214+ #[ cfg( feature = "backtrace" ) ]
215+ if let Some ( client) = sentry_core:: Hub :: current ( ) . client ( ) {
216+ if client. options ( ) . attach_stacktrace {
217+ thread = sentry_backtrace:: current_thread ( true ) ;
218+ }
219+ }
220+
221+ let exception = Exception {
222+ ty : level_to_exception_type ( event. metadata ( ) . level ( ) ) . to_owned ( ) ,
223+ value : message. take ( ) ,
224+ module : event. metadata ( ) . module_path ( ) . map ( str:: to_owned) ,
225+ stacktrace : thread. stacktrace ,
226+ raw_stacktrace : thread. raw_stacktrace ,
227+ thread_id : thread. id ,
228+ mechanism : Some ( Mechanism {
229+ synthetic : Some ( true ) ,
230+ ..Mechanism :: default ( )
231+ } ) ,
232+ } ;
233+
234+ exceptions. push ( exception) ;
235+ }
236+
237+ if let Some ( exception) = exceptions. last_mut ( ) {
238+ exception
239+ . mechanism
240+ . get_or_insert_with ( Mechanism :: default)
241+ . ty = "tracing" . to_owned ( ) ;
242+ }
243+
167244 Event {
168245 logger : Some ( event. metadata ( ) . target ( ) . to_owned ( ) ) ,
169246 level : convert_tracing_level ( event. metadata ( ) . level ( ) ) ,
170247 message,
171- exception : visitor. exceptions . into ( ) ,
172- contexts : contexts_from_event ( event, visitor. json_values ) ,
248+ exception : exceptions. into ( ) ,
249+ tags : tags_from_event ( & mut json_values) ,
250+ contexts : contexts_from_event ( event, json_values) ,
173251 ..Default :: default ( )
174252 }
175253}
0 commit comments