use std::borrow::Cow;
use std::cmp;
use std::convert::TryFrom;
use std::fmt;
use std::iter::FromIterator;
use std::net::{AddrParseError, IpAddr};
use std::ops;
use std::str;
use std::time::SystemTime;
use self::debugid::{CodeId, DebugId};
use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
use thiserror::Error;
use url::Url;
use uuid::Uuid;
use crate::utils::{ts_rfc3339_opt, ts_seconds_float};
pub use super::attachment::*;
pub use super::envelope::*;
pub use super::monitor::*;
pub use super::session::*;
pub mod value {
pub use serde_json::value::{from_value, to_value, Index, Map, Number, Value};
}
pub mod map {
pub use std::collections::btree_map::{BTreeMap as Map, *};
}
pub mod debugid {
pub use debugid::{BreakpadFormat, CodeId, DebugId, ParseDebugIdError};
}
pub use self::value::Value;
pub use self::map::Map;
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
pub struct Values<T> {
pub values: Vec<T>,
}
impl<T> Values<T> {
pub fn new() -> Values<T> {
Values { values: Vec::new() }
}
pub fn is_empty(&self) -> bool {
self.values.is_empty()
}
}
impl<T> Default for Values<T> {
fn default() -> Self {
Values::new()
}
}
impl<T> From<Vec<T>> for Values<T> {
fn from(values: Vec<T>) -> Self {
Values { values }
}
}
impl<T> AsRef<[T]> for Values<T> {
fn as_ref(&self) -> &[T] {
&self.values
}
}
impl<T> AsMut<Vec<T>> for Values<T> {
fn as_mut(&mut self) -> &mut Vec<T> {
&mut self.values
}
}
impl<T> ops::Deref for Values<T> {
type Target = [T];
fn deref(&self) -> &Self::Target {
&self.values
}
}
impl<T> ops::DerefMut for Values<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.values
}
}
impl<T> FromIterator<T> for Values<T> {
fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
Vec::<T>::from_iter(iter).into()
}
}
impl<T> Extend<T> for Values<T> {
fn extend<I>(&mut self, iter: I)
where
I: IntoIterator<Item = T>,
{
self.values.extend(iter)
}
}
impl<'a, T> IntoIterator for &'a mut Values<T> {
type Item = <&'a mut Vec<T> as IntoIterator>::Item;
type IntoIter = <&'a mut Vec<T> as IntoIterator>::IntoIter;
fn into_iter(self) -> Self::IntoIter {
self.values.iter_mut()
}
}
impl<'a, T> IntoIterator for &'a Values<T> {
type Item = <&'a Vec<T> as IntoIterator>::Item;
type IntoIter = <&'a Vec<T> as IntoIterator>::IntoIter;
fn into_iter(self) -> Self::IntoIter {
self.values.iter()
}
}
impl<T> IntoIterator for Values<T> {
type Item = <Vec<T> as IntoIterator>::Item;
type IntoIter = <Vec<T> as IntoIterator>::IntoIter;
fn into_iter(self) -> Self::IntoIter {
self.values.into_iter()
}
}
#[derive(Serialize, Deserialize, Default, Clone, Debug, PartialEq)]
pub struct LogEntry {
pub message: String,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub params: Vec<Value>,
}
#[derive(Serialize, Deserialize, Default, Clone, Debug, PartialEq)]
pub struct Frame {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub function: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub symbol: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub module: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub package: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub filename: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub abs_path: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub lineno: Option<u64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub colno: Option<u64>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub pre_context: Vec<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub context_line: Option<String>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub post_context: Vec<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub in_app: Option<bool>,
#[serde(default, skip_serializing_if = "Map::is_empty")]
pub vars: Map<String, Value>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub image_addr: Option<Addr>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub instruction_addr: Option<Addr>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub symbol_addr: Option<Addr>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub addr_mode: Option<String>,
}
#[derive(Serialize, Deserialize, Default, Clone, Debug, PartialEq)]
pub struct TemplateInfo {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub filename: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub abs_path: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub lineno: Option<u64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub colno: Option<u64>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub pre_context: Vec<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub context_line: Option<String>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub post_context: Vec<String>,
}
#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq)]
pub struct Stacktrace {
#[serde(default)]
pub frames: Vec<Frame>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub frames_omitted: Option<(u64, u64)>,
#[serde(default, skip_serializing_if = "Map::is_empty")]
pub registers: Map<String, RegVal>,
}
impl Stacktrace {
pub fn from_frames_reversed(mut frames: Vec<Frame>) -> Option<Stacktrace> {
if frames.is_empty() {
None
} else {
frames.reverse();
Some(Stacktrace {
frames,
..Default::default()
})
}
}
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Ord, PartialOrd, Hash)]
#[serde(untagged)]
pub enum ThreadId {
Int(u64),
String(String),
}
impl Default for ThreadId {
fn default() -> ThreadId {
ThreadId::Int(0)
}
}
impl<'a> From<&'a str> for ThreadId {
fn from(id: &'a str) -> ThreadId {
ThreadId::String(id.to_string())
}
}
impl From<String> for ThreadId {
fn from(id: String) -> ThreadId {
ThreadId::String(id)
}
}
impl From<i64> for ThreadId {
fn from(id: i64) -> ThreadId {
ThreadId::Int(id as u64)
}
}
impl From<i32> for ThreadId {
fn from(id: i32) -> ThreadId {
ThreadId::Int(id as u64)
}
}
impl From<u32> for ThreadId {
fn from(id: u32) -> ThreadId {
ThreadId::Int(id as u64)
}
}
impl From<u16> for ThreadId {
fn from(id: u16) -> ThreadId {
ThreadId::Int(id as u64)
}
}
impl fmt::Display for ThreadId {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
ThreadId::Int(i) => write!(f, "{i}"),
ThreadId::String(ref s) => write!(f, "{s}"),
}
}
}
#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, Ord, PartialOrd, Hash)]
pub struct Addr(pub u64);
impl Addr {
pub fn is_null(&self) -> bool {
self.0 == 0
}
}
impl_hex_serde!(Addr, u64);
impl From<u64> for Addr {
fn from(addr: u64) -> Addr {
Addr(addr)
}
}
impl From<i32> for Addr {
fn from(addr: i32) -> Addr {
Addr(addr as u64)
}
}
impl From<u32> for Addr {
fn from(addr: u32) -> Addr {
Addr(addr as u64)
}
}
impl From<usize> for Addr {
fn from(addr: usize) -> Addr {
Addr(addr as u64)
}
}
impl<T> From<*const T> for Addr {
fn from(addr: *const T) -> Addr {
Addr(addr as u64)
}
}
impl<T> From<*mut T> for Addr {
fn from(addr: *mut T) -> Addr {
Addr(addr as u64)
}
}
impl From<Addr> for u64 {
fn from(addr: Addr) -> Self {
addr.0
}
}
fn is_false(value: &bool) -> bool {
!*value
}
#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, Ord, PartialOrd, Hash)]
pub struct RegVal(pub u64);
impl_hex_serde!(RegVal, u64);
impl From<u64> for RegVal {
fn from(addr: u64) -> RegVal {
RegVal(addr)
}
}
impl From<i32> for RegVal {
fn from(addr: i32) -> RegVal {
RegVal(addr as u64)
}
}
impl From<u32> for RegVal {
fn from(addr: u32) -> RegVal {
RegVal(addr as u64)
}
}
impl From<usize> for RegVal {
fn from(addr: usize) -> RegVal {
RegVal(addr as u64)
}
}
impl<T> From<*const T> for RegVal {
fn from(addr: *const T) -> RegVal {
RegVal(addr as u64)
}
}
impl<T> From<*mut T> for RegVal {
fn from(addr: *mut T) -> RegVal {
RegVal(addr as u64)
}
}
impl From<RegVal> for u64 {
fn from(reg: RegVal) -> Self {
reg.0
}
}
#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq)]
pub struct Thread {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub id: Option<ThreadId>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub name: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub stacktrace: Option<Stacktrace>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub raw_stacktrace: Option<Stacktrace>,
#[serde(default, skip_serializing_if = "is_false")]
pub crashed: bool,
#[serde(default, skip_serializing_if = "is_false")]
pub current: bool,
}
#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq, Eq)]
pub struct CError {
pub number: i32,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub name: Option<String>,
}
impl From<i32> for CError {
fn from(number: i32) -> CError {
CError { number, name: None }
}
}
impl From<CError> for i32 {
fn from(err: CError) -> Self {
err.number
}
}
#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq, Eq)]
pub struct MachException {
pub exception: i32,
pub code: u64,
pub subcode: u64,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub name: Option<String>,
}
#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq, Eq)]
pub struct PosixSignal {
pub number: i32,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub code: Option<i32>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub name: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub code_name: Option<String>,
}
impl From<i32> for PosixSignal {
fn from(number: i32) -> PosixSignal {
PosixSignal {
number,
code: None,
name: None,
code_name: None,
}
}
}
impl From<(i32, i32)> for PosixSignal {
fn from(tuple: (i32, i32)) -> PosixSignal {
let (number, code) = tuple;
PosixSignal {
number,
code: Some(code),
name: None,
code_name: None,
}
}
}
impl From<PosixSignal> for i32 {
fn from(sig: PosixSignal) -> Self {
sig.number
}
}
#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq)]
pub struct MechanismMeta {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub errno: Option<CError>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub signal: Option<PosixSignal>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub mach_exception: Option<MachException>,
}
impl MechanismMeta {
fn is_empty(&self) -> bool {
self.errno.is_none() && self.signal.is_none() && self.mach_exception.is_none()
}
}
#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq)]
pub struct Mechanism {
#[serde(rename = "type")]
pub ty: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub help_link: Option<Url>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub handled: Option<bool>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub synthetic: Option<bool>,
#[serde(default, skip_serializing_if = "Map::is_empty")]
pub data: Map<String, Value>,
#[serde(default, skip_serializing_if = "MechanismMeta::is_empty")]
pub meta: MechanismMeta,
}
#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq)]
pub struct Exception {
#[serde(rename = "type")]
pub ty: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub value: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub module: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub stacktrace: Option<Stacktrace>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub raw_stacktrace: Option<Stacktrace>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub thread_id: Option<ThreadId>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub mechanism: Option<Mechanism>,
}
#[derive(Debug, Error)]
#[error("invalid level")]
pub struct ParseLevelError;
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
pub enum Level {
Debug,
#[default]
Info,
Warning,
Error,
Fatal,
}
impl str::FromStr for Level {
type Err = ParseLevelError;
fn from_str(string: &str) -> Result<Level, Self::Err> {
Ok(match string {
"debug" => Level::Debug,
"info" | "log" => Level::Info,
"warning" => Level::Warning,
"error" => Level::Error,
"fatal" => Level::Fatal,
_ => return Err(ParseLevelError),
})
}
}
impl fmt::Display for Level {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
Level::Debug => write!(f, "debug"),
Level::Info => write!(f, "info"),
Level::Warning => write!(f, "warning"),
Level::Error => write!(f, "error"),
Level::Fatal => write!(f, "fatal"),
}
}
}
impl Level {
pub fn is_debug(&self) -> bool {
*self == Level::Debug
}
pub fn is_info(&self) -> bool {
*self == Level::Info
}
pub fn is_warning(&self) -> bool {
*self == Level::Warning
}
pub fn is_error(&self) -> bool {
*self == Level::Error
}
pub fn is_fatal(&self) -> bool {
*self == Level::Fatal
}
}
impl_str_serde!(Level);
mod breadcrumb {
use super::*;
pub fn default_type() -> String {
"default".to_string()
}
pub fn is_default_type(ty: &str) -> bool {
ty == "default"
}
pub fn default_level() -> Level {
Level::Info
}
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
pub struct Breadcrumb {
#[serde(default = "SystemTime::now", with = "ts_seconds_float")]
pub timestamp: SystemTime,
#[serde(
rename = "type",
default = "breadcrumb::default_type",
skip_serializing_if = "breadcrumb::is_default_type"
)]
pub ty: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub category: Option<String>,
#[serde(
default = "breadcrumb::default_level",
skip_serializing_if = "Level::is_info"
)]
pub level: Level,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub message: Option<String>,
#[serde(default, skip_serializing_if = "Map::is_empty")]
pub data: Map<String, Value>,
}
impl Default for Breadcrumb {
fn default() -> Breadcrumb {
Breadcrumb {
timestamp: SystemTime::now(),
ty: breadcrumb::default_type(),
category: Default::default(),
level: breadcrumb::default_level(),
message: Default::default(),
data: Default::default(),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Ord, PartialOrd, Hash, Default)]
pub enum IpAddress {
#[default]
Auto,
Exact(IpAddr),
}
impl PartialEq<IpAddr> for IpAddress {
fn eq(&self, other: &IpAddr) -> bool {
match *self {
IpAddress::Auto => false,
IpAddress::Exact(ref addr) => addr == other,
}
}
}
impl cmp::PartialOrd<IpAddr> for IpAddress {
fn partial_cmp(&self, other: &IpAddr) -> Option<cmp::Ordering> {
match *self {
IpAddress::Auto => None,
IpAddress::Exact(ref addr) => addr.partial_cmp(other),
}
}
}
impl fmt::Display for IpAddress {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
IpAddress::Auto => write!(f, "{{{{auto}}}}"),
IpAddress::Exact(ref addr) => write!(f, "{addr}"),
}
}
}
impl From<IpAddr> for IpAddress {
fn from(addr: IpAddr) -> IpAddress {
IpAddress::Exact(addr)
}
}
impl str::FromStr for IpAddress {
type Err = AddrParseError;
fn from_str(string: &str) -> Result<IpAddress, AddrParseError> {
match string {
"{{auto}}" => Ok(IpAddress::Auto),
other => other.parse().map(IpAddress::Exact),
}
}
}
impl_str_serde!(IpAddress);
#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq)]
pub struct User {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub id: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub email: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub ip_address: Option<IpAddress>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub username: Option<String>,
#[serde(flatten)]
pub other: Map<String, Value>,
}
#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq)]
pub struct Request {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub url: Option<Url>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub method: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub data: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub query_string: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub cookies: Option<String>,
#[serde(default, skip_serializing_if = "Map::is_empty")]
pub headers: Map<String, String>,
#[serde(default, skip_serializing_if = "Map::is_empty")]
pub env: Map<String, String>,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
pub struct SystemSdkInfo {
pub sdk_name: String,
pub version_major: u32,
pub version_minor: u32,
pub version_patchlevel: u32,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
#[serde(rename_all = "snake_case", tag = "type")]
pub enum DebugImage {
Apple(AppleDebugImage),
Symbolic(SymbolicDebugImage),
Proguard(ProguardDebugImage),
Wasm(WasmDebugImage),
}
impl DebugImage {
pub fn type_name(&self) -> &str {
match *self {
DebugImage::Apple(..) => "apple",
DebugImage::Symbolic(..) => "symbolic",
DebugImage::Proguard(..) => "proguard",
DebugImage::Wasm(..) => "wasm",
}
}
}
macro_rules! into_debug_image {
($kind:ident, $ty:ty) => {
impl From<$ty> for DebugImage {
fn from(data: $ty) -> DebugImage {
DebugImage::$kind(data)
}
}
};
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
pub struct AppleDebugImage {
pub name: String,
pub arch: Option<String>,
pub cpu_type: Option<u32>,
pub cpu_subtype: Option<u32>,
pub image_addr: Addr,
pub image_size: u64,
#[serde(default, skip_serializing_if = "Addr::is_null")]
pub image_vmaddr: Addr,
pub uuid: Uuid,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
pub struct SymbolicDebugImage {
pub name: String,
pub arch: Option<String>,
pub image_addr: Addr,
pub image_size: u64,
#[serde(default, skip_serializing_if = "Addr::is_null")]
pub image_vmaddr: Addr,
pub id: DebugId,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub code_id: Option<CodeId>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub debug_file: Option<String>,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
pub struct ProguardDebugImage {
pub uuid: Uuid,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
pub struct WasmDebugImage {
pub name: String,
pub debug_id: Uuid,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub debug_file: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub code_id: Option<String>,
pub code_file: String,
}
into_debug_image!(Apple, AppleDebugImage);
into_debug_image!(Symbolic, SymbolicDebugImage);
into_debug_image!(Proguard, ProguardDebugImage);
into_debug_image!(Wasm, WasmDebugImage);
#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq)]
pub struct DebugMeta {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub sdk_info: Option<SystemSdkInfo>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub images: Vec<DebugImage>,
}
impl DebugMeta {
pub fn is_empty(&self) -> bool {
self.sdk_info.is_none() && self.images.is_empty()
}
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
pub struct ClientSdkInfo {
pub name: String,
pub version: String,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub integrations: Vec<String>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub packages: Vec<ClientSdkPackage>,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
pub struct ClientSdkPackage {
pub name: String,
pub version: String,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
#[serde(rename_all = "snake_case", tag = "type")]
#[non_exhaustive]
pub enum Context {
Device(Box<DeviceContext>),
Os(Box<OsContext>),
Runtime(Box<RuntimeContext>),
App(Box<AppContext>),
Browser(Box<BrowserContext>),
Trace(Box<TraceContext>),
Gpu(Box<GpuContext>),
#[serde(rename = "unknown")]
Other(Map<String, Value>),
}
impl Context {
pub fn type_name(&self) -> &str {
match *self {
Context::Device(..) => "device",
Context::Os(..) => "os",
Context::Runtime(..) => "runtime",
Context::App(..) => "app",
Context::Browser(..) => "browser",
Context::Trace(..) => "trace",
Context::Gpu(..) => "gpu",
Context::Other(..) => "unknown",
}
}
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Hash)]
#[serde(rename_all = "lowercase")]
pub enum Orientation {
Portrait,
Landscape,
}
#[derive(Serialize, Deserialize, Debug, Clone, Default, PartialEq)]
pub struct DeviceContext {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub name: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub family: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub model: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub model_id: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub arch: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub battery_level: Option<f32>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub orientation: Option<Orientation>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub simulator: Option<bool>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub memory_size: Option<u64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub free_memory: Option<u64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub usable_memory: Option<u64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub storage_size: Option<u64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub free_storage: Option<u64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub external_storage_size: Option<u64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub external_free_storage: Option<u64>,
#[serde(
default,
skip_serializing_if = "Option::is_none",
with = "ts_rfc3339_opt"
)]
pub boot_time: Option<SystemTime>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub timezone: Option<String>,
#[serde(flatten)]
pub other: Map<String, Value>,
}
#[derive(Serialize, Deserialize, Debug, Clone, Default, PartialEq)]
pub struct OsContext {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub name: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub version: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub build: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub kernel_version: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub rooted: Option<bool>,
#[serde(flatten)]
pub other: Map<String, Value>,
}
#[derive(Serialize, Deserialize, Debug, Clone, Default, PartialEq)]
pub struct RuntimeContext {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub name: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub version: Option<String>,
#[serde(flatten)]
pub other: Map<String, Value>,
}
#[derive(Serialize, Deserialize, Debug, Clone, Default, PartialEq)]
pub struct AppContext {
#[serde(
default,
skip_serializing_if = "Option::is_none",
with = "ts_rfc3339_opt"
)]
pub app_start_time: Option<SystemTime>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub device_app_hash: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub build_type: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub app_identifier: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub app_name: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub app_version: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub app_build: Option<String>,
#[serde(flatten)]
pub other: Map<String, Value>,
}
#[derive(Serialize, Deserialize, Debug, Clone, Default, PartialEq)]
pub struct BrowserContext {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub name: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub version: Option<String>,
#[serde(flatten)]
pub other: Map<String, Value>,
}
#[derive(Serialize, Deserialize, Debug, Clone, Default, PartialEq)]
pub struct GpuContext {
pub name: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub version: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub driver_version: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub id: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub vendor_id: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub vendor_name: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub memory_size: Option<u32>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub api_type: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub multi_threaded_rendering: Option<bool>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub npot_support: Option<bool>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub max_texture_size: Option<u32>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub graphics_shader_level: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub supports_draw_call_instancing: Option<bool>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub supports_ray_tracing: Option<bool>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub supports_compute_shaders: Option<bool>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub supports_geometry_shaders: Option<bool>,
#[serde(flatten)]
pub other: Map<String, Value>,
}
#[derive(Serialize, Deserialize, Debug, Copy, Clone, Eq, PartialEq, Hash)]
#[serde(try_from = "String", into = "String")]
pub struct SpanId([u8; 8]);
impl Default for SpanId {
fn default() -> Self {
Self(rand::random())
}
}
impl fmt::Display for SpanId {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
write!(fmt, "{}", hex::encode(self.0))
}
}
impl From<SpanId> for String {
fn from(span_id: SpanId) -> Self {
span_id.to_string()
}
}
impl str::FromStr for SpanId {
type Err = hex::FromHexError;
fn from_str(input: &str) -> Result<Self, Self::Err> {
let mut buf = [0; 8];
hex::decode_to_slice(input, &mut buf)?;
Ok(Self(buf))
}
}
impl TryFrom<String> for SpanId {
type Error = hex::FromHexError;
fn try_from(value: String) -> Result<Self, Self::Error> {
value.parse()
}
}
#[derive(Serialize, Deserialize, Debug, Copy, Clone, Eq, PartialEq, Hash)]
#[serde(try_from = "String", into = "String")]
pub struct TraceId([u8; 16]);
impl Default for TraceId {
fn default() -> Self {
Self(rand::random())
}
}
impl fmt::Display for TraceId {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
write!(fmt, "{}", hex::encode(self.0))
}
}
impl From<TraceId> for String {
fn from(trace_id: TraceId) -> Self {
trace_id.to_string()
}
}
impl str::FromStr for TraceId {
type Err = hex::FromHexError;
fn from_str(input: &str) -> Result<Self, Self::Err> {
let mut buf = [0; 16];
hex::decode_to_slice(input, &mut buf)?;
Ok(Self(buf))
}
}
impl TryFrom<String> for TraceId {
type Error = hex::FromHexError;
fn try_from(value: String) -> Result<Self, Self::Error> {
value.parse()
}
}
#[derive(Serialize, Deserialize, Debug, Clone, Default, PartialEq)]
pub struct TraceContext {
#[serde(default)]
pub span_id: SpanId,
#[serde(default)]
pub trace_id: TraceId,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub parent_span_id: Option<SpanId>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub op: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub status: Option<SpanStatus>,
}
macro_rules! into_context {
($kind:ident, $ty:ty) => {
impl From<$ty> for Context {
fn from(data: $ty) -> Self {
Context::$kind(Box::new(data))
}
}
};
}
into_context!(App, AppContext);
into_context!(Device, DeviceContext);
into_context!(Os, OsContext);
into_context!(Runtime, RuntimeContext);
into_context!(Browser, BrowserContext);
into_context!(Trace, TraceContext);
into_context!(Gpu, GpuContext);
const INFERABLE_CONTEXTS: &[&str] = &["device", "os", "runtime", "app", "browser", "trace", "gpu"];
struct ContextsVisitor;
impl<'de> de::Visitor<'de> for ContextsVisitor {
type Value = Map<String, Context>;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("contexts object")
}
fn visit_map<A>(self, mut access: A) -> Result<Self::Value, A::Error>
where
A: de::MapAccess<'de>,
{
let mut map: Map<String, Context> = Map::new();
while let Some((key, mut value)) = access.next_entry::<String, Value>()? {
let typed_value = value
.as_object_mut()
.map(|ctx| {
if !ctx.contains_key("type") {
let type_key = if INFERABLE_CONTEXTS.contains(&key.as_str()) {
key.clone().into()
} else {
Value::String("unknown".into())
};
ctx.insert(String::from("type"), type_key);
}
ctx.to_owned()
})
.ok_or_else(|| de::Error::custom("expected valid `context` object"))?;
match serde_json::from_value(serde_json::to_value(typed_value).unwrap()) {
Ok(context) => {
map.insert(key, context);
}
Err(e) => return Err(de::Error::custom(e.to_string())),
}
}
Ok(map)
}
}
fn deserialize_contexts<'de, D>(deserializer: D) -> Result<Map<String, Context>, D::Error>
where
D: Deserializer<'de>,
{
deserializer.deserialize_map(ContextsVisitor {})
}
mod event {
use super::*;
pub fn default_id() -> Uuid {
crate::random_uuid()
}
pub fn serialize_id<S: Serializer>(uuid: &Uuid, serializer: S) -> Result<S::Ok, S::Error> {
serializer.serialize_some(&uuid.as_simple().to_string())
}
pub fn default_level() -> Level {
Level::Error
}
pub fn default_platform() -> Cow<'static, str> {
Cow::Borrowed("other")
}
pub fn is_default_platform(value: &str) -> bool {
value == "other"
}
static DEFAULT_FINGERPRINT: &[Cow<'static, str>] = &[Cow::Borrowed("{{ default }}")];
pub fn default_fingerprint<'a>() -> Cow<'a, [Cow<'a, str>]> {
Cow::Borrowed(DEFAULT_FINGERPRINT)
}
pub fn is_default_fingerprint(fp: &[Cow<'_, str>]) -> bool {
fp.len() == 1 && ((fp)[0] == "{{ default }}" || (fp)[0] == "{{default}}")
}
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
pub struct Event<'a> {
#[serde(default = "event::default_id", serialize_with = "event::serialize_id")]
pub event_id: Uuid,
#[serde(
default = "event::default_level",
skip_serializing_if = "Level::is_error"
)]
pub level: Level,
#[serde(
default = "event::default_fingerprint",
skip_serializing_if = "event::is_default_fingerprint"
)]
pub fingerprint: Cow<'a, [Cow<'a, str>]>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub culprit: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub transaction: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub message: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub logentry: Option<LogEntry>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub logger: Option<String>,
#[serde(default, skip_serializing_if = "Map::is_empty")]
pub modules: Map<String, String>,
#[serde(
default = "event::default_platform",
skip_serializing_if = "event::is_default_platform"
)]
pub platform: Cow<'a, str>,
#[serde(default = "SystemTime::now", with = "ts_seconds_float")]
pub timestamp: SystemTime,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub server_name: Option<Cow<'a, str>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub release: Option<Cow<'a, str>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub dist: Option<Cow<'a, str>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub environment: Option<Cow<'a, str>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub user: Option<User>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub request: Option<Request>,
#[serde(
default,
skip_serializing_if = "Map::is_empty",
deserialize_with = "deserialize_contexts"
)]
pub contexts: Map<String, Context>,
#[serde(default, skip_serializing_if = "Values::is_empty")]
pub breadcrumbs: Values<Breadcrumb>,
#[serde(default, skip_serializing_if = "Values::is_empty")]
pub exception: Values<Exception>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub stacktrace: Option<Stacktrace>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub template: Option<TemplateInfo>,
#[serde(default, skip_serializing_if = "Values::is_empty")]
pub threads: Values<Thread>,
#[serde(default, skip_serializing_if = "Map::is_empty")]
pub tags: Map<String, String>,
#[serde(default, skip_serializing_if = "Map::is_empty")]
pub extra: Map<String, Value>,
#[serde(default, skip_serializing_if = "DebugMeta::is_empty")]
pub debug_meta: Cow<'a, DebugMeta>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub sdk: Option<Cow<'a, ClientSdkInfo>>,
}
impl<'a> Default for Event<'a> {
fn default() -> Self {
Event {
event_id: event::default_id(),
level: event::default_level(),
fingerprint: event::default_fingerprint(),
culprit: Default::default(),
transaction: Default::default(),
message: Default::default(),
logentry: Default::default(),
logger: Default::default(),
modules: Default::default(),
platform: event::default_platform(),
timestamp: SystemTime::now(),
server_name: Default::default(),
release: Default::default(),
dist: Default::default(),
environment: Default::default(),
user: Default::default(),
request: Default::default(),
contexts: Default::default(),
breadcrumbs: Default::default(),
exception: Default::default(),
stacktrace: Default::default(),
template: Default::default(),
threads: Default::default(),
tags: Default::default(),
extra: Default::default(),
debug_meta: Default::default(),
sdk: Default::default(),
}
}
}
impl<'a> Event<'a> {
pub fn new() -> Event<'a> {
Default::default()
}
pub fn into_owned(self) -> Event<'static> {
Event {
event_id: self.event_id,
level: self.level,
fingerprint: Cow::Owned(
self.fingerprint
.iter()
.map(|x| Cow::Owned(x.to_string()))
.collect(),
),
culprit: self.culprit,
transaction: self.transaction,
message: self.message,
logentry: self.logentry,
logger: self.logger,
modules: self.modules,
platform: Cow::Owned(self.platform.into_owned()),
timestamp: self.timestamp,
server_name: self.server_name.map(|x| Cow::Owned(x.into_owned())),
release: self.release.map(|x| Cow::Owned(x.into_owned())),
dist: self.dist.map(|x| Cow::Owned(x.into_owned())),
environment: self.environment.map(|x| Cow::Owned(x.into_owned())),
user: self.user,
request: self.request,
contexts: self.contexts,
breadcrumbs: self.breadcrumbs,
exception: self.exception,
stacktrace: self.stacktrace,
template: self.template,
threads: self.threads,
tags: self.tags,
extra: self.extra,
debug_meta: Cow::Owned(self.debug_meta.into_owned()),
sdk: self.sdk.map(|x| Cow::Owned(x.into_owned())),
}
}
}
impl<'a> fmt::Display for Event<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"Event(id: {}, ts: {})",
self.event_id,
crate::utils::to_rfc3339(&self.timestamp)
)
}
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
pub struct Span {
#[serde(default)]
pub span_id: SpanId,
#[serde(default)]
pub trace_id: TraceId,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub parent_span_id: Option<SpanId>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub same_process_as_parent: Option<bool>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub op: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
#[serde(
default,
skip_serializing_if = "Option::is_none",
with = "ts_rfc3339_opt"
)]
pub timestamp: Option<SystemTime>,
#[serde(default = "SystemTime::now", with = "ts_seconds_float")]
pub start_timestamp: SystemTime,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub status: Option<SpanStatus>,
#[serde(default, skip_serializing_if = "Map::is_empty")]
pub tags: Map<String, String>,
#[serde(default, skip_serializing_if = "Map::is_empty")]
pub data: Map<String, Value>,
}
impl Default for Span {
fn default() -> Self {
Span {
span_id: Default::default(),
trace_id: Default::default(),
timestamp: Default::default(),
tags: Default::default(),
start_timestamp: SystemTime::now(),
description: Default::default(),
status: Default::default(),
parent_span_id: Default::default(),
same_process_as_parent: Default::default(),
op: Default::default(),
data: Default::default(),
}
}
}
impl Span {
pub fn new() -> Span {
Default::default()
}
pub fn finish(&mut self) {
self.timestamp = Some(SystemTime::now());
}
}
impl fmt::Display for Span {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"Span(id: {}, ts: {})",
self.span_id,
crate::utils::to_rfc3339(&self.start_timestamp)
)
}
}
#[derive(Debug, Error)]
#[error("invalid status")]
pub struct ParseStatusError;
#[derive(Serialize, Deserialize, Debug, Copy, Clone, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum SpanStatus {
#[serde(rename = "ok")]
Ok,
#[serde(rename = "deadline_exceeded")]
DeadlineExceeded,
#[serde(rename = "unauthenticated")]
Unauthenticated,
#[serde(rename = "permission_denied")]
PermissionDenied,
#[serde(rename = "not_found")]
NotFound,
#[serde(rename = "resource_exhausted")]
ResourceExhausted,
#[serde(rename = "invalid_argument")]
InvalidArgument,
#[serde(rename = "unimplemented")]
Unimplemented,
#[serde(rename = "unavailable")]
Unavailable,
#[serde(rename = "internal_error")]
InternalError,
#[serde(rename = "unknown_error")]
UnknownError,
#[serde(rename = "cancelled")]
Cancelled,
#[serde(rename = "already_exists")]
AlreadyExists,
#[serde(rename = "failed_precondition")]
FailedPrecondition,
#[serde(rename = "aborted")]
Aborted,
#[serde(rename = "out_of_range")]
OutOfRange,
#[serde(rename = "data_loss")]
DataLoss,
}
impl str::FromStr for SpanStatus {
type Err = ParseStatusError;
fn from_str(s: &str) -> Result<SpanStatus, Self::Err> {
Ok(match s {
"ok" => SpanStatus::Ok,
"deadline_exceeded" => SpanStatus::DeadlineExceeded,
"unauthenticated" => SpanStatus::Unauthenticated,
"permission_denied" => SpanStatus::PermissionDenied,
"not_found" => SpanStatus::NotFound,
"resource_exhausted" => SpanStatus::ResourceExhausted,
"invalid_argument" => SpanStatus::InvalidArgument,
"unimplemented" => SpanStatus::Unimplemented,
"unavailable" => SpanStatus::Unavailable,
"internal_error" => SpanStatus::InternalError,
"unknown_error" => SpanStatus::UnknownError,
"cancelled" => SpanStatus::Cancelled,
"already_exists" => SpanStatus::AlreadyExists,
"failed_precondition" => SpanStatus::FailedPrecondition,
"aborted" => SpanStatus::Aborted,
"out_of_range" => SpanStatus::OutOfRange,
"data_loss" => SpanStatus::DataLoss,
_ => return Err(ParseStatusError),
})
}
}
impl fmt::Display for SpanStatus {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
SpanStatus::Ok => write!(f, "ok"),
SpanStatus::DeadlineExceeded => write!(f, "deadline_exceeded"),
SpanStatus::Unauthenticated => write!(f, "unauthenticated"),
SpanStatus::PermissionDenied => write!(f, "permission_denied"),
SpanStatus::NotFound => write!(f, "not_found"),
SpanStatus::ResourceExhausted => write!(f, "resource_exhausted"),
SpanStatus::InvalidArgument => write!(f, "invalid_argument"),
SpanStatus::Unimplemented => write!(f, "unimplemented"),
SpanStatus::Unavailable => write!(f, "unavailable"),
SpanStatus::InternalError => write!(f, "internal_error"),
SpanStatus::UnknownError => write!(f, "unknown_error"),
SpanStatus::Cancelled => write!(f, "cancelled"),
SpanStatus::AlreadyExists => write!(f, "already_exists"),
SpanStatus::FailedPrecondition => write!(f, "failed_precondition"),
SpanStatus::Aborted => write!(f, "aborted"),
SpanStatus::OutOfRange => write!(f, "out_of_range"),
SpanStatus::DataLoss => write!(f, "data_loss"),
}
}
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
pub struct Transaction<'a> {
#[serde(default = "event::default_id", serialize_with = "event::serialize_id")]
pub event_id: Uuid,
#[serde(
rename = "transaction",
default,
skip_serializing_if = "Option::is_none"
)]
pub name: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub release: Option<Cow<'a, str>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub environment: Option<Cow<'a, str>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub user: Option<User>,
#[serde(default, skip_serializing_if = "Map::is_empty")]
pub tags: Map<String, String>,
#[serde(default, skip_serializing_if = "Map::is_empty")]
pub extra: Map<String, Value>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub sdk: Option<Cow<'a, ClientSdkInfo>>,
#[serde(
default = "event::default_platform",
skip_serializing_if = "event::is_default_platform"
)]
pub platform: Cow<'a, str>,
#[serde(
default,
skip_serializing_if = "Option::is_none",
with = "ts_rfc3339_opt"
)]
pub timestamp: Option<SystemTime>,
#[serde(default = "SystemTime::now", with = "ts_seconds_float")]
pub start_timestamp: SystemTime,
pub spans: Vec<Span>,
#[serde(
default,
skip_serializing_if = "Map::is_empty",
deserialize_with = "deserialize_contexts"
)]
pub contexts: Map<String, Context>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub request: Option<Request>,
}
impl<'a> Default for Transaction<'a> {
fn default() -> Self {
Transaction {
event_id: event::default_id(),
name: Default::default(),
user: Default::default(),
tags: Default::default(),
extra: Default::default(),
release: Default::default(),
environment: Default::default(),
sdk: Default::default(),
platform: event::default_platform(),
timestamp: Default::default(),
start_timestamp: SystemTime::now(),
spans: Default::default(),
contexts: Default::default(),
request: Default::default(),
}
}
}
impl<'a> Transaction<'a> {
pub fn new() -> Transaction<'a> {
Default::default()
}
pub fn into_owned(self) -> Transaction<'static> {
Transaction {
event_id: self.event_id,
name: self.name,
user: self.user,
tags: self.tags,
extra: self.extra,
release: self.release.map(|x| Cow::Owned(x.into_owned())),
environment: self.environment.map(|x| Cow::Owned(x.into_owned())),
sdk: self.sdk.map(|x| Cow::Owned(x.into_owned())),
platform: Cow::Owned(self.platform.into_owned()),
timestamp: self.timestamp,
start_timestamp: self.start_timestamp,
spans: self.spans,
contexts: self.contexts,
request: self.request,
}
}
pub fn finish(&mut self) {
self.timestamp = Some(SystemTime::now());
}
}
impl<'a> fmt::Display for Transaction<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"Transaction(id: {}, ts: {})",
self.event_id,
crate::utils::to_rfc3339(&self.start_timestamp)
)
}
}