sentrystr_tracing/
layer.rs

1use crate::{convert_tracing_level, create_sentrystr_event, extract_event_metadata, FieldVisitor};
2use sentrystr::{DirectMessageSender, MessageEvent, NostrSentryClient};
3use std::sync::Arc;
4use tokio::sync::RwLock;
5use tracing::{Event, Subscriber};
6use tracing_subscriber::{layer::Context, Layer};
7
8pub struct SentryStrLayer {
9    client: Arc<RwLock<NostrSentryClient>>,
10    dm_sender: Option<Arc<RwLock<DirectMessageSender>>>,
11    min_level: Option<tracing::Level>,
12    include_fields: bool,
13    include_metadata: bool,
14}
15
16impl SentryStrLayer {
17    pub fn new(client: NostrSentryClient) -> Self {
18        Self {
19            client: Arc::new(RwLock::new(client)),
20            dm_sender: None,
21            min_level: None,
22            include_fields: true,
23            include_metadata: true,
24        }
25    }
26
27    pub fn with_direct_messaging(mut self, dm_sender: DirectMessageSender) -> Self {
28        self.dm_sender = Some(Arc::new(RwLock::new(dm_sender)));
29        self
30    }
31
32    pub fn with_min_level(mut self, level: tracing::Level) -> Self {
33        self.min_level = Some(level);
34        self
35    }
36
37    pub fn with_fields(mut self, include: bool) -> Self {
38        self.include_fields = include;
39        self
40    }
41
42    pub fn with_metadata(mut self, include: bool) -> Self {
43        self.include_metadata = include;
44        self
45    }
46
47    fn should_process_event(&self, event_level: &tracing::Level) -> bool {
48        if let Some(min_level) = &self.min_level {
49            event_level <= min_level
50        } else {
51            true
52        }
53    }
54}
55
56impl<S> Layer<S> for SentryStrLayer
57where
58    S: Subscriber + for<'a> tracing_subscriber::registry::LookupSpan<'a>,
59{
60    fn on_event(&self, event: &Event<'_>, _ctx: Context<'_, S>) {
61        let mut visitor = FieldVisitor::new();
62        event.record(&mut visitor);
63
64        let message = visitor.extract_message();
65        let level = convert_tracing_level(event.metadata().level());
66
67        if !self.should_process_event(event.metadata().level()) {
68            return;
69        }
70
71        let fields = if self.include_fields {
72            visitor.fields
73        } else {
74            std::collections::BTreeMap::new()
75        };
76
77        let metadata_fields = if self.include_metadata {
78            extract_event_metadata(event.metadata())
79        } else {
80            std::collections::BTreeMap::new()
81        };
82
83        let sentrystr_event = create_sentrystr_event(message, level, fields, metadata_fields);
84
85        let client = Arc::clone(&self.client);
86        let dm_sender = self.dm_sender.as_ref().map(Arc::clone);
87
88        tokio::spawn(async move {
89            let client = client.read().await;
90            if let Err(e) = client.capture_event(sentrystr_event.clone()).await {
91                eprintln!("Failed to send event to SentryStr: {}", e);
92                return;
93            }
94
95            if let Some(dm_sender) = dm_sender {
96                let dm_sender = dm_sender.read().await;
97                let message_event = MessageEvent {
98                    event: sentrystr_event,
99                    author: nostr::Keys::generate().public_key(),
100                    nostr_event_id: nostr::EventId::all_zeros(),
101                    received_at: chrono::Utc::now(),
102                };
103
104                if let Err(e) = dm_sender.send_message_for_event(&message_event).await {
105                    eprintln!("Failed to send direct message: {}", e);
106                }
107            }
108        });
109    }
110}
111
112impl Clone for SentryStrLayer {
113    fn clone(&self) -> Self {
114        Self {
115            client: Arc::clone(&self.client),
116            dm_sender: self.dm_sender.as_ref().map(Arc::clone),
117            min_level: self.min_level,
118            include_fields: self.include_fields,
119            include_metadata: self.include_metadata,
120        }
121    }
122}