use std::collections::{BTreeMap, VecDeque};
use serde_json::Value;
use crate::protocol::{Breadcrumb, ErrorEvent, Level, User};
#[derive(Debug, Clone, Default)]
pub struct Scope {
pub(crate) user: Option<User>,
pub(crate) tags: BTreeMap<String, String>,
pub(crate) extra: BTreeMap<String, Value>,
pub(crate) contexts: BTreeMap<String, Value>,
pub(crate) level: Option<Level>,
pub(crate) fingerprint: Option<Vec<String>>,
pub(crate) transaction: Option<String>,
pub(crate) trace_id: Option<String>,
pub(crate) span_id: Option<String>,
pub(crate) request_id: Option<String>,
pub(crate) breadcrumbs: VecDeque<Breadcrumb>,
pub(crate) max_breadcrumbs: usize,
}
impl Scope {
pub fn new(max_breadcrumbs: usize) -> Self {
Scope {
max_breadcrumbs,
..Scope::default()
}
}
pub fn set_user(&mut self, user: Option<User>) {
self.user = user;
}
pub fn set_tag(&mut self, key: impl Into<String>, value: impl Into<String>) {
self.tags.insert(key.into(), value.into());
}
pub fn set_extra(&mut self, key: impl Into<String>, value: impl Into<Value>) {
self.extra.insert(key.into(), value.into());
}
pub fn set_context(&mut self, key: impl Into<String>, value: impl Into<Value>) {
self.contexts.insert(key.into(), value.into());
}
pub fn set_level(&mut self, level: Option<Level>) {
self.level = level;
}
pub fn set_fingerprint(&mut self, fingerprint: Option<&[&str]>) {
self.fingerprint = fingerprint.map(|f| f.iter().map(|s| s.to_string()).collect());
}
pub fn set_transaction(&mut self, transaction: Option<&str>) {
self.transaction = transaction.map(|s| s.to_string());
}
pub fn set_trace_id(&mut self, trace_id: Option<String>) {
self.trace_id = trace_id;
}
pub fn set_span_id(&mut self, span_id: Option<String>) {
self.span_id = span_id;
}
pub fn span_id(&self) -> Option<&str> {
self.span_id.as_deref()
}
pub fn trace_id(&self) -> Option<&str> {
self.trace_id.as_deref()
}
pub fn set_request_id(&mut self, request_id: Option<String>) {
self.request_id = request_id;
}
pub fn request_id(&self) -> Option<&str> {
self.request_id.as_deref()
}
pub fn trace_context(&self) -> crate::propagation::TraceContext {
crate::propagation::TraceContext {
trace_id: self.trace_id.clone(),
parent_span_id: self.span_id.clone(),
request_id: self.request_id.clone(),
baggage: None,
}
}
pub fn add_breadcrumb(&mut self, breadcrumb: Breadcrumb) {
self.breadcrumbs.push_back(breadcrumb);
while self.breadcrumbs.len() > self.max_breadcrumbs {
self.breadcrumbs.pop_front();
}
}
pub fn clear(&mut self) {
let cap = self.max_breadcrumbs;
*self = Scope::new(cap);
}
pub(crate) fn apply_to_event(&self, event: &mut ErrorEvent) {
if event.user.is_none() {
event.user = self.user.clone();
}
if event.trace_id.is_none() {
event.trace_id = self.trace_id.clone();
}
if !self.breadcrumbs.is_empty() {
let mut crumbs: Vec<Breadcrumb> = self.breadcrumbs.iter().cloned().collect();
if let Some(existing) = event.breadcrumbs.take() {
crumbs.extend(existing);
}
event.breadcrumbs = Some(crumbs);
}
let mut meta = match event.metadata.take() {
Some(Value::Object(m)) => m,
_ => serde_json::Map::new(),
};
if !self.tags.is_empty() {
meta.insert(
"tags".to_string(),
serde_json::to_value(&self.tags).unwrap_or(Value::Null),
);
}
if !self.extra.is_empty() {
meta.insert(
"extra".to_string(),
serde_json::to_value(&self.extra).unwrap_or(Value::Null),
);
}
if !self.contexts.is_empty() {
meta.insert(
"contexts".to_string(),
serde_json::to_value(&self.contexts).unwrap_or(Value::Null),
);
}
if let Some(fp) = &self.fingerprint {
meta.insert(
"fingerprint".to_string(),
serde_json::to_value(fp).unwrap_or(Value::Null),
);
}
if let Some(tx) = &self.transaction {
meta.insert("transaction".to_string(), Value::String(tx.clone()));
}
if !meta.is_empty() {
event.metadata = Some(Value::Object(meta));
}
if let Some(level) = self.level {
if event.level.is_none() {
event.level = Some(level.as_str().to_string());
}
}
}
}