22 * License, v. 2.0. If a copy of the MPL was not distributed with this
33 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
44
5- use parking_lot:: RwLock ;
6- use std:: collections:: { BTreeMap , HashMap } ;
7- use std:: sync:: { Arc , LazyLock } ;
5+ use parking_lot:: { const_rwlock, RwLock } ;
6+ use std:: collections:: BTreeMap ;
7+ use std:: sync:: {
8+ atomic:: { AtomicU32 , Ordering } ,
9+ Arc ,
10+ } ;
811use tracing:: { subscriber:: Interest , Metadata } ;
912use tracing_subscriber:: {
1013 layer:: { Context , Filter } ,
1114 Layer ,
1215} ;
1316
14- use crate :: EventSink ;
17+ use crate :: { EventSink , Level } ;
1518use tracing:: field:: { Field , Visit } ;
1619
17- struct LogEntry {
18- level : tracing:: Level ,
19- sink : Arc < dyn EventSink > ,
20- }
20+ static SINKS : RwLock < Vec < RegisteredEventSink > > = const_rwlock ( Vec :: new ( ) ) ;
21+ static EVENT_SINK_COUNTER : AtomicU32 = AtomicU32 :: new ( 0 ) ;
2122
22- static SINKS_BY_TARGET : LazyLock < RwLock < HashMap < String , LogEntry > > > =
23- LazyLock :: new ( || RwLock :: new ( HashMap :: new ( ) ) ) ;
23+ # [ derive ( Clone , Copy , PartialEq , Eq ) ]
24+ pub struct EventSinkId ( u32 ) ;
2425
25- static MIN_LEVEL_SINK : RwLock < Option < LogEntry > > = RwLock :: new ( None ) ;
26+ uniffi:: custom_type!( EventSinkId , u32 , {
27+ try_lift: |raw_id| Ok ( EventSinkId ( raw_id) ) ,
28+ lower: |sink_id| sink_id. 0 ,
29+ } ) ;
2630
27- pub fn register_event_sink ( target : & str , level : crate :: Level , sink : Arc < dyn EventSink > ) {
28- SINKS_BY_TARGET . write ( ) . insert (
29- target. to_string ( ) ,
30- LogEntry {
31- level : level. into ( ) ,
32- sink,
33- } ,
34- ) ;
31+ /// Register an event sink using an [EventSinkSpecification]
32+ ///
33+ /// Returns an [EventSinkId] that can be used to unregister the sink.
34+ pub fn register_event_sink ( spec : EventSinkSpecification , sink : Arc < dyn EventSink > ) -> EventSinkId {
35+ let id = EventSinkId ( EVENT_SINK_COUNTER . fetch_add ( 1 , Ordering :: Relaxed ) ) ;
36+ SINKS . write ( ) . push ( RegisteredEventSink { id, spec, sink } ) ;
37+ id
3538}
3639
37- /// Register an event sink that will receive events based on a minimum level
38- ///
39- /// If an event's level is at least `level`, then the event will be sent to this sink.
40- /// If so, sinks registered with `register_event_sink` will still be processed.
41- ///
42- /// There can only be 1 min-level sink registered at once.
43- pub fn register_min_level_event_sink ( level : crate :: Level , sink : Arc < dyn EventSink > ) {
44- * MIN_LEVEL_SINK . write ( ) = Some ( LogEntry {
45- level : level. into ( ) ,
46- sink,
47- } ) ;
40+ struct RegisteredEventSink {
41+ // ID that can be used to unregister this sink
42+ id : EventSinkId ,
43+ spec : EventSinkSpecification ,
44+ sink : Arc < dyn EventSink > ,
4845}
4946
50- #[ uniffi:: export]
51- pub fn unregister_event_sink ( target : & str ) {
52- SINKS_BY_TARGET . write ( ) . remove ( target) ;
47+ #[ derive( uniffi:: Record ) ]
48+ /// Describes which events to an EventSink
49+ pub struct EventSinkSpecification {
50+ // Send events that match these targets/levels
51+ #[ uniffi( default ) ]
52+ pub targets : Vec < EventTarget > ,
53+ // Send events have a `min_level` or above.
54+ #[ uniffi( default ) ]
55+ pub min_level : Option < Level > ,
56+ }
57+
58+ #[ derive( uniffi:: Record , Debug ) ]
59+ pub struct EventTarget {
60+ pub target : String ,
61+ pub level : Level ,
5362}
5463
55- /// Remove the sink registered with [register_min_level_event_sink], if any.
5664#[ uniffi:: export]
57- pub fn unregister_min_level_event_sink ( ) {
58- * MIN_LEVEL_SINK . write ( ) = None ;
65+ pub fn unregister_event_sink ( id : EventSinkId ) {
66+ SINKS . write ( ) . retain ( |info| info . id != id ) ;
5967}
6068
6169// UniFFI versions of the registration functions. This input a Box to be compatible with callback
6270// interfaces
63- #[ uniffi:: export( name = "register_event_sink" ) ]
64- pub fn register_event_sink_box ( target : & str , level : crate :: Level , sink : Box < dyn EventSink > ) {
65- register_event_sink ( target, level, sink. into ( ) )
66- }
6771
68- #[ uniffi:: export( name = "register_min_level_event_sink" ) ]
69- pub fn register_min_level_event_sink_box ( level : crate :: Level , sink : Box < dyn EventSink > ) {
70- register_min_level_event_sink ( level, sink. into ( ) )
72+ #[ uniffi:: export( name = "register_event_sink" ) ]
73+ pub fn register_event_sink_box (
74+ targets : EventSinkSpecification ,
75+ sink : Box < dyn EventSink > ,
76+ ) -> EventSinkId {
77+ register_event_sink ( targets, sink. into ( ) )
7178}
7279
7380pub fn simple_event_layer < S > ( ) -> impl Layer < S >
@@ -88,43 +95,59 @@ where
8895 event : & tracing:: Event < ' _ > ,
8996 _ctx : tracing_subscriber:: layer:: Context < ' _ , S > ,
9097 ) {
91- let target = event. metadata ( ) . target ( ) ;
92- let prefix = match target. find ( ':' ) {
93- Some ( index) => & target[ ..index] ,
94- None => target,
95- } ;
96- if let Some ( entry) = & * MIN_LEVEL_SINK . read ( ) {
97- if entry. level >= * event. metadata ( ) . level ( ) {
98- entry. send_event ( event) ;
99- }
98+ let sinks = find_sinks_for_event ( event) ;
99+ if sinks. is_empty ( ) {
100+ // return early to skip the conversion below
101+ return ;
100102 }
101-
102- if let Some ( entry) = SINKS_BY_TARGET . read ( ) . get ( prefix) {
103- let level = * event. metadata ( ) . level ( ) ;
104- if level <= entry. level {
105- entry. send_event ( event) ;
106- }
107- }
108- }
109- }
110-
111- impl LogEntry {
112- fn send_event ( & self , event : & tracing:: Event < ' _ > ) {
113103 let mut fields = BTreeMap :: new ( ) ;
114104 let mut message = String :: default ( ) ;
115105 let mut visitor = JsonVisitor ( & mut message, & mut fields) ;
116106 event. record ( & mut visitor) ;
117- let event = crate :: Event {
107+ let tracing_event = crate :: Event {
118108 level : ( * event. metadata ( ) . level ( ) ) . into ( ) ,
119109 target : event. metadata ( ) . target ( ) . to_string ( ) ,
120110 name : event. metadata ( ) . name ( ) . to_string ( ) ,
121111 message,
122112 fields : serde_json:: to_value ( & fields) . unwrap_or_default ( ) ,
123113 } ;
124- self . sink . on_event ( event) ;
114+ for sink in sinks {
115+ sink. on_event ( tracing_event. clone ( ) ) ;
116+ }
125117 }
126118}
127119
120+ /// Find event sinks that match an event.
121+ fn find_sinks_for_event ( event : & tracing:: Event < ' _ > ) -> Vec < Arc < dyn EventSink > > {
122+ let target = event. metadata ( ) . target ( ) ;
123+ let prefix = match target. find ( ':' ) {
124+ Some ( index) => & target[ ..index] ,
125+ None => target,
126+ } ;
127+ let level = Level :: from ( * event. metadata ( ) . level ( ) ) ;
128+
129+ // This requires a iterating through the entire SINKS vec, which could have performance impacts
130+ // if we have many sinks registered. In practice, there should only be a handful of these so
131+ // this should be fine.
132+ SINKS
133+ . read ( )
134+ . iter ( )
135+ . filter_map ( |info| {
136+ if let Some ( min_level) = & info. spec . min_level {
137+ if * min_level >= level {
138+ return Some ( info. sink . clone ( ) ) ;
139+ }
140+ }
141+ for target in info. spec . targets . iter ( ) {
142+ if target. target == prefix && target. level >= level {
143+ return Some ( info. sink . clone ( ) ) ;
144+ }
145+ }
146+ None
147+ } )
148+ . collect ( )
149+ }
150+
128151struct SimpleEventFilter ;
129152
130153impl SimpleEventFilter {
0 commit comments