Skip to main content

sentry_types/protocol/
v7.rs

1//! The current latest sentry protocol version.
2//!
3//! Most constructs in the protocol map directly to types here but some
4//! cleanup by renaming attributes has been applied.  The idea here is that
5//! a future sentry protocol will be a cleanup of the old one and is mapped
6//! to similar values on the rust side.
7
8use std::borrow::Cow;
9use std::cmp;
10use std::convert::TryFrom;
11use std::fmt;
12use std::iter::FromIterator;
13use std::net::{AddrParseError, IpAddr};
14use std::ops;
15use std::str;
16use std::time::SystemTime;
17
18use self::debugid::{CodeId, DebugId};
19use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
20use thiserror::Error;
21
22pub use url::Url;
23pub use uuid::Uuid;
24
25use crate::utils::{display_from_str_opt, ts_rfc3339_opt, ts_seconds_float};
26
27pub use super::attachment::*;
28pub use super::envelope::*;
29pub use super::monitor::*;
30pub use super::session::*;
31pub use super::unit::Unit;
32
33/// An arbitrary (JSON) value.
34pub mod value {
35    pub use serde_json::value::{from_value, to_value, Index, Map, Number, Value};
36}
37
38/// The internally used arbitrary data map type.
39pub mod map {
40    pub use std::collections::btree_map::{BTreeMap as Map, *};
41}
42
43/// Represents a debug ID.
44pub mod debugid {
45    pub use debugid::{BreakpadFormat, CodeId, DebugId, ParseDebugIdError};
46}
47
48/// An arbitrary (JSON) value.
49pub use self::value::Value;
50
51/// The internally used map type.
52pub use self::map::Map;
53
54/// A wrapper type for collections with attached meta data.
55///
56/// The JSON payload can either directly be an array or an object containing a `values` field and
57/// arbitrary other fields. All other fields will be collected into `Values::data` when
58/// deserializing and re-serialized in the same place. The shorthand array notation is always
59/// reserialized as object.
60#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
61pub struct Values<T> {
62    /// The values of the collection.
63    pub values: Vec<T>,
64}
65
66impl<T> Values<T> {
67    /// Creates an empty values struct.
68    pub fn new() -> Values<T> {
69        Values { values: Vec::new() }
70    }
71
72    /// Checks whether this struct is empty in both values and data.
73    pub fn is_empty(&self) -> bool {
74        self.values.is_empty()
75    }
76}
77
78impl<T> Default for Values<T> {
79    fn default() -> Self {
80        // Default implemented manually even if <T> does not impl Default.
81        Values::new()
82    }
83}
84
85impl<T> From<Vec<T>> for Values<T> {
86    fn from(values: Vec<T>) -> Self {
87        Values { values }
88    }
89}
90
91impl<T> AsRef<[T]> for Values<T> {
92    fn as_ref(&self) -> &[T] {
93        &self.values
94    }
95}
96
97impl<T> AsMut<Vec<T>> for Values<T> {
98    fn as_mut(&mut self) -> &mut Vec<T> {
99        &mut self.values
100    }
101}
102
103impl<T> ops::Deref for Values<T> {
104    type Target = [T];
105
106    fn deref(&self) -> &Self::Target {
107        &self.values
108    }
109}
110
111impl<T> ops::DerefMut for Values<T> {
112    fn deref_mut(&mut self) -> &mut Self::Target {
113        &mut self.values
114    }
115}
116
117impl<T> FromIterator<T> for Values<T> {
118    fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
119        Vec::<T>::from_iter(iter).into()
120    }
121}
122
123impl<T> Extend<T> for Values<T> {
124    fn extend<I>(&mut self, iter: I)
125    where
126        I: IntoIterator<Item = T>,
127    {
128        self.values.extend(iter)
129    }
130}
131
132impl<'a, T> IntoIterator for &'a mut Values<T> {
133    type Item = <&'a mut Vec<T> as IntoIterator>::Item;
134    type IntoIter = <&'a mut Vec<T> as IntoIterator>::IntoIter;
135
136    fn into_iter(self) -> Self::IntoIter {
137        self.values.iter_mut()
138    }
139}
140
141impl<'a, T> IntoIterator for &'a Values<T> {
142    type Item = <&'a Vec<T> as IntoIterator>::Item;
143    type IntoIter = <&'a Vec<T> as IntoIterator>::IntoIter;
144
145    fn into_iter(self) -> Self::IntoIter {
146        self.values.iter()
147    }
148}
149
150impl<T> IntoIterator for Values<T> {
151    type Item = <Vec<T> as IntoIterator>::Item;
152    type IntoIter = <Vec<T> as IntoIterator>::IntoIter;
153
154    fn into_iter(self) -> Self::IntoIter {
155        self.values.into_iter()
156    }
157}
158
159/// Represents a log entry message.
160///
161/// A log message is similar to the `message` attribute on the event itself but
162/// can additionally hold optional parameters.
163#[derive(Serialize, Deserialize, Default, Clone, Debug, PartialEq)]
164pub struct LogEntry {
165    /// The log message with parameters replaced by `%s`
166    pub message: String,
167    /// Positional parameters to be inserted into the log entry.
168    #[serde(default, skip_serializing_if = "Vec::is_empty")]
169    pub params: Vec<Value>,
170}
171
172/// Represents a frame.
173#[derive(Serialize, Deserialize, Default, Clone, Debug, PartialEq)]
174pub struct Frame {
175    /// The name of the function is known.
176    ///
177    /// Note that this might include the name of a class as well if that makes
178    /// sense for the language.
179    #[serde(default, skip_serializing_if = "Option::is_none")]
180    pub function: Option<String>,
181    /// The potentially mangled name of the symbol as it appears in an executable.
182    ///
183    /// This is different from a function name by generally being the mangled
184    /// name that appears natively in the binary.  This is relevant for languages
185    /// like Swift, C++ or Rust.
186    #[serde(default, skip_serializing_if = "Option::is_none")]
187    pub symbol: Option<String>,
188    /// The name of the module the frame is contained in.
189    ///
190    /// Note that this might also include a class name if that is something the
191    /// language natively considers to be part of the stack (for instance in Java).
192    #[serde(default, skip_serializing_if = "Option::is_none")]
193    pub module: Option<String>,
194    /// The name of the package that contains the frame.
195    ///
196    /// For instance this can be a dylib for native languages, the name of the jar
197    /// or .NET assembly.
198    #[serde(default, skip_serializing_if = "Option::is_none")]
199    pub package: Option<String>,
200    /// The filename (basename only).
201    #[serde(default, skip_serializing_if = "Option::is_none")]
202    pub filename: Option<String>,
203    /// If known the absolute path.
204    #[serde(default, skip_serializing_if = "Option::is_none")]
205    pub abs_path: Option<String>,
206    /// The line number if known.
207    #[serde(default, skip_serializing_if = "Option::is_none")]
208    pub lineno: Option<u64>,
209    /// The column number if known.
210    #[serde(default, skip_serializing_if = "Option::is_none")]
211    pub colno: Option<u64>,
212    /// The sources of the lines leading up to the current line.
213    #[serde(default, skip_serializing_if = "Vec::is_empty")]
214    pub pre_context: Vec<String>,
215    /// The current line as source.
216    #[serde(default, skip_serializing_if = "Option::is_none")]
217    pub context_line: Option<String>,
218    /// The sources of the lines after the current line.
219    #[serde(default, skip_serializing_if = "Vec::is_empty")]
220    pub post_context: Vec<String>,
221    /// In-app indicator.
222    #[serde(default, skip_serializing_if = "Option::is_none")]
223    pub in_app: Option<bool>,
224    /// Optional local variables.
225    #[serde(default, skip_serializing_if = "Map::is_empty")]
226    pub vars: Map<String, Value>,
227    /// If known the location of the image.
228    #[serde(default, skip_serializing_if = "Option::is_none")]
229    pub image_addr: Option<Addr>,
230    /// If known the location of the instruction.
231    #[serde(default, skip_serializing_if = "Option::is_none")]
232    pub instruction_addr: Option<Addr>,
233    /// If known the location of symbol.
234    #[serde(default, skip_serializing_if = "Option::is_none")]
235    pub symbol_addr: Option<Addr>,
236    /// Optionally changes the addressing mode. The default value is the same as
237    /// `"abs"` which means absolute referencing. This can also be set to
238    /// `"rel:DEBUG_ID"` or `"rel:IMAGE_INDEX"` to make addresses relative to an
239    /// object referenced by debug id or index. This for instance is necessary
240    /// for WASM processing as WASM does not use a unified address space.
241    #[serde(default, skip_serializing_if = "Option::is_none")]
242    pub addr_mode: Option<String>,
243}
244
245/// Represents template debug info.
246#[derive(Serialize, Deserialize, Default, Clone, Debug, PartialEq)]
247pub struct TemplateInfo {
248    /// The filename (basename only).
249    #[serde(default, skip_serializing_if = "Option::is_none")]
250    pub filename: Option<String>,
251    /// If known the absolute path.
252    #[serde(default, skip_serializing_if = "Option::is_none")]
253    pub abs_path: Option<String>,
254    /// The line number if known.
255    #[serde(default, skip_serializing_if = "Option::is_none")]
256    pub lineno: Option<u64>,
257    /// The column number if known.
258    #[serde(default, skip_serializing_if = "Option::is_none")]
259    pub colno: Option<u64>,
260    /// The sources of the lines leading up to the current line.
261    #[serde(default, skip_serializing_if = "Vec::is_empty")]
262    pub pre_context: Vec<String>,
263    /// The current line as source.
264    #[serde(default, skip_serializing_if = "Option::is_none")]
265    pub context_line: Option<String>,
266    /// The sources of the lines after the current line.
267    #[serde(default, skip_serializing_if = "Vec::is_empty")]
268    pub post_context: Vec<String>,
269}
270
271/// Represents a stacktrace.
272#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq)]
273pub struct Stacktrace {
274    /// The list of frames in the stacktrace.
275    #[serde(default)]
276    pub frames: Vec<Frame>,
277    /// Optionally a segment of frames removed (`start`, `end`).
278    #[serde(default, skip_serializing_if = "Option::is_none")]
279    pub frames_omitted: Option<(u64, u64)>,
280    /// Optional register values of the thread.
281    #[serde(default, skip_serializing_if = "Map::is_empty")]
282    pub registers: Map<String, RegVal>,
283}
284
285impl Stacktrace {
286    /// Optionally creates a stacktrace from a list of stack frames.
287    pub fn from_frames_reversed(mut frames: Vec<Frame>) -> Option<Stacktrace> {
288        if frames.is_empty() {
289            None
290        } else {
291            frames.reverse();
292            Some(Stacktrace {
293                frames,
294                ..Default::default()
295            })
296        }
297    }
298}
299
300/// Represents a thread id.
301#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Ord, PartialOrd, Hash)]
302#[serde(untagged)]
303pub enum ThreadId {
304    /// Integer representation for the thread id
305    Int(u64),
306    /// String representation for the thread id
307    String(String),
308}
309
310impl Default for ThreadId {
311    fn default() -> ThreadId {
312        ThreadId::Int(0)
313    }
314}
315
316impl<'a> From<&'a str> for ThreadId {
317    fn from(id: &'a str) -> ThreadId {
318        ThreadId::String(id.to_string())
319    }
320}
321
322impl From<String> for ThreadId {
323    fn from(id: String) -> ThreadId {
324        ThreadId::String(id)
325    }
326}
327
328impl From<i64> for ThreadId {
329    fn from(id: i64) -> ThreadId {
330        ThreadId::Int(id as u64)
331    }
332}
333
334impl From<i32> for ThreadId {
335    fn from(id: i32) -> ThreadId {
336        ThreadId::Int(id as u64)
337    }
338}
339
340impl From<u32> for ThreadId {
341    fn from(id: u32) -> ThreadId {
342        ThreadId::Int(id as u64)
343    }
344}
345
346impl From<u16> for ThreadId {
347    fn from(id: u16) -> ThreadId {
348        ThreadId::Int(id as u64)
349    }
350}
351
352impl fmt::Display for ThreadId {
353    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
354        match *self {
355            ThreadId::Int(i) => write!(f, "{i}"),
356            ThreadId::String(ref s) => write!(f, "{s}"),
357        }
358    }
359}
360
361/// Represents an address.
362#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, Ord, PartialOrd, Hash)]
363pub struct Addr(pub u64);
364
365impl Addr {
366    /// Returns `true` if this address is the null pointer.
367    pub fn is_null(&self) -> bool {
368        self.0 == 0
369    }
370}
371
372impl_hex_serde!(Addr, u64);
373
374impl From<u64> for Addr {
375    fn from(addr: u64) -> Addr {
376        Addr(addr)
377    }
378}
379
380impl From<i32> for Addr {
381    fn from(addr: i32) -> Addr {
382        Addr(addr as u64)
383    }
384}
385
386impl From<u32> for Addr {
387    fn from(addr: u32) -> Addr {
388        Addr(addr as u64)
389    }
390}
391
392impl From<usize> for Addr {
393    fn from(addr: usize) -> Addr {
394        Addr(addr as u64)
395    }
396}
397
398impl<T> From<*const T> for Addr {
399    fn from(addr: *const T) -> Addr {
400        Addr(addr as u64)
401    }
402}
403
404impl<T> From<*mut T> for Addr {
405    fn from(addr: *mut T) -> Addr {
406        Addr(addr as u64)
407    }
408}
409
410impl From<Addr> for u64 {
411    fn from(addr: Addr) -> Self {
412        addr.0
413    }
414}
415
416fn is_false(value: &bool) -> bool {
417    !*value
418}
419
420/// Represents a register value.
421#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, Ord, PartialOrd, Hash)]
422pub struct RegVal(pub u64);
423
424impl_hex_serde!(RegVal, u64);
425
426impl From<u64> for RegVal {
427    fn from(addr: u64) -> RegVal {
428        RegVal(addr)
429    }
430}
431
432impl From<i32> for RegVal {
433    fn from(addr: i32) -> RegVal {
434        RegVal(addr as u64)
435    }
436}
437
438impl From<u32> for RegVal {
439    fn from(addr: u32) -> RegVal {
440        RegVal(addr as u64)
441    }
442}
443
444impl From<usize> for RegVal {
445    fn from(addr: usize) -> RegVal {
446        RegVal(addr as u64)
447    }
448}
449
450impl<T> From<*const T> for RegVal {
451    fn from(addr: *const T) -> RegVal {
452        RegVal(addr as u64)
453    }
454}
455
456impl<T> From<*mut T> for RegVal {
457    fn from(addr: *mut T) -> RegVal {
458        RegVal(addr as u64)
459    }
460}
461
462impl From<RegVal> for u64 {
463    fn from(reg: RegVal) -> Self {
464        reg.0
465    }
466}
467
468/// Represents a single thread.
469#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq)]
470pub struct Thread {
471    /// The optional ID of the thread (usually an integer)
472    #[serde(default, skip_serializing_if = "Option::is_none")]
473    pub id: Option<ThreadId>,
474    /// The optional name of the thread.
475    #[serde(default, skip_serializing_if = "Option::is_none")]
476    pub name: Option<String>,
477    /// If the thread suspended or crashed a stacktrace can be
478    /// attached here.
479    #[serde(default, skip_serializing_if = "Option::is_none")]
480    pub stacktrace: Option<Stacktrace>,
481    /// Optional raw stacktrace.
482    #[serde(default, skip_serializing_if = "Option::is_none")]
483    pub raw_stacktrace: Option<Stacktrace>,
484    /// True if this is the crashed thread.
485    #[serde(default, skip_serializing_if = "is_false")]
486    pub crashed: bool,
487    /// Indicates that the thread was not suspended when the
488    /// event was created.
489    #[serde(default, skip_serializing_if = "is_false")]
490    pub current: bool,
491}
492
493/// POSIX signal with optional extended data.
494#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq, Eq)]
495pub struct CError {
496    /// The error code as specified by ISO C99, POSIX.1-2001 or POSIX.1-2008.
497    pub number: i32,
498    /// Optional name of the errno constant.
499    #[serde(default, skip_serializing_if = "Option::is_none")]
500    pub name: Option<String>,
501}
502
503impl From<i32> for CError {
504    fn from(number: i32) -> CError {
505        CError { number, name: None }
506    }
507}
508
509impl From<CError> for i32 {
510    fn from(err: CError) -> Self {
511        err.number
512    }
513}
514
515/// Mach exception information.
516#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq, Eq)]
517pub struct MachException {
518    /// The mach exception type.
519    pub exception: i32,
520    /// The mach exception code.
521    pub code: u64,
522    /// The mach exception subcode.
523    pub subcode: u64,
524    /// Optional name of the mach exception.
525    #[serde(default, skip_serializing_if = "Option::is_none")]
526    pub name: Option<String>,
527}
528
529/// POSIX signal with optional extended data.
530#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq, Eq)]
531pub struct PosixSignal {
532    /// The POSIX signal number.
533    pub number: i32,
534    /// An optional signal code present on Apple systems.
535    #[serde(default, skip_serializing_if = "Option::is_none")]
536    pub code: Option<i32>,
537    /// Optional name of the errno constant.
538    #[serde(default, skip_serializing_if = "Option::is_none")]
539    pub name: Option<String>,
540    /// Optional name of the errno constant.
541    #[serde(default, skip_serializing_if = "Option::is_none")]
542    pub code_name: Option<String>,
543}
544
545impl From<i32> for PosixSignal {
546    fn from(number: i32) -> PosixSignal {
547        PosixSignal {
548            number,
549            code: None,
550            name: None,
551            code_name: None,
552        }
553    }
554}
555
556impl From<(i32, i32)> for PosixSignal {
557    fn from(tuple: (i32, i32)) -> PosixSignal {
558        let (number, code) = tuple;
559        PosixSignal {
560            number,
561            code: Some(code),
562            name: None,
563            code_name: None,
564        }
565    }
566}
567
568impl From<PosixSignal> for i32 {
569    fn from(sig: PosixSignal) -> Self {
570        sig.number
571    }
572}
573
574/// Operating system or runtime meta information to an exception mechanism.
575#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq)]
576pub struct MechanismMeta {
577    /// Optional ISO C standard error code.
578    #[serde(default, skip_serializing_if = "Option::is_none")]
579    pub errno: Option<CError>,
580    /// Optional POSIX signal number.
581    #[serde(default, skip_serializing_if = "Option::is_none")]
582    pub signal: Option<PosixSignal>,
583    /// Optional mach exception information.
584    #[serde(default, skip_serializing_if = "Option::is_none")]
585    pub mach_exception: Option<MachException>,
586}
587
588impl MechanismMeta {
589    fn is_empty(&self) -> bool {
590        self.errno.is_none() && self.signal.is_none() && self.mach_exception.is_none()
591    }
592}
593
594/// Represents a single exception.
595#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq)]
596pub struct Mechanism {
597    /// The mechanism type identifier.
598    #[serde(rename = "type")]
599    pub ty: String,
600    /// Human readable detail description.
601    #[serde(default, skip_serializing_if = "Option::is_none")]
602    pub description: Option<String>,
603    /// An optional link to online resources describing this error.
604    #[serde(default, skip_serializing_if = "Option::is_none")]
605    pub help_link: Option<Url>,
606    /// An optional flag indicating whether this exception was handled.
607    #[serde(default, skip_serializing_if = "Option::is_none")]
608    pub handled: Option<bool>,
609    /// An optional flag indicating a synthetic exception.
610    #[serde(default, skip_serializing_if = "Option::is_none")]
611    pub synthetic: Option<bool>,
612    /// Additional attributes depending on the mechanism type.
613    #[serde(default, skip_serializing_if = "Map::is_empty")]
614    pub data: Map<String, Value>,
615    /// Operating system or runtime meta information.
616    #[serde(default, skip_serializing_if = "MechanismMeta::is_empty")]
617    pub meta: MechanismMeta,
618}
619
620/// Represents a single exception.
621#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq)]
622pub struct Exception {
623    /// The type of the exception.
624    #[serde(rename = "type")]
625    pub ty: String,
626    /// The optional value of the exception.
627    #[serde(skip_serializing_if = "Option::is_none")]
628    pub value: Option<String>,
629    /// An optional module for this exception.
630    #[serde(default, skip_serializing_if = "Option::is_none")]
631    pub module: Option<String>,
632    /// Optionally the stacktrace.
633    #[serde(default, skip_serializing_if = "Option::is_none")]
634    pub stacktrace: Option<Stacktrace>,
635    /// An optional raw stacktrace.
636    #[serde(default, skip_serializing_if = "Option::is_none")]
637    pub raw_stacktrace: Option<Stacktrace>,
638    /// Optional identifier referring to a thread.
639    #[serde(default, skip_serializing_if = "Option::is_none")]
640    pub thread_id: Option<ThreadId>,
641    /// The mechanism of the exception including OS specific exception values.
642    #[serde(default, skip_serializing_if = "Option::is_none")]
643    pub mechanism: Option<Mechanism>,
644}
645
646/// An error used when parsing `Level`.
647#[derive(Debug, Error)]
648#[error("invalid level")]
649pub struct ParseLevelError;
650
651/// Represents the level of severity of an event or breadcrumb.
652#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
653pub enum Level {
654    /// Indicates very spammy debug information.
655    Debug,
656    /// Informational messages.
657    #[default]
658    Info,
659    /// A warning.
660    Warning,
661    /// An error.
662    Error,
663    /// Similar to error but indicates a critical event that usually causes a shutdown.
664    Fatal,
665}
666
667impl str::FromStr for Level {
668    type Err = ParseLevelError;
669
670    fn from_str(string: &str) -> Result<Level, Self::Err> {
671        Ok(match string {
672            "debug" => Level::Debug,
673            "info" | "log" => Level::Info,
674            "warning" => Level::Warning,
675            "error" => Level::Error,
676            "fatal" => Level::Fatal,
677            _ => return Err(ParseLevelError),
678        })
679    }
680}
681
682impl fmt::Display for Level {
683    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
684        match *self {
685            Level::Debug => write!(f, "debug"),
686            Level::Info => write!(f, "info"),
687            Level::Warning => write!(f, "warning"),
688            Level::Error => write!(f, "error"),
689            Level::Fatal => write!(f, "fatal"),
690        }
691    }
692}
693
694impl Level {
695    /// A quick way to check if the level is `debug`.
696    pub fn is_debug(&self) -> bool {
697        *self == Level::Debug
698    }
699
700    /// A quick way to check if the level is `info`.
701    pub fn is_info(&self) -> bool {
702        *self == Level::Info
703    }
704
705    /// A quick way to check if the level is `warning`.
706    pub fn is_warning(&self) -> bool {
707        *self == Level::Warning
708    }
709
710    /// A quick way to check if the level is `error`.
711    pub fn is_error(&self) -> bool {
712        *self == Level::Error
713    }
714
715    /// A quick way to check if the level is `fatal`.
716    pub fn is_fatal(&self) -> bool {
717        *self == Level::Fatal
718    }
719}
720
721impl_str_serde!(Level);
722
723mod breadcrumb {
724    use super::*;
725
726    pub fn default_type() -> String {
727        "default".to_string()
728    }
729
730    pub fn is_default_type(ty: &str) -> bool {
731        ty == "default"
732    }
733
734    pub fn default_level() -> Level {
735        Level::Info
736    }
737}
738
739/// Represents a single breadcrumb.
740#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
741pub struct Breadcrumb {
742    /// The timestamp of the breadcrumb.  This is required.
743    #[serde(default = "SystemTime::now", with = "ts_seconds_float")]
744    pub timestamp: SystemTime,
745    /// The type of the breadcrumb.
746    #[serde(
747        rename = "type",
748        default = "breadcrumb::default_type",
749        skip_serializing_if = "breadcrumb::is_default_type"
750    )]
751    pub ty: String,
752    /// The optional category of the breadcrumb.
753    #[serde(default, skip_serializing_if = "Option::is_none")]
754    pub category: Option<String>,
755    /// The non optional level of the breadcrumb.  It
756    /// defaults to info.
757    #[serde(
758        default = "breadcrumb::default_level",
759        skip_serializing_if = "Level::is_info"
760    )]
761    pub level: Level,
762    /// An optional human readbale message for the breadcrumb.
763    #[serde(default, skip_serializing_if = "Option::is_none")]
764    pub message: Option<String>,
765    /// Arbitrary breadcrumb data that should be send along.
766    #[serde(default, skip_serializing_if = "Map::is_empty")]
767    pub data: Map<String, Value>,
768}
769
770impl Default for Breadcrumb {
771    fn default() -> Breadcrumb {
772        Breadcrumb {
773            timestamp: SystemTime::now(),
774            ty: breadcrumb::default_type(),
775            category: Default::default(),
776            level: breadcrumb::default_level(),
777            message: Default::default(),
778            data: Default::default(),
779        }
780    }
781}
782
783/// An IP address, either IPv4, IPv6 or Auto.
784#[derive(Debug, Clone, Copy, PartialEq, Eq, Ord, PartialOrd, Hash, Default)]
785pub enum IpAddress {
786    /// The IP address needs to be inferred from the user's context.
787    #[default]
788    Auto,
789    /// The exact given IP address (v4 or v6).
790    Exact(IpAddr),
791}
792
793impl PartialEq<IpAddr> for IpAddress {
794    fn eq(&self, other: &IpAddr) -> bool {
795        match *self {
796            IpAddress::Auto => false,
797            IpAddress::Exact(ref addr) => addr == other,
798        }
799    }
800}
801
802impl cmp::PartialOrd<IpAddr> for IpAddress {
803    fn partial_cmp(&self, other: &IpAddr) -> Option<cmp::Ordering> {
804        match *self {
805            IpAddress::Auto => None,
806            IpAddress::Exact(ref addr) => addr.partial_cmp(other),
807        }
808    }
809}
810
811impl fmt::Display for IpAddress {
812    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
813        match *self {
814            IpAddress::Auto => write!(f, "{{{{auto}}}}"),
815            IpAddress::Exact(ref addr) => write!(f, "{addr}"),
816        }
817    }
818}
819
820impl From<IpAddr> for IpAddress {
821    fn from(addr: IpAddr) -> IpAddress {
822        IpAddress::Exact(addr)
823    }
824}
825
826impl str::FromStr for IpAddress {
827    type Err = AddrParseError;
828
829    fn from_str(string: &str) -> Result<IpAddress, AddrParseError> {
830        match string {
831            "{{auto}}" => Ok(IpAddress::Auto),
832            other => other.parse().map(IpAddress::Exact),
833        }
834    }
835}
836
837impl_str_serde!(IpAddress);
838
839/// Represents user info.
840#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq)]
841pub struct User {
842    /// The ID of the user.
843    #[serde(default, skip_serializing_if = "Option::is_none")]
844    pub id: Option<String>,
845    /// The email address of the user.
846    #[serde(default, skip_serializing_if = "Option::is_none")]
847    pub email: Option<String>,
848    /// The remote ip address of the user.
849    #[serde(default, skip_serializing_if = "Option::is_none")]
850    pub ip_address: Option<IpAddress>,
851    /// A human readable username of the user.
852    #[serde(default, skip_serializing_if = "Option::is_none")]
853    pub username: Option<String>,
854    /// Additional arbitrary fields for forwards compatibility.
855    #[serde(flatten)]
856    pub other: Map<String, Value>,
857}
858
859/// Represents http request data.
860#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq)]
861pub struct Request {
862    /// The current URL of the request.
863    #[serde(default, skip_serializing_if = "Option::is_none")]
864    pub url: Option<Url>,
865    /// The HTTP request method.
866    #[serde(default, skip_serializing_if = "Option::is_none")]
867    pub method: Option<String>,
868    /// Optionally some associated request data (human readable)
869    // XXX: this makes absolutely no sense because of unicode
870    #[serde(default, skip_serializing_if = "Option::is_none")]
871    pub data: Option<String>,
872    /// Optionally the encoded query string.
873    #[serde(default, skip_serializing_if = "Option::is_none")]
874    pub query_string: Option<String>,
875    /// An encoded cookie string if available.
876    #[serde(default, skip_serializing_if = "Option::is_none")]
877    pub cookies: Option<String>,
878    /// HTTP request headers.
879    #[serde(default, skip_serializing_if = "Map::is_empty")]
880    pub headers: Map<String, String>,
881    /// Optionally a CGI/WSGI etc. environment dictionary.
882    #[serde(default, skip_serializing_if = "Map::is_empty")]
883    pub env: Map<String, String>,
884}
885
886/// Holds information about the system SDK.
887///
888/// This is relevant for iOS and other platforms that have a system
889/// SDK.  Not to be confused with the client SDK.
890#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
891pub struct SystemSdkInfo {
892    /// The internal name of the SDK
893    pub sdk_name: String,
894    /// the major version of the SDK as integer or 0
895    pub version_major: u32,
896    /// the minor version of the SDK as integer or 0
897    pub version_minor: u32,
898    /// the patch version of the SDK as integer or 0
899    pub version_patchlevel: u32,
900}
901
902/// Represents a debug image.
903#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
904#[serde(rename_all = "snake_case", tag = "type")]
905pub enum DebugImage {
906    /// Apple debug images (machos).  This is currently also used for
907    /// non apple platforms with similar debug setups.
908    Apple(AppleDebugImage),
909    /// Symbolic (new style) debug infos.
910    Symbolic(SymbolicDebugImage),
911    /// A reference to a proguard debug file.
912    Proguard(ProguardDebugImage),
913    /// Image used for WebAssembly. Their structure is identical to other native
914    /// images.
915    Wasm(WasmDebugImage),
916}
917
918impl DebugImage {
919    /// Returns the name of the type on sentry.
920    pub fn type_name(&self) -> &str {
921        match *self {
922            DebugImage::Apple(..) => "apple",
923            DebugImage::Symbolic(..) => "symbolic",
924            DebugImage::Proguard(..) => "proguard",
925            DebugImage::Wasm(..) => "wasm",
926        }
927    }
928}
929
930macro_rules! into_debug_image {
931    ($kind:ident, $ty:ty) => {
932        impl From<$ty> for DebugImage {
933            fn from(data: $ty) -> DebugImage {
934                DebugImage::$kind(data)
935            }
936        }
937    };
938}
939
940/// Represents an apple debug image in the debug meta.
941#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
942pub struct AppleDebugImage {
943    /// The name of the debug image (usually filename)
944    pub name: String,
945    /// The optional CPU architecture of the debug image.
946    pub arch: Option<String>,
947    /// Alternatively a macho cpu type.
948    pub cpu_type: Option<u32>,
949    /// Alternatively a macho cpu subtype.
950    pub cpu_subtype: Option<u32>,
951    /// The starting address of the image.
952    pub image_addr: Addr,
953    /// The size of the image in bytes.
954    pub image_size: u64,
955    /// The address where the image is loaded at runtime.
956    #[serde(default, skip_serializing_if = "Addr::is_null")]
957    pub image_vmaddr: Addr,
958    /// The unique UUID of the image.
959    pub uuid: Uuid,
960}
961
962/// Represents a symbolic debug image.
963#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
964pub struct SymbolicDebugImage {
965    /// Path and name of the image file (required).
966    ///
967    /// The absolute path to the dynamic library or executable. This helps to locate the file if it is missing on Sentry.
968    /// This is also called `code_file`.
969    pub name: String,
970    /// The optional CPU architecture of the debug image.
971    pub arch: Option<String>,
972    /// Starting memory address of the image (required).
973    ///
974    /// Memory address, at which the image is mounted in the virtual address space of the process.
975    pub image_addr: Addr,
976    /// Size of the image in bytes (required).
977    ///
978    /// The size of the image in virtual memory.
979    pub image_size: u64,
980    /// Loading address in virtual memory.
981    ///
982    /// Preferred load address of the image in virtual memory, as declared in the headers of the
983    /// image. When loading an image, the operating system may still choose to place it at a
984    /// different address.
985    ///
986    /// Symbols and addresses in the native image are always relative to the start of the image and do not consider the preferred load address. It is merely a hint to the loader.
987    #[serde(default, skip_serializing_if = "Addr::is_null")]
988    pub image_vmaddr: Addr,
989    /// Unique debug identifier of the image.
990    ///
991    /// This is also called `debug_id`.
992    pub id: DebugId,
993
994    /// Optional identifier of the code file.
995    #[serde(default, skip_serializing_if = "Option::is_none")]
996    pub code_id: Option<CodeId>,
997    /// Path and name of the debug companion file.
998    #[serde(default, skip_serializing_if = "Option::is_none")]
999    pub debug_file: Option<String>,
1000}
1001
1002/// Represents a proguard mapping file reference.
1003#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
1004pub struct ProguardDebugImage {
1005    /// The UUID of the associated proguard file.
1006    pub uuid: Uuid,
1007}
1008
1009/// Represents a WebAssembly debug image.
1010#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
1011pub struct WasmDebugImage {
1012    /// The name of the debug image (usually filename)
1013    pub name: String,
1014    /// Identifier of the dynamic library or executable.
1015    pub debug_id: Uuid,
1016    /// Name or absolute URL to the WASM file containing debug information for
1017    /// this image. This value might be required to retrieve debug files from
1018    /// certain symbol servers. This should correspond to the externalized URL
1019    /// pulled from the external_debug_info custom section.
1020    #[serde(default, skip_serializing_if = "Option::is_none")]
1021    pub debug_file: Option<String>,
1022    /// Identifier of the WASM file. It is the value of the build_id custom
1023    /// section formatted as HEX string.
1024    #[serde(default, skip_serializing_if = "Option::is_none")]
1025    pub code_id: Option<String>,
1026    /// The absolute URL to the wasm file. This helps to locate the file if it
1027    /// is missing on Sentry.
1028    pub code_file: String,
1029}
1030
1031into_debug_image!(Apple, AppleDebugImage);
1032into_debug_image!(Symbolic, SymbolicDebugImage);
1033into_debug_image!(Proguard, ProguardDebugImage);
1034into_debug_image!(Wasm, WasmDebugImage);
1035
1036/// Represents debug meta information.
1037#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq)]
1038pub struct DebugMeta {
1039    /// Optional system SDK information.
1040    #[serde(default, skip_serializing_if = "Option::is_none")]
1041    pub sdk_info: Option<SystemSdkInfo>,
1042    /// A list of debug information files.
1043    #[serde(default, skip_serializing_if = "Vec::is_empty")]
1044    pub images: Vec<DebugImage>,
1045}
1046
1047impl DebugMeta {
1048    /// Returns true if the debug meta is empty.
1049    ///
1050    /// This is used by the serializer to entirely skip the section.
1051    pub fn is_empty(&self) -> bool {
1052        self.sdk_info.is_none() && self.images.is_empty()
1053    }
1054}
1055
1056/// Information on the SDK client.
1057#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
1058pub struct ClientSdkInfo {
1059    /// The name of the SDK.
1060    pub name: String,
1061    /// The version of the SDK.
1062    pub version: String,
1063    /// An optional list of integrations that are enabled in this SDK.
1064    #[serde(default, skip_serializing_if = "Vec::is_empty")]
1065    pub integrations: Vec<String>,
1066    /// An optional list of packages that are installed in the SDK's environment.
1067    #[serde(default, skip_serializing_if = "Vec::is_empty")]
1068    pub packages: Vec<ClientSdkPackage>,
1069}
1070
1071/// Represents an installed package relevant to the SDK.
1072#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
1073pub struct ClientSdkPackage {
1074    /// The name of the package installed.
1075    pub name: String,
1076    /// The version of the package.
1077    pub version: String,
1078}
1079
1080/// Typed contextual data.
1081///
1082/// Types like `OsContext` can be directly converted with `.into()`
1083/// to `Context`.
1084#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
1085#[serde(rename_all = "snake_case", tag = "type")]
1086#[non_exhaustive]
1087pub enum Context {
1088    /// Device data.
1089    Device(Box<DeviceContext>),
1090    /// Operating system data.
1091    Os(Box<OsContext>),
1092    /// Runtime data.
1093    Runtime(Box<RuntimeContext>),
1094    /// Application data.
1095    App(Box<AppContext>),
1096    /// Web browser data.
1097    Browser(Box<BrowserContext>),
1098    /// Tracing data.
1099    Trace(Box<TraceContext>),
1100    /// GPU data.
1101    Gpu(Box<GpuContext>),
1102    /// OpenTelemetry data.
1103    Otel(Box<OtelContext>),
1104    /// HTTP response data.
1105    Response(Box<ResponseContext>),
1106    /// Generic other context data.
1107    #[serde(rename = "unknown")]
1108    Other(Map<String, Value>),
1109}
1110
1111impl Context {
1112    /// Returns the name of the type for sentry.
1113    pub fn type_name(&self) -> &str {
1114        match *self {
1115            Context::Device(..) => "device",
1116            Context::Os(..) => "os",
1117            Context::Runtime(..) => "runtime",
1118            Context::App(..) => "app",
1119            Context::Browser(..) => "browser",
1120            Context::Trace(..) => "trace",
1121            Context::Gpu(..) => "gpu",
1122            Context::Otel(..) => "otel",
1123            Context::Response(..) => "response",
1124            Context::Other(..) => "unknown",
1125        }
1126    }
1127}
1128
1129/// Optional device screen orientation
1130#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Hash)]
1131#[serde(rename_all = "lowercase")]
1132pub enum Orientation {
1133    /// Portrait device orientation.
1134    Portrait,
1135    /// Landscape device orientation.
1136    Landscape,
1137}
1138
1139/// Holds device information.
1140#[derive(Serialize, Deserialize, Debug, Clone, Default, PartialEq)]
1141pub struct DeviceContext {
1142    /// The name of the device.
1143    #[serde(default, skip_serializing_if = "Option::is_none")]
1144    pub name: Option<String>,
1145    /// The family of the device model.
1146    #[serde(default, skip_serializing_if = "Option::is_none")]
1147    pub family: Option<String>,
1148    /// The device model (human readable).
1149    #[serde(default, skip_serializing_if = "Option::is_none")]
1150    pub model: Option<String>,
1151    /// The device model (internal identifier).
1152    #[serde(default, skip_serializing_if = "Option::is_none")]
1153    pub model_id: Option<String>,
1154    /// The native cpu architecture of the device.
1155    #[serde(default, skip_serializing_if = "Option::is_none")]
1156    pub arch: Option<String>,
1157    /// The current battery level (0-100).
1158    #[serde(default, skip_serializing_if = "Option::is_none")]
1159    pub battery_level: Option<f32>,
1160    /// The current screen orientation.
1161    #[serde(default, skip_serializing_if = "Option::is_none")]
1162    pub orientation: Option<Orientation>,
1163    /// Simulator/prod indicator.
1164    #[serde(default, skip_serializing_if = "Option::is_none")]
1165    pub simulator: Option<bool>,
1166    /// Total memory available in byts.
1167    #[serde(default, skip_serializing_if = "Option::is_none")]
1168    pub memory_size: Option<u64>,
1169    /// How much memory is still available in bytes.
1170    #[serde(default, skip_serializing_if = "Option::is_none")]
1171    pub free_memory: Option<u64>,
1172    /// How much memory is usable for the app in bytes.
1173    #[serde(default, skip_serializing_if = "Option::is_none")]
1174    pub usable_memory: Option<u64>,
1175    /// Total storage size of the device in bytes.
1176    #[serde(default, skip_serializing_if = "Option::is_none")]
1177    pub storage_size: Option<u64>,
1178    /// How much storage is free in bytes.
1179    #[serde(default, skip_serializing_if = "Option::is_none")]
1180    pub free_storage: Option<u64>,
1181    /// Total size of the attached external storage in bytes (eg: android SDK card).
1182    #[serde(default, skip_serializing_if = "Option::is_none")]
1183    pub external_storage_size: Option<u64>,
1184    /// Free size of the attached external storage in bytes (eg: android SDK card).
1185    #[serde(default, skip_serializing_if = "Option::is_none")]
1186    pub external_free_storage: Option<u64>,
1187    /// Optionally an indicator when the device was booted.
1188    #[serde(
1189        default,
1190        skip_serializing_if = "Option::is_none",
1191        with = "ts_rfc3339_opt"
1192    )]
1193    pub boot_time: Option<SystemTime>,
1194    /// The timezone of the device.
1195    #[serde(default, skip_serializing_if = "Option::is_none")]
1196    pub timezone: Option<String>,
1197    /// Additional arbitrary fields for forwards compatibility.
1198    #[serde(flatten)]
1199    pub other: Map<String, Value>,
1200}
1201
1202/// Holds operating system information.
1203#[derive(Serialize, Deserialize, Debug, Clone, Default, PartialEq)]
1204pub struct OsContext {
1205    /// The name of the operating system.
1206    #[serde(default, skip_serializing_if = "Option::is_none")]
1207    pub name: Option<String>,
1208    /// The version of the operating system.
1209    #[serde(default, skip_serializing_if = "Option::is_none")]
1210    pub version: Option<String>,
1211    /// The internal build number of the operating system.
1212    #[serde(default, skip_serializing_if = "Option::is_none")]
1213    pub build: Option<String>,
1214    /// The current kernel version.
1215    #[serde(default, skip_serializing_if = "Option::is_none")]
1216    pub kernel_version: Option<String>,
1217    /// An indicator if the os is rooted (mobile mostly).
1218    #[serde(default, skip_serializing_if = "Option::is_none")]
1219    pub rooted: Option<bool>,
1220    /// Additional arbitrary fields for forwards compatibility.
1221    #[serde(flatten)]
1222    pub other: Map<String, Value>,
1223}
1224
1225/// Holds information about the runtime.
1226#[derive(Serialize, Deserialize, Debug, Clone, Default, PartialEq)]
1227pub struct RuntimeContext {
1228    /// The name of the runtime (for instance JVM).
1229    #[serde(default, skip_serializing_if = "Option::is_none")]
1230    pub name: Option<String>,
1231    /// The version of the runtime.
1232    #[serde(default, skip_serializing_if = "Option::is_none")]
1233    pub version: Option<String>,
1234    /// Additional arbitrary fields for forwards compatibility.
1235    #[serde(flatten)]
1236    pub other: Map<String, Value>,
1237}
1238
1239/// Holds app information.
1240#[derive(Serialize, Deserialize, Debug, Clone, Default, PartialEq)]
1241pub struct AppContext {
1242    /// Optional start time of the app.
1243    #[serde(
1244        default,
1245        skip_serializing_if = "Option::is_none",
1246        with = "ts_rfc3339_opt"
1247    )]
1248    pub app_start_time: Option<SystemTime>,
1249    /// Optional device app hash (app specific device ID)
1250    #[serde(default, skip_serializing_if = "Option::is_none")]
1251    pub device_app_hash: Option<String>,
1252    /// Optional build identicator.
1253    #[serde(default, skip_serializing_if = "Option::is_none")]
1254    pub build_type: Option<String>,
1255    /// Optional app identifier (dotted bundle id).
1256    #[serde(default, skip_serializing_if = "Option::is_none")]
1257    pub app_identifier: Option<String>,
1258    /// Application name as it appears on the platform.
1259    #[serde(default, skip_serializing_if = "Option::is_none")]
1260    pub app_name: Option<String>,
1261    /// Application version as it appears on the platform.
1262    #[serde(default, skip_serializing_if = "Option::is_none")]
1263    pub app_version: Option<String>,
1264    /// Internal build ID as it appears on the platform.
1265    #[serde(default, skip_serializing_if = "Option::is_none")]
1266    pub app_build: Option<String>,
1267    /// Additional arbitrary fields for forwards compatibility.
1268    #[serde(flatten)]
1269    pub other: Map<String, Value>,
1270}
1271
1272/// Holds information about the web browser.
1273#[derive(Serialize, Deserialize, Debug, Clone, Default, PartialEq)]
1274pub struct BrowserContext {
1275    /// The name of the browser (for instance "Chrome").
1276    #[serde(default, skip_serializing_if = "Option::is_none")]
1277    pub name: Option<String>,
1278    /// The version of the browser.
1279    #[serde(default, skip_serializing_if = "Option::is_none")]
1280    pub version: Option<String>,
1281    /// Additional arbitrary fields for forwards compatibility.
1282    #[serde(flatten)]
1283    pub other: Map<String, Value>,
1284}
1285
1286/// GPU context describes the GPU of the device.
1287#[derive(Serialize, Deserialize, Debug, Clone, Default, PartialEq)]
1288pub struct GpuContext {
1289    /// The name of the graphics device.
1290    pub name: String,
1291    /// The Version of the graphics device.
1292    #[serde(default, skip_serializing_if = "Option::is_none")]
1293    pub version: Option<String>,
1294    /// The version of the graphic device driver.
1295    #[serde(default, skip_serializing_if = "Option::is_none")]
1296    pub driver_version: Option<String>,
1297    /// The PCI identifier of the graphics device.
1298    #[serde(default, skip_serializing_if = "Option::is_none")]
1299    pub id: Option<String>,
1300    /// The PCI vendor identifier of the graphics device.
1301    #[serde(default, skip_serializing_if = "Option::is_none")]
1302    pub vendor_id: Option<String>,
1303    /// The vendor name as reported by the graphics device.
1304    #[serde(default, skip_serializing_if = "Option::is_none")]
1305    pub vendor_name: Option<String>,
1306    /// The total GPU memory available in Megabytes.
1307    #[serde(default, skip_serializing_if = "Option::is_none")]
1308    pub memory_size: Option<u32>,
1309    /// The device low-level API type. Examples: "Apple Metal" or "Direct3D11"
1310    #[serde(default, skip_serializing_if = "Option::is_none")]
1311    pub api_type: Option<String>,
1312    /// Whether the GPU has multi-threaded rendering or not.
1313    #[serde(default, skip_serializing_if = "Option::is_none")]
1314    pub multi_threaded_rendering: Option<bool>,
1315    /// The Non-Power-Of-Two-Support support.
1316    #[serde(default, skip_serializing_if = "Option::is_none")]
1317    pub npot_support: Option<bool>,
1318    /// Largest size of a texture that is supported by the graphics hardware.
1319    #[serde(default, skip_serializing_if = "Option::is_none")]
1320    pub max_texture_size: Option<u32>,
1321    /// Approximate "shader capability" level of the graphics device. For example,
1322    /// `Shader Model 2.0, OpenGL ES 3.0, Metal / OpenGL ES 3.1, 27 (unknown)`.
1323    #[serde(default, skip_serializing_if = "Option::is_none")]
1324    pub graphics_shader_level: Option<String>,
1325    /// Is GPU draw call instancing supported?
1326    #[serde(default, skip_serializing_if = "Option::is_none")]
1327    pub supports_draw_call_instancing: Option<bool>,
1328    /// Is ray tracing available on the device?
1329    #[serde(default, skip_serializing_if = "Option::is_none")]
1330    pub supports_ray_tracing: Option<bool>,
1331    /// Are compute shaders available on the device?
1332    #[serde(default, skip_serializing_if = "Option::is_none")]
1333    pub supports_compute_shaders: Option<bool>,
1334    /// Are geometry shaders available on the device?
1335    #[serde(default, skip_serializing_if = "Option::is_none")]
1336    pub supports_geometry_shaders: Option<bool>,
1337    /// Additional arbitrary fields for forwards compatibility.
1338    #[serde(flatten)]
1339    pub other: Map<String, Value>,
1340}
1341
1342/// OpenTelemetry context
1343#[derive(Serialize, Deserialize, Debug, Clone, Default, PartialEq)]
1344pub struct OtelContext {
1345    /// OpenTelemetry [general
1346    /// attributes](https://opentelemetry.io/docs/specs/semconv/general/attributes/).
1347    #[serde(default, skip_serializing_if = "Map::is_empty")]
1348    pub attributes: Map<String, Value>,
1349    /// OpenTelemetry [resource attributes](https://opentelemetry.io/docs/specs/semconv/resource/),
1350    /// describing the entity producing telemetry.
1351    #[serde(default, skip_serializing_if = "Map::is_empty")]
1352    pub resource: Map<String, Value>,
1353    /// Additional arbitrary fields for forwards compatibility.
1354    #[serde(flatten)]
1355    pub other: Map<String, Value>,
1356}
1357
1358/// Holds information about an HTTP response.
1359#[derive(Serialize, Deserialize, Debug, Clone, Default, PartialEq)]
1360pub struct ResponseContext {
1361    /// The unparsed cookie values.
1362    #[serde(default, skip_serializing_if = "Option::is_none")]
1363    pub cookies: Option<String>,
1364    /// A map of submitted headers.
1365    ///
1366    /// If a header appears multiple times, it needs to be merged according to the HTTP standard
1367    /// for header merging. Header names are treated case-insensitively by Sentry.
1368    #[serde(default, skip_serializing_if = "Map::is_empty")]
1369    pub headers: Map<String, String>,
1370    /// The HTTP response status code.
1371    #[serde(default, skip_serializing_if = "Option::is_none")]
1372    pub status_code: Option<u64>,
1373    /// The response body size in bytes.
1374    #[serde(default, skip_serializing_if = "Option::is_none")]
1375    pub body_size: Option<u64>,
1376    /// Response data in any format that makes sense.
1377    #[serde(default, skip_serializing_if = "Option::is_none")]
1378    pub data: Option<Value>,
1379}
1380
1381/// Holds the identifier for a Span
1382#[derive(Serialize, Deserialize, Copy, Clone, Eq, PartialEq, Hash)]
1383#[serde(try_from = "String", into = "String")]
1384pub struct SpanId([u8; 8]);
1385
1386impl Default for SpanId {
1387    fn default() -> Self {
1388        Self(rand::random())
1389    }
1390}
1391
1392impl fmt::Display for SpanId {
1393    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
1394        write!(fmt, "{}", hex::encode(self.0))
1395    }
1396}
1397
1398impl fmt::Debug for SpanId {
1399    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
1400        write!(fmt, "SpanId({self})")
1401    }
1402}
1403
1404impl From<SpanId> for String {
1405    fn from(span_id: SpanId) -> Self {
1406        span_id.to_string()
1407    }
1408}
1409
1410impl str::FromStr for SpanId {
1411    type Err = hex::FromHexError;
1412
1413    fn from_str(input: &str) -> Result<Self, Self::Err> {
1414        let mut buf = [0; 8];
1415        hex::decode_to_slice(input, &mut buf)?;
1416        Ok(Self(buf))
1417    }
1418}
1419
1420impl TryFrom<String> for SpanId {
1421    type Error = hex::FromHexError;
1422
1423    fn try_from(value: String) -> Result<Self, Self::Error> {
1424        value.parse()
1425    }
1426}
1427
1428impl From<[u8; 8]> for SpanId {
1429    fn from(value: [u8; 8]) -> Self {
1430        Self(value)
1431    }
1432}
1433
1434/// Holds the identifier for a Trace
1435#[derive(Serialize, Deserialize, Copy, Clone, Eq, PartialEq, Hash)]
1436#[serde(try_from = "String", into = "String")]
1437pub struct TraceId([u8; 16]);
1438
1439impl Default for TraceId {
1440    fn default() -> Self {
1441        Self(rand::random())
1442    }
1443}
1444
1445impl fmt::Display for TraceId {
1446    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
1447        write!(fmt, "{}", hex::encode(self.0))
1448    }
1449}
1450
1451impl fmt::Debug for TraceId {
1452    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
1453        write!(fmt, "TraceId({self})")
1454    }
1455}
1456
1457impl From<TraceId> for String {
1458    fn from(trace_id: TraceId) -> Self {
1459        trace_id.to_string()
1460    }
1461}
1462
1463impl str::FromStr for TraceId {
1464    type Err = hex::FromHexError;
1465
1466    fn from_str(input: &str) -> Result<Self, Self::Err> {
1467        let mut buf = [0; 16];
1468        hex::decode_to_slice(input, &mut buf)?;
1469        Ok(Self(buf))
1470    }
1471}
1472
1473impl TryFrom<String> for TraceId {
1474    type Error = hex::FromHexError;
1475
1476    fn try_from(value: String) -> Result<Self, Self::Error> {
1477        value.parse()
1478    }
1479}
1480
1481impl From<[u8; 16]> for TraceId {
1482    fn from(value: [u8; 16]) -> Self {
1483        Self(value)
1484    }
1485}
1486
1487/// Holds information about a tracing event.
1488#[derive(Serialize, Deserialize, Debug, Clone, Default, PartialEq)]
1489pub struct TraceContext {
1490    /// The ID of the trace event
1491    #[serde(default)]
1492    pub span_id: SpanId,
1493    /// Determines which trace the transaction belongs to.
1494    #[serde(default)]
1495    pub trace_id: TraceId,
1496    /// Determines the parent of this transaction if any.
1497    #[serde(default, skip_serializing_if = "Option::is_none")]
1498    pub parent_span_id: Option<SpanId>,
1499    /// Short code identifying the type of operation the transaction is measuring.
1500    #[serde(default, skip_serializing_if = "Option::is_none")]
1501    pub op: Option<String>,
1502    /// Human readable detail description.
1503    #[serde(default, skip_serializing_if = "Option::is_none")]
1504    pub description: Option<String>,
1505    /// Describes the status of the span (e.g. `ok`, `cancelled`, etc.)
1506    #[serde(default, skip_serializing_if = "Option::is_none")]
1507    pub status: Option<SpanStatus>,
1508    /// Describes what created the transaction. See the [develop
1509    /// docs](https://develop.sentry.dev/sdk/telemetry/traces/trace-origin/) for more information.
1510    #[serde(default, skip_serializing_if = "Option::is_none")]
1511    pub origin: Option<String>,
1512    /// Optional data attributes to be associated with the transaction.
1513    #[serde(default, skip_serializing_if = "Map::is_empty")]
1514    pub data: Map<String, Value>,
1515}
1516
1517macro_rules! into_context {
1518    ($kind:ident, $ty:ty) => {
1519        impl From<$ty> for Context {
1520            fn from(data: $ty) -> Self {
1521                Context::$kind(Box::new(data))
1522            }
1523        }
1524    };
1525}
1526
1527into_context!(App, AppContext);
1528into_context!(Device, DeviceContext);
1529into_context!(Os, OsContext);
1530into_context!(Runtime, RuntimeContext);
1531into_context!(Browser, BrowserContext);
1532into_context!(Trace, TraceContext);
1533into_context!(Gpu, GpuContext);
1534into_context!(Otel, OtelContext);
1535into_context!(Response, ResponseContext);
1536
1537const INFERABLE_CONTEXTS: &[&str] = &[
1538    "device", "os", "runtime", "app", "browser", "trace", "gpu", "otel", "response",
1539];
1540
1541struct ContextsVisitor;
1542
1543impl<'de> de::Visitor<'de> for ContextsVisitor {
1544    type Value = Map<String, Context>;
1545
1546    fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
1547        formatter.write_str("contexts object")
1548    }
1549
1550    fn visit_map<A>(self, mut access: A) -> Result<Self::Value, A::Error>
1551    where
1552        A: de::MapAccess<'de>,
1553    {
1554        let mut map: Map<String, Context> = Map::new();
1555
1556        while let Some((key, mut value)) = access.next_entry::<String, Value>()? {
1557            let typed_value = value
1558                .as_object_mut()
1559                .map(|ctx| {
1560                    if !ctx.contains_key("type") {
1561                        let type_key = if INFERABLE_CONTEXTS.contains(&key.as_str()) {
1562                            key.clone().into()
1563                        } else {
1564                            Value::String("unknown".into())
1565                        };
1566                        ctx.insert(String::from("type"), type_key);
1567                    }
1568                    ctx.to_owned()
1569                })
1570                .ok_or_else(|| de::Error::custom("expected valid `context` object"))?;
1571
1572            match serde_json::from_value(serde_json::to_value(typed_value).unwrap()) {
1573                Ok(context) => {
1574                    map.insert(key, context);
1575                }
1576                Err(e) => return Err(de::Error::custom(e.to_string())),
1577            }
1578        }
1579
1580        Ok(map)
1581    }
1582}
1583
1584fn deserialize_contexts<'de, D>(deserializer: D) -> Result<Map<String, Context>, D::Error>
1585where
1586    D: Deserializer<'de>,
1587{
1588    deserializer.deserialize_map(ContextsVisitor {})
1589}
1590
1591mod event {
1592    use super::*;
1593
1594    pub fn default_id() -> Uuid {
1595        crate::random_uuid()
1596    }
1597
1598    pub fn serialize_id<S: Serializer>(uuid: &Uuid, serializer: S) -> Result<S::Ok, S::Error> {
1599        serializer.serialize_some(&uuid.as_simple().to_string())
1600    }
1601
1602    pub fn default_level() -> Level {
1603        Level::Error
1604    }
1605
1606    pub fn default_platform() -> Cow<'static, str> {
1607        Cow::Borrowed("other")
1608    }
1609
1610    pub fn is_default_platform(value: &str) -> bool {
1611        value == "other"
1612    }
1613
1614    static DEFAULT_FINGERPRINT: &[Cow<'static, str>] = &[Cow::Borrowed("{{ default }}")];
1615
1616    pub fn default_fingerprint<'a>() -> Cow<'a, [Cow<'a, str>]> {
1617        Cow::Borrowed(DEFAULT_FINGERPRINT)
1618    }
1619
1620    pub fn is_default_fingerprint(fp: &[Cow<'_, str>]) -> bool {
1621        fp.len() == 1 && ((fp)[0] == "{{ default }}" || (fp)[0] == "{{default}}")
1622    }
1623}
1624
1625/// Represents a full event for Sentry.
1626#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
1627pub struct Event<'a> {
1628    /// The ID of the event
1629    #[serde(default = "event::default_id", serialize_with = "event::serialize_id")]
1630    pub event_id: Uuid,
1631    /// The level of the event (defaults to error)
1632    #[serde(
1633        default = "event::default_level",
1634        skip_serializing_if = "Level::is_error"
1635    )]
1636    pub level: Level,
1637    /// An optional fingerprint configuration to override the default.
1638    #[serde(
1639        default = "event::default_fingerprint",
1640        skip_serializing_if = "event::is_default_fingerprint"
1641    )]
1642    pub fingerprint: Cow<'a, [Cow<'a, str>]>,
1643    /// The culprit of the event.
1644    #[serde(default, skip_serializing_if = "Option::is_none")]
1645    pub culprit: Option<String>,
1646    /// The transaction name of the event.
1647    #[serde(default, skip_serializing_if = "Option::is_none")]
1648    pub transaction: Option<String>,
1649    /// A message to be sent with the event.
1650    #[serde(default, skip_serializing_if = "Option::is_none")]
1651    pub message: Option<String>,
1652    /// Optionally a log entry that can be used instead of the message for
1653    /// more complex cases.
1654    #[serde(default, skip_serializing_if = "Option::is_none")]
1655    pub logentry: Option<LogEntry>,
1656    /// Optionally the name of the logger that created this event.
1657    #[serde(default, skip_serializing_if = "Option::is_none")]
1658    pub logger: Option<String>,
1659    /// Optionally a name to version mapping of installed modules.
1660    #[serde(default, skip_serializing_if = "Map::is_empty")]
1661    pub modules: Map<String, String>,
1662    /// A platform identifier for this event.
1663    #[serde(
1664        default = "event::default_platform",
1665        skip_serializing_if = "event::is_default_platform"
1666    )]
1667    pub platform: Cow<'a, str>,
1668    /// The timestamp of when the event was created.
1669    ///
1670    /// This can be set to `None` in which case the server will set a timestamp.
1671    #[serde(default = "SystemTime::now", with = "ts_seconds_float")]
1672    pub timestamp: SystemTime,
1673    /// Optionally the server (or device) name of this event.
1674    #[serde(default, skip_serializing_if = "Option::is_none")]
1675    pub server_name: Option<Cow<'a, str>>,
1676    /// A release identifier.
1677    #[serde(default, skip_serializing_if = "Option::is_none")]
1678    pub release: Option<Cow<'a, str>>,
1679    /// An optional distribution identifier.
1680    #[serde(default, skip_serializing_if = "Option::is_none")]
1681    pub dist: Option<Cow<'a, str>>,
1682    /// An optional environment identifier.
1683    #[serde(default, skip_serializing_if = "Option::is_none")]
1684    pub environment: Option<Cow<'a, str>>,
1685    /// Optionally user data to be sent along.
1686    #[serde(default, skip_serializing_if = "Option::is_none")]
1687    pub user: Option<User>,
1688    /// Optionally HTTP request data to be sent along.
1689    #[serde(default, skip_serializing_if = "Option::is_none")]
1690    pub request: Option<Request>,
1691    /// Optional contexts.
1692    #[serde(
1693        default,
1694        skip_serializing_if = "Map::is_empty",
1695        deserialize_with = "deserialize_contexts"
1696    )]
1697    pub contexts: Map<String, Context>,
1698    /// List of breadcrumbs to send along.
1699    #[serde(default, skip_serializing_if = "Values::is_empty")]
1700    pub breadcrumbs: Values<Breadcrumb>,
1701    /// Exceptions to be attached (one or multiple if chained).
1702    #[serde(default, skip_serializing_if = "Values::is_empty")]
1703    pub exception: Values<Exception>,
1704    /// A single stacktrace (deprecated)
1705    #[serde(default, skip_serializing_if = "Option::is_none")]
1706    pub stacktrace: Option<Stacktrace>,
1707    /// Simplified template error location info
1708    #[serde(default, skip_serializing_if = "Option::is_none")]
1709    pub template: Option<TemplateInfo>,
1710    /// A list of threads.
1711    #[serde(default, skip_serializing_if = "Values::is_empty")]
1712    pub threads: Values<Thread>,
1713    /// Optional tags to be attached to the event.
1714    #[serde(default, skip_serializing_if = "Map::is_empty")]
1715    pub tags: Map<String, String>,
1716    /// Optional extra information to be sent with the event.
1717    #[serde(default, skip_serializing_if = "Map::is_empty")]
1718    pub extra: Map<String, Value>,
1719    /// Debug meta information.
1720    #[serde(default, skip_serializing_if = "DebugMeta::is_empty")]
1721    pub debug_meta: Cow<'a, DebugMeta>,
1722    /// SDK metadata
1723    #[serde(default, skip_serializing_if = "Option::is_none")]
1724    pub sdk: Option<Cow<'a, ClientSdkInfo>>,
1725}
1726
1727impl Default for Event<'_> {
1728    fn default() -> Self {
1729        Event {
1730            event_id: event::default_id(),
1731            level: event::default_level(),
1732            fingerprint: event::default_fingerprint(),
1733            culprit: Default::default(),
1734            transaction: Default::default(),
1735            message: Default::default(),
1736            logentry: Default::default(),
1737            logger: Default::default(),
1738            modules: Default::default(),
1739            platform: event::default_platform(),
1740            timestamp: SystemTime::now(),
1741            server_name: Default::default(),
1742            release: Default::default(),
1743            dist: Default::default(),
1744            environment: Default::default(),
1745            user: Default::default(),
1746            request: Default::default(),
1747            contexts: Default::default(),
1748            breadcrumbs: Default::default(),
1749            exception: Default::default(),
1750            stacktrace: Default::default(),
1751            template: Default::default(),
1752            threads: Default::default(),
1753            tags: Default::default(),
1754            extra: Default::default(),
1755            debug_meta: Default::default(),
1756            sdk: Default::default(),
1757        }
1758    }
1759}
1760
1761impl<'a> Event<'a> {
1762    /// Creates a new event with the current timestamp and random id.
1763    pub fn new() -> Event<'a> {
1764        Default::default()
1765    }
1766
1767    /// Creates a fully owned version of the event.
1768    pub fn into_owned(self) -> Event<'static> {
1769        Event {
1770            event_id: self.event_id,
1771            level: self.level,
1772            fingerprint: Cow::Owned(
1773                self.fingerprint
1774                    .iter()
1775                    .map(|x| Cow::Owned(x.to_string()))
1776                    .collect(),
1777            ),
1778            culprit: self.culprit,
1779            transaction: self.transaction,
1780            message: self.message,
1781            logentry: self.logentry,
1782            logger: self.logger,
1783            modules: self.modules,
1784            platform: Cow::Owned(self.platform.into_owned()),
1785            timestamp: self.timestamp,
1786            server_name: self.server_name.map(|x| Cow::Owned(x.into_owned())),
1787            release: self.release.map(|x| Cow::Owned(x.into_owned())),
1788            dist: self.dist.map(|x| Cow::Owned(x.into_owned())),
1789            environment: self.environment.map(|x| Cow::Owned(x.into_owned())),
1790            user: self.user,
1791            request: self.request,
1792            contexts: self.contexts,
1793            breadcrumbs: self.breadcrumbs,
1794            exception: self.exception,
1795            stacktrace: self.stacktrace,
1796            template: self.template,
1797            threads: self.threads,
1798            tags: self.tags,
1799            extra: self.extra,
1800            debug_meta: Cow::Owned(self.debug_meta.into_owned()),
1801            sdk: self.sdk.map(|x| Cow::Owned(x.into_owned())),
1802        }
1803    }
1804}
1805
1806impl fmt::Display for Event<'_> {
1807    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1808        write!(
1809            f,
1810            "Event(id: {}, ts: {})",
1811            self.event_id,
1812            crate::utils::to_rfc3339(&self.timestamp)
1813        )
1814    }
1815}
1816
1817/// Represents a tracing span.
1818#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
1819pub struct Span {
1820    /// The ID of the span
1821    #[serde(default)]
1822    pub span_id: SpanId,
1823    /// Determines which trace the span belongs to.
1824    #[serde(default)]
1825    pub trace_id: TraceId,
1826    /// Determines the parent of this span, if any.
1827    #[serde(default, skip_serializing_if = "Option::is_none")]
1828    pub parent_span_id: Option<SpanId>,
1829    /// Determines whether this span is generated in the same process as its parent, if any.
1830    #[serde(default, skip_serializing_if = "Option::is_none")]
1831    pub same_process_as_parent: Option<bool>,
1832    /// Short code identifying the type of operation the span is measuring.
1833    #[serde(default, skip_serializing_if = "Option::is_none")]
1834    pub op: Option<String>,
1835    /// Longer description of the span's operation, which uniquely identifies the span
1836    /// but is consistent across instances of the span.
1837    #[serde(default, skip_serializing_if = "Option::is_none")]
1838    pub description: Option<String>,
1839    /// The timestamp at the measuring of the span finished.
1840    #[serde(
1841        default,
1842        skip_serializing_if = "Option::is_none",
1843        with = "ts_rfc3339_opt"
1844    )]
1845    pub timestamp: Option<SystemTime>,
1846    /// The timestamp at the measuring of the span started.
1847    #[serde(default = "SystemTime::now", with = "ts_seconds_float")]
1848    pub start_timestamp: SystemTime,
1849    /// Describes the status of the span (e.g. `ok`, `cancelled`, etc.)
1850    #[serde(default, skip_serializing_if = "Option::is_none")]
1851    pub status: Option<SpanStatus>,
1852    /// Optional tags to be attached to the span.
1853    #[serde(default, skip_serializing_if = "Map::is_empty")]
1854    pub tags: Map<String, String>,
1855    /// Optional extra information to be sent with the span.
1856    #[serde(default, skip_serializing_if = "Map::is_empty")]
1857    pub data: Map<String, Value>,
1858}
1859
1860impl Default for Span {
1861    fn default() -> Self {
1862        Span {
1863            span_id: Default::default(),
1864            trace_id: Default::default(),
1865            timestamp: Default::default(),
1866            tags: Default::default(),
1867            start_timestamp: SystemTime::now(),
1868            description: Default::default(),
1869            status: Default::default(),
1870            parent_span_id: Default::default(),
1871            same_process_as_parent: Default::default(),
1872            op: Default::default(),
1873            data: Default::default(),
1874        }
1875    }
1876}
1877
1878impl Span {
1879    /// Creates a new span with the current timestamp and random id.
1880    pub fn new() -> Span {
1881        Default::default()
1882    }
1883
1884    /// Finalizes the span with the provided timestamp.
1885    pub fn finish_with_timestamp(&mut self, timestamp: SystemTime) {
1886        self.timestamp = Some(timestamp);
1887    }
1888
1889    /// Finalizes the span.
1890    pub fn finish(&mut self) {
1891        self.timestamp = Some(SystemTime::now());
1892    }
1893}
1894
1895impl fmt::Display for Span {
1896    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1897        write!(
1898            f,
1899            "Span(id: {}, ts: {})",
1900            self.span_id,
1901            crate::utils::to_rfc3339(&self.start_timestamp)
1902        )
1903    }
1904}
1905
1906/// An error used when parsing `SpanStatus`.
1907#[derive(Debug, Error)]
1908#[error("invalid status")]
1909pub struct ParseStatusError;
1910
1911/// The status of a Span.
1912#[derive(Serialize, Deserialize, Debug, Copy, Clone, PartialEq, Eq, Hash)]
1913#[non_exhaustive]
1914pub enum SpanStatus {
1915    /// The operation completed successfully.
1916    #[serde(rename = "ok")]
1917    Ok,
1918    /// Deadline expired before operation could complete.
1919    #[serde(rename = "deadline_exceeded")]
1920    DeadlineExceeded,
1921    /// 401 Unauthorized (actually does mean unauthenticated according to RFC 7235)
1922    #[serde(rename = "unauthenticated")]
1923    Unauthenticated,
1924    /// 403 Forbidden
1925    #[serde(rename = "permission_denied")]
1926    PermissionDenied,
1927    /// 404 Not Found. Some requested entity (file or directory) was not found.
1928    #[serde(rename = "not_found")]
1929    NotFound,
1930    /// 429 Too Many Requests
1931    #[serde(rename = "resource_exhausted")]
1932    ResourceExhausted,
1933    /// Client specified an invalid argument. 4xx.
1934    #[serde(rename = "invalid_argument")]
1935    InvalidArgument,
1936    /// 501 Not Implemented
1937    #[serde(rename = "unimplemented")]
1938    Unimplemented,
1939    /// 503 Service Unavailable
1940    #[serde(rename = "unavailable")]
1941    Unavailable,
1942    /// Other/generic 5xx.
1943    #[serde(rename = "internal_error")]
1944    InternalError,
1945    /// Unknown. Any non-standard HTTP status code.
1946    #[serde(rename = "unknown_error")]
1947    UnknownError,
1948    /// The operation was cancelled (typically by the user).
1949    #[serde(rename = "cancelled")]
1950    Cancelled,
1951    /// Already exists (409)
1952    #[serde(rename = "already_exists")]
1953    AlreadyExists,
1954    /// Operation was rejected because the system is not in a state required for the operation's
1955    #[serde(rename = "failed_precondition")]
1956    FailedPrecondition,
1957    /// The operation was aborted, typically due to a concurrency issue.
1958    #[serde(rename = "aborted")]
1959    Aborted,
1960    /// Operation was attempted past the valid range.
1961    #[serde(rename = "out_of_range")]
1962    OutOfRange,
1963    /// Unrecoverable data loss or corruption
1964    #[serde(rename = "data_loss")]
1965    DataLoss,
1966}
1967
1968impl str::FromStr for SpanStatus {
1969    type Err = ParseStatusError;
1970
1971    fn from_str(s: &str) -> Result<SpanStatus, Self::Err> {
1972        Ok(match s {
1973            "ok" => SpanStatus::Ok,
1974            "deadline_exceeded" => SpanStatus::DeadlineExceeded,
1975            "unauthenticated" => SpanStatus::Unauthenticated,
1976            "permission_denied" => SpanStatus::PermissionDenied,
1977            "not_found" => SpanStatus::NotFound,
1978            "resource_exhausted" => SpanStatus::ResourceExhausted,
1979            "invalid_argument" => SpanStatus::InvalidArgument,
1980            "unimplemented" => SpanStatus::Unimplemented,
1981            "unavailable" => SpanStatus::Unavailable,
1982            "internal_error" => SpanStatus::InternalError,
1983            "unknown_error" => SpanStatus::UnknownError,
1984            "cancelled" => SpanStatus::Cancelled,
1985            "already_exists" => SpanStatus::AlreadyExists,
1986            "failed_precondition" => SpanStatus::FailedPrecondition,
1987            "aborted" => SpanStatus::Aborted,
1988            "out_of_range" => SpanStatus::OutOfRange,
1989            "data_loss" => SpanStatus::DataLoss,
1990            _ => return Err(ParseStatusError),
1991        })
1992    }
1993}
1994
1995impl fmt::Display for SpanStatus {
1996    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1997        match self {
1998            SpanStatus::Ok => write!(f, "ok"),
1999            SpanStatus::DeadlineExceeded => write!(f, "deadline_exceeded"),
2000            SpanStatus::Unauthenticated => write!(f, "unauthenticated"),
2001            SpanStatus::PermissionDenied => write!(f, "permission_denied"),
2002            SpanStatus::NotFound => write!(f, "not_found"),
2003            SpanStatus::ResourceExhausted => write!(f, "resource_exhausted"),
2004            SpanStatus::InvalidArgument => write!(f, "invalid_argument"),
2005            SpanStatus::Unimplemented => write!(f, "unimplemented"),
2006            SpanStatus::Unavailable => write!(f, "unavailable"),
2007            SpanStatus::InternalError => write!(f, "internal_error"),
2008            SpanStatus::UnknownError => write!(f, "unknown_error"),
2009            SpanStatus::Cancelled => write!(f, "cancelled"),
2010            SpanStatus::AlreadyExists => write!(f, "already_exists"),
2011            SpanStatus::FailedPrecondition => write!(f, "failed_precondition"),
2012            SpanStatus::Aborted => write!(f, "aborted"),
2013            SpanStatus::OutOfRange => write!(f, "out_of_range"),
2014            SpanStatus::DataLoss => write!(f, "data_loss"),
2015        }
2016    }
2017}
2018
2019/// Represents a tracing transaction.
2020#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
2021pub struct Transaction<'a> {
2022    /// The ID of the event
2023    #[serde(default = "event::default_id", serialize_with = "event::serialize_id")]
2024    pub event_id: Uuid,
2025    /// The transaction name.
2026    #[serde(
2027        rename = "transaction",
2028        default,
2029        skip_serializing_if = "Option::is_none"
2030    )]
2031    pub name: Option<String>,
2032    /// A release identifier.
2033    #[serde(default, skip_serializing_if = "Option::is_none")]
2034    pub release: Option<Cow<'a, str>>,
2035    /// An optional environment identifier.
2036    #[serde(default, skip_serializing_if = "Option::is_none")]
2037    pub environment: Option<Cow<'a, str>>,
2038    /// Optionally user data to be sent along.
2039    #[serde(default, skip_serializing_if = "Option::is_none")]
2040    pub user: Option<User>,
2041    /// Optional tags to be attached to the event.
2042    #[serde(default, skip_serializing_if = "Map::is_empty")]
2043    pub tags: Map<String, String>,
2044    /// Optional extra information to be sent with the event.
2045    #[serde(default, skip_serializing_if = "Map::is_empty")]
2046    pub extra: Map<String, Value>,
2047    /// SDK metadata
2048    #[serde(default, skip_serializing_if = "Option::is_none")]
2049    pub sdk: Option<Cow<'a, ClientSdkInfo>>,
2050    /// A platform identifier for this event.
2051    #[serde(
2052        default = "event::default_platform",
2053        skip_serializing_if = "event::is_default_platform"
2054    )]
2055    pub platform: Cow<'a, str>,
2056    /// The end time of the transaction.
2057    #[serde(
2058        default,
2059        skip_serializing_if = "Option::is_none",
2060        with = "ts_rfc3339_opt"
2061    )]
2062    pub timestamp: Option<SystemTime>,
2063    /// The start time of the transaction.
2064    #[serde(default = "SystemTime::now", with = "ts_seconds_float")]
2065    pub start_timestamp: SystemTime,
2066    /// The collection of finished spans part of this transaction.
2067    pub spans: Vec<Span>,
2068    /// Optional contexts.
2069    #[serde(
2070        default,
2071        skip_serializing_if = "Map::is_empty",
2072        deserialize_with = "deserialize_contexts"
2073    )]
2074    pub contexts: Map<String, Context>,
2075    /// Optionally HTTP request data to be sent along.
2076    #[serde(default, skip_serializing_if = "Option::is_none")]
2077    pub request: Option<Request>,
2078    /// Optionally the server (or device) name of this event.
2079    #[serde(default, skip_serializing_if = "Option::is_none")]
2080    pub server_name: Option<Cow<'a, str>>,
2081}
2082
2083impl Default for Transaction<'_> {
2084    fn default() -> Self {
2085        Transaction {
2086            event_id: event::default_id(),
2087            name: Default::default(),
2088            user: Default::default(),
2089            tags: Default::default(),
2090            extra: Default::default(),
2091            release: Default::default(),
2092            environment: Default::default(),
2093            sdk: Default::default(),
2094            platform: event::default_platform(),
2095            timestamp: Default::default(),
2096            start_timestamp: SystemTime::now(),
2097            spans: Default::default(),
2098            contexts: Default::default(),
2099            request: Default::default(),
2100            server_name: Default::default(),
2101        }
2102    }
2103}
2104
2105impl<'a> Transaction<'a> {
2106    /// Creates a new span transaction the current timestamp and random id.
2107    pub fn new() -> Transaction<'a> {
2108        Default::default()
2109    }
2110
2111    /// Creates a fully owned version of the transaction.
2112    pub fn into_owned(self) -> Transaction<'static> {
2113        Transaction {
2114            event_id: self.event_id,
2115            name: self.name,
2116            user: self.user,
2117            tags: self.tags,
2118            extra: self.extra,
2119            release: self.release.map(|x| Cow::Owned(x.into_owned())),
2120            environment: self.environment.map(|x| Cow::Owned(x.into_owned())),
2121            sdk: self.sdk.map(|x| Cow::Owned(x.into_owned())),
2122            platform: Cow::Owned(self.platform.into_owned()),
2123            timestamp: self.timestamp,
2124            start_timestamp: self.start_timestamp,
2125            spans: self.spans,
2126            contexts: self.contexts,
2127            request: self.request,
2128            server_name: self.server_name.map(|x| Cow::Owned(x.into_owned())),
2129        }
2130    }
2131
2132    /// Finalizes the transaction to be dispatched.
2133    pub fn finish(&mut self) {
2134        self.timestamp = Some(SystemTime::now());
2135    }
2136
2137    /// Finalizes the transaction to be dispatched with the given end timestamp.
2138    pub fn finish_with_timestamp(&mut self, timestamp: SystemTime) {
2139        self.timestamp = Some(timestamp);
2140    }
2141}
2142
2143impl fmt::Display for Transaction<'_> {
2144    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
2145        write!(
2146            f,
2147            "Transaction(id: {}, ts: {})",
2148            self.event_id,
2149            crate::utils::to_rfc3339(&self.start_timestamp)
2150        )
2151    }
2152}
2153
2154/// A single [structured log](https://docs.sentry.io/product/explore/logs/).
2155#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
2156pub struct Log {
2157    /// The severity of the log (required).
2158    pub level: LogLevel,
2159    /// The log body/message (required).
2160    pub body: String,
2161    /// The ID of the Trace in which this log happened (required).
2162    #[serde(default, skip_serializing_if = "Option::is_none")]
2163    pub trace_id: Option<TraceId>,
2164    /// The timestamp of the log (required).
2165    #[serde(with = "ts_seconds_float")]
2166    pub timestamp: SystemTime,
2167    /// The severity number of the log.
2168    #[serde(default, skip_serializing_if = "Option::is_none")]
2169    pub severity_number: Option<LogSeverityNumber>,
2170    /// Additional arbitrary attributes attached to the log.
2171    #[serde(default, skip_serializing_if = "Map::is_empty")]
2172    pub attributes: Map<String, LogAttribute>,
2173}
2174
2175/// Indicates the severity of a log, according to the
2176/// OpenTelemetry [`SeverityText`](https://opentelemetry.io/docs/specs/otel/logs/data-model/#field-severitytext) spec.
2177#[derive(Serialize, Deserialize, Copy, Clone, Debug, PartialEq)]
2178#[serde(rename_all = "lowercase")]
2179pub enum LogLevel {
2180    /// A fine-grained debugging event.
2181    Trace,
2182    /// A debugging event.
2183    Debug,
2184    /// An informational event. Indicates that an event happened.
2185    Info,
2186    /// A warning event. Not an error but is likely more important than an informational event.
2187    Warn,
2188    /// An error event. Something went wrong.
2189    Error,
2190    /// A fatal error such as application or system crash.
2191    Fatal,
2192}
2193
2194/// A number indicating the severity of a log, according to the OpenTelemetry
2195/// [`SeverityNumber`](https://opentelemetry.io/docs/specs/otel/logs/data-model/#field-severitynumber) spec.
2196#[derive(Serialize, Deserialize, Copy, Clone, Debug, PartialEq)]
2197pub struct LogSeverityNumber(u8);
2198
2199impl LogSeverityNumber {
2200    /// The minimum severity number.
2201    pub const MIN: u8 = 1;
2202    /// The maximum severity number.
2203    pub const MAX: u8 = 24;
2204}
2205
2206impl TryFrom<u8> for LogSeverityNumber {
2207    type Error = String;
2208
2209    fn try_from(value: u8) -> Result<Self, Self::Error> {
2210        if (LogSeverityNumber::MIN..=LogSeverityNumber::MAX).contains(&value) {
2211            Ok(Self(value))
2212        } else {
2213            Err(format!(
2214                "Log severity number must be between {} and {}",
2215                LogSeverityNumber::MIN,
2216                LogSeverityNumber::MAX
2217            ))
2218        }
2219    }
2220}
2221
2222/// An attribute that can be attached to a log.
2223#[derive(Clone, Debug, PartialEq)]
2224pub struct LogAttribute(pub Value);
2225
2226impl<T> From<T> for LogAttribute
2227where
2228    Value: From<T>,
2229{
2230    fn from(value: T) -> Self {
2231        Self(Value::from(value))
2232    }
2233}
2234
2235impl Serialize for LogAttribute {
2236    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
2237    where
2238        S: serde::Serializer,
2239    {
2240        use serde::ser::SerializeStruct;
2241        let mut state = serializer.serialize_struct("LogAttribute", 2)?;
2242
2243        match &self.0 {
2244            Value::String(s) => {
2245                state.serialize_field("value", s.as_str())?;
2246                state.serialize_field("type", "string")?;
2247            }
2248            Value::Number(n) => {
2249                if let Some(i) = n.as_i64() {
2250                    state.serialize_field("value", &i)?;
2251                    state.serialize_field("type", "integer")?;
2252                } else if let Some(u) = n.as_u64() {
2253                    // Converting to a f64 could lead to precision loss
2254                    state.serialize_field("value", &u.to_string())?;
2255                    state.serialize_field("type", "string")?;
2256                } else if let Some(f) = n.as_f64() {
2257                    state.serialize_field("value", &f)?;
2258                    state.serialize_field("type", "double")?;
2259                } else {
2260                    // This should be unreachable, as a `Value::Number` can only be built from an i64, u64 or f64
2261                    state.serialize_field("value", &n.to_string())?;
2262                    state.serialize_field("type", "string")?;
2263                }
2264            }
2265            Value::Bool(b) => {
2266                state.serialize_field("value", &b)?;
2267                state.serialize_field("type", "boolean")?;
2268            }
2269            // For any other type (Null, Array, Object), convert to string with JSON representation
2270            _ => {
2271                state.serialize_field("value", &self.0.to_string())?;
2272                state.serialize_field("type", "string")?;
2273            }
2274        }
2275
2276        state.end()
2277    }
2278}
2279
2280impl<'de> Deserialize<'de> for LogAttribute {
2281    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
2282    where
2283        D: serde::Deserializer<'de>,
2284    {
2285        use serde::de::{self, MapAccess, Visitor};
2286        use std::fmt;
2287
2288        struct LogAttributeVisitor;
2289
2290        impl<'de> Visitor<'de> for LogAttributeVisitor {
2291            type Value = LogAttribute;
2292
2293            fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
2294                formatter.write_str("a LogAttribute with value and type fields")
2295            }
2296
2297            fn visit_map<V>(self, mut map: V) -> Result<LogAttribute, V::Error>
2298            where
2299                V: MapAccess<'de>,
2300            {
2301                let mut value: Option<serde_json::Value> = None;
2302                let mut type_str: Option<String> = None;
2303
2304                while let Some(key) = map.next_key::<String>()? {
2305                    match key.as_str() {
2306                        "value" => {
2307                            if value.is_some() {
2308                                return Err(de::Error::duplicate_field("value"));
2309                            }
2310                            value = Some(map.next_value()?);
2311                        }
2312                        "type" => {
2313                            if type_str.is_some() {
2314                                return Err(de::Error::duplicate_field("type"));
2315                            }
2316                            type_str = Some(map.next_value()?);
2317                        }
2318                        _ => {
2319                            // Ignore unknown fields
2320                            let _: serde_json::Value = map.next_value()?;
2321                        }
2322                    }
2323                }
2324
2325                let value = value.ok_or_else(|| de::Error::missing_field("value"))?;
2326                let type_str = type_str.ok_or_else(|| de::Error::missing_field("type"))?;
2327
2328                match type_str.as_str() {
2329                    "string" => {
2330                        if !value.is_string() {
2331                            return Err(de::Error::custom(
2332                                "type is 'string' but value is not a string",
2333                            ));
2334                        }
2335                    }
2336                    "integer" => {
2337                        if !value.is_i64() {
2338                            return Err(de::Error::custom(
2339                                "type is 'integer' but value is not an integer",
2340                            ));
2341                        }
2342                    }
2343                    "double" => {
2344                        if !value.is_f64() {
2345                            return Err(de::Error::custom(
2346                                "type is 'double' but value is not a double",
2347                            ));
2348                        }
2349                    }
2350                    "boolean" => {
2351                        if !value.is_boolean() {
2352                            return Err(de::Error::custom(
2353                                "type is 'boolean' but value is not a boolean",
2354                            ));
2355                        }
2356                    }
2357                    _ => {
2358                        return Err(de::Error::custom(format!(
2359                        "expected type to be 'string' | 'integer' | 'double' | 'boolean', found {type_str}"
2360                    )))
2361                    }
2362                }
2363
2364                Ok(LogAttribute(value))
2365            }
2366        }
2367
2368        deserializer.deserialize_map(LogAttributeVisitor)
2369    }
2370}
2371
2372/// The type of a [metric](https://develop.sentry.dev/sdk/telemetry/metrics/).
2373#[derive(Serialize, Deserialize, Copy, Clone, Debug, PartialEq)]
2374#[serde(rename_all = "lowercase")]
2375pub enum MetricType {
2376    /// A counter metric that only increments.
2377    Counter,
2378    /// A gauge metric that can go up and down.
2379    Gauge,
2380    /// A distribution metric for statistical spread measurements.
2381    Distribution,
2382}
2383
2384/// A single [metric](https://develop.sentry.dev/sdk/telemetry/metrics/).
2385///
2386/// Construct this type directly with a struct literal.
2387#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
2388pub struct Metric {
2389    /// The metric type.
2390    pub r#type: MetricType,
2391    /// The metric name. Uses dot separators for hierarchy.
2392    pub name: Cow<'static, str>,
2393    /// The numeric value.
2394    pub value: f64,
2395    /// The timestamp when recorded.
2396    #[serde(with = "ts_seconds_float")]
2397    pub timestamp: SystemTime,
2398    /// The trace ID this metric is associated with.
2399    pub trace_id: TraceId,
2400    /// The span ID of the active span, if any.
2401    #[serde(default, skip_serializing_if = "Option::is_none")]
2402    pub span_id: Option<SpanId>,
2403    /// The measurement unit.
2404    #[serde(default, skip_serializing_if = "Option::is_none")]
2405    pub unit: Option<Unit>,
2406    /// Additional key-value attributes.
2407    #[serde(default, skip_serializing_if = "Map::is_empty")]
2408    pub attributes: Map<Cow<'static, str>, LogAttribute>,
2409}
2410
2411/// An ID that identifies an organization in the Sentry backend.
2412#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq)]
2413pub struct OrganizationId(u64);
2414
2415impl From<u64> for OrganizationId {
2416    fn from(value: u64) -> Self {
2417        Self(value)
2418    }
2419}
2420
2421impl std::str::FromStr for OrganizationId {
2422    type Err = std::num::ParseIntError;
2423
2424    fn from_str(s: &str) -> Result<Self, Self::Err> {
2425        s.parse().map(Self)
2426    }
2427}
2428
2429impl std::fmt::Display for OrganizationId {
2430    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2431        write!(f, "{}", self.0)
2432    }
2433}
2434
2435/// A random number generated at the start of a trace by the head of trace SDK.
2436#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq)]
2437pub struct SampleRand(f64);
2438
2439/// An error that indicates failure to construct a SampleRand.
2440#[derive(Debug, Error)]
2441pub enum InvalidSampleRandError {
2442    /// Indicates that the given value cannot be converted to a f64 succesfully.
2443    #[error("failed to parse f64: {0}")]
2444    InvalidFloat(#[from] std::num::ParseFloatError),
2445
2446    /// Indicates that the given float is outside of the valid range for a sample rand, that is the
2447    /// half-open interval [0.0, 1.0).
2448    #[error("sample rand value out of admissible interval [0.0, 1.0)")]
2449    OutOfRange,
2450}
2451
2452impl TryFrom<f64> for SampleRand {
2453    type Error = InvalidSampleRandError;
2454
2455    fn try_from(value: f64) -> Result<Self, Self::Error> {
2456        if !(0.0..1.0).contains(&value) {
2457            return Err(InvalidSampleRandError::OutOfRange);
2458        }
2459        Ok(Self(value))
2460    }
2461}
2462
2463impl std::str::FromStr for SampleRand {
2464    type Err = InvalidSampleRandError;
2465
2466    fn from_str(s: &str) -> Result<Self, Self::Err> {
2467        let x: f64 = s.parse().map_err(InvalidSampleRandError::InvalidFloat)?;
2468        Self::try_from(x)
2469    }
2470}
2471
2472impl std::fmt::Display for SampleRand {
2473    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2474        // Special case: "{:.6}" would round values greater than or equal to 0.9999995 to 1.0,
2475        // as Rust uses [rounding half-to-even](https://doc.rust-lang.org/std/fmt/#precision).
2476        // Round to 0.999999 instead to comply with spec.
2477        if self.0 >= 0.9999995 {
2478            write!(f, "0.999999")
2479        } else {
2480            write!(f, "{:.6}", self.0)
2481        }
2482    }
2483}
2484
2485/// The [Dynamic Sampling
2486/// Context](https://develop.sentry.dev/sdk/telemetry/traces/dynamic-sampling-context/).
2487///
2488/// Sentry supports sampling at the server level through [Dynamic Sampling](https://docs.sentry.io/organization/dynamic-sampling/).
2489/// This feature allows users to specify target sample rates for each project via the frontend instead of requiring an application redeployment.
2490/// The backend needs additional information from the SDK to support these features, contained in
2491/// the Dynamic Sampling Context.
2492#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq)]
2493pub struct DynamicSamplingContext {
2494    // Strictly required fields
2495    // Still typed as optional, as when deserializing an envelope created by an older SDK they might still be missing
2496    #[serde(default, skip_serializing_if = "Option::is_none")]
2497    trace_id: Option<TraceId>,
2498    #[serde(default, skip_serializing_if = "Option::is_none")]
2499    public_key: Option<String>,
2500    #[serde(
2501        default,
2502        skip_serializing_if = "Option::is_none",
2503        with = "display_from_str_opt"
2504    )]
2505    sample_rate: Option<f32>,
2506    // Required fields
2507    #[serde(
2508        default,
2509        skip_serializing_if = "Option::is_none",
2510        with = "display_from_str_opt"
2511    )]
2512    sample_rand: Option<SampleRand>,
2513    #[serde(
2514        default,
2515        skip_serializing_if = "Option::is_none",
2516        with = "display_from_str_opt"
2517    )]
2518    sampled: Option<bool>,
2519    #[serde(default, skip_serializing_if = "Option::is_none")]
2520    release: Option<String>,
2521    #[serde(default, skip_serializing_if = "Option::is_none")]
2522    environment: Option<String>,
2523    #[serde(default, skip_serializing_if = "Option::is_none")]
2524    transaction: Option<String>,
2525    #[serde(
2526        default,
2527        skip_serializing_if = "Option::is_none",
2528        with = "display_from_str_opt"
2529    )]
2530    org_id: Option<OrganizationId>,
2531}
2532
2533impl DynamicSamplingContext {
2534    /// Creates an empty Dynamic Sampling Context.
2535    pub fn new() -> Self {
2536        Default::default()
2537    }
2538
2539    /// Sets the trace ID.
2540    #[must_use]
2541    pub fn with_trace_id(mut self, trace_id: TraceId) -> Self {
2542        self.trace_id = Some(trace_id);
2543        self
2544    }
2545
2546    /// Sets the DSN public key.
2547    #[must_use]
2548    pub fn with_public_key(mut self, public_key: String) -> Self {
2549        self.public_key = Some(public_key);
2550        self
2551    }
2552
2553    /// Sets the sample rate.
2554    #[must_use]
2555    pub fn with_sample_rate(mut self, sample_rate: f32) -> Self {
2556        self.sample_rate = Some(sample_rate);
2557        self
2558    }
2559
2560    /// Sets the sample random value generated by the head of trace SDK.
2561    #[must_use]
2562    pub fn with_sample_rand(mut self, sample_rand: SampleRand) -> Self {
2563        self.sample_rand = Some(sample_rand);
2564        self
2565    }
2566
2567    /// Sets the sampled flag.
2568    #[must_use]
2569    pub fn with_sampled(mut self, sampled: bool) -> Self {
2570        self.sampled = Some(sampled);
2571        self
2572    }
2573
2574    /// Sets the release.
2575    #[must_use]
2576    pub fn with_release(mut self, release: String) -> Self {
2577        self.release = Some(release);
2578        self
2579    }
2580
2581    /// Sets the environment.
2582    #[must_use]
2583    pub fn with_environment(mut self, environment: String) -> Self {
2584        self.environment = Some(environment);
2585        self
2586    }
2587
2588    /// Sets the transaction.
2589    #[must_use]
2590    pub fn with_transaction(mut self, transaction: String) -> Self {
2591        self.transaction = Some(transaction);
2592        self
2593    }
2594
2595    /// Sets the organization ID.
2596    #[must_use]
2597    pub fn with_org_id(mut self, org_id: OrganizationId) -> Self {
2598        self.org_id = Some(org_id);
2599        self
2600    }
2601}