use sentry::{protocol::EnvelopeItem, Breadcrumb, Client, Envelope};
use serde::Deserialize;
use tauri::{Runtime, State, Webview};
use crate::process::WebviewTags;
#[derive(Debug, Clone, Deserialize)]
#[serde(untagged)]
#[allow(missing_docs)]
pub enum Buffer {
Text(String),
Raw(Vec<u8>),
}
#[tauri::command]
pub fn envelope<R: Runtime>(
webview: Webview<R>,
sentry_client: State<'_, Client>,
envelope: Buffer,
) {
let tags = WebviewTags::new(&webview);
let buffer = match envelope {
Buffer::Text(str) => str.into_bytes(),
Buffer::Raw(vec) => vec,
};
let parsed = Envelope::from_slice(&buffer);
if parsed.is_err() {
if let Ok(raw) = Envelope::from_bytes_raw(buffer) {
sentry_client.send_envelope(raw);
}
return;
}
if let Ok(envelope) = parsed {
if let Some(mut event) = envelope.event().cloned() {
event.platform = "javascript".into();
event.release = None;
event.environment = None;
event.dist = None;
if let Some(ref mut req) = event.request {
req.headers.remove("User-Agent");
}
let attachments = envelope
.items()
.filter_map(|item| match item {
EnvelopeItem::Attachment(attachment) => Some(attachment.clone()),
_ => None,
})
.collect::<Vec<_>>();
sentry::with_scope(
|scope| {
tags.apply_to_scope(scope);
for attachment in attachments {
scope.add_attachment(attachment);
}
},
|| {
sentry::capture_event(event);
},
)
} else {
sentry_client.send_envelope(add_sdk_package(envelope, &tags));
}
}
}
fn add_sdk_package(envelope: Envelope, tags: &WebviewTags) -> Envelope {
let mut out = Envelope::new().with_headers(envelope.headers().clone());
for mut item in envelope.into_items() {
if let EnvelopeItem::Transaction(ref mut transaction) = item {
if let Some(sdk) = transaction.sdk.as_mut() {
crate::add_sdk_package(sdk);
}
tags.apply(&mut transaction.tags);
}
out.add_item(item);
}
out
}
#[tauri::command]
pub fn breadcrumb(breadcrumb: Breadcrumb) {
sentry::add_breadcrumb(breadcrumb);
}
#[cfg(test)]
mod tests {
use std::borrow::Cow;
use sentry::protocol::{Attachment, ClientSdkInfo, Transaction};
use super::*;
#[test]
fn adds_sdk_package_to_transactions() {
let mut envelope = Envelope::new();
envelope.add_item(Transaction {
sdk: Some(Cow::Owned(ClientSdkInfo {
name: "sentry.javascript.browser".into(),
version: "10.0.0".into(),
integrations: vec![],
packages: vec![],
})),
..Default::default()
});
envelope.add_item(Attachment {
buffer: b"data".to_vec(),
filename: "file.txt".into(),
..Default::default()
});
let tags = WebviewTags::from_labels("main-view", "main");
let envelope = add_sdk_package(envelope, &tags);
let items = envelope.items().collect::<Vec<_>>();
assert_eq!(items.len(), 2);
let EnvelopeItem::Transaction(transaction) = items[0] else {
panic!("expected transaction");
};
let sdk = transaction.sdk.as_ref().unwrap();
assert_eq!(sdk.name, "sentry.javascript.browser");
assert!(sdk
.packages
.iter()
.any(|p| p.name == "cargo:tauri-plugin-sentry"));
assert_eq!(transaction.tags["event.process"], "webview");
assert_eq!(transaction.tags["tauri.webview"], "main-view");
assert_eq!(transaction.tags["tauri.window"], "main");
}
}