ts_opentelemetry_stdout 0.1.0-beta.1

A fork of an OpenTelemetry exporter for stdout
Documentation
use std::{
    borrow::Cow,
    collections::BTreeMap,
    hash::{Hash, Hasher},
    time::{SystemTime, UNIX_EPOCH},
};

use ordered_float::OrderedFloat;
use serde::{Serialize, Serializer};

#[derive(Debug, Serialize, Clone, Hash, Eq, PartialEq)]
pub(crate) struct AttributeSet(pub BTreeMap<Key, Value>);

impl From<&ts_opentelemetry_sdk::AttributeSet> for AttributeSet {
    fn from(value: &ts_opentelemetry_sdk::AttributeSet) -> Self {
        AttributeSet(
            value
                .iter()
                .map(|(key, value)| (Key::from(key.clone()), Value::from(value.clone())))
                .collect(),
        )
    }
}

impl From<&ts_opentelemetry_sdk::Resource> for AttributeSet {
    fn from(value: &ts_opentelemetry_sdk::Resource) -> Self {
        AttributeSet(
            value
                .iter()
                .map(|(key, value)| (Key::from(key.clone()), Value::from(value.clone())))
                .collect(),
        )
    }
}

#[derive(Debug, Clone, Serialize)]
#[serde(rename_all = "camelCase")]
pub(crate) struct Resource {
    attributes: Vec<KeyValue>,
    #[serde(skip_serializing_if = "is_zero")]
    dropped_attributes_count: u64,
}

fn is_zero(v: &u64) -> bool {
    *v == 0
}

impl From<&ts_opentelemetry_sdk::Resource> for Resource {
    fn from(value: &ts_opentelemetry_sdk::Resource) -> Self {
        Resource {
            attributes: value
                .iter()
                .map(|(key, value)| KeyValue {
                    key: key.clone().into(),
                    value: value.clone().into(),
                })
                .collect(),
            dropped_attributes_count: 0,
        }
    }
}

#[derive(Debug, Clone, Serialize, Hash, PartialEq, Eq, Ord, PartialOrd)]
pub(crate) struct Key(Cow<'static, str>);

impl From<Cow<'static, str>> for Key {
    fn from(value: Cow<'static, str>) -> Self {
        Key(value)
    }
}

impl From<ts_opentelemetry_api::Key> for Key {
    fn from(value: ts_opentelemetry_api::Key) -> Self {
        Key(value.as_str().to_string().into())
    }
}

#[derive(Debug, Serialize, Clone)]
#[allow(dead_code)]
pub(crate) enum Value {
    #[serde(rename = "boolValue")]
    Bool(bool),
    #[serde(rename = "intValue")]
    Int(i64),
    #[serde(rename = "doubleValue")]
    Double(f64),
    #[serde(rename = "stringValue")]
    String(String),
    #[serde(rename = "arrayValue")]
    Array(Vec<Value>),
    #[serde(rename = "kvListValue")]
    KeyValues(Vec<KeyValue>),
    #[serde(rename = "bytesValue")]
    BytesValue(Vec<u8>),
}

impl PartialEq for Value {
    fn eq(&self, other: &Self) -> bool {
        match (&self, &other) {
            (Value::Double(f), Value::Double(of)) => OrderedFloat(*f).eq(&OrderedFloat(*of)),
            (non_double, other_non_double) => non_double.eq(other_non_double),
        }
    }
}

impl Eq for Value {}

impl Hash for Value {
    fn hash<H: Hasher>(&self, state: &mut H) {
        match &self {
            Value::Bool(b) => b.hash(state),
            Value::Int(i) => i.hash(state),
            Value::Double(f) => OrderedFloat(*f).hash(state),
            Value::String(s) => s.hash(state),
            Value::Array(a) => a.iter().for_each(|v| v.hash(state)),
            Value::KeyValues(kv) => kv.iter().for_each(|kv| {
                kv.key.hash(state);
                kv.value.hash(state);
            }),
            Value::BytesValue(b) => b.hash(state),
        }
    }
}

impl From<ts_opentelemetry_api::Value> for Value {
    fn from(value: ts_opentelemetry_api::Value) -> Self {
        match value {
            ts_opentelemetry_api::Value::Bool(b) => Value::Bool(b),
            ts_opentelemetry_api::Value::I64(i) => Value::Int(i),
            ts_opentelemetry_api::Value::F64(f) => Value::Double(f),
            ts_opentelemetry_api::Value::String(s) => Value::String(s.into()),
            ts_opentelemetry_api::Value::Array(a) => match a {
                ts_opentelemetry_api::Array::Bool(b) => {
                    Value::Array(b.into_iter().map(Value::Bool).collect())
                }
                ts_opentelemetry_api::Array::I64(i) => {
                    Value::Array(i.into_iter().map(Value::Int).collect())
                }
                ts_opentelemetry_api::Array::F64(f) => {
                    Value::Array(f.into_iter().map(Value::Double).collect())
                }
                ts_opentelemetry_api::Array::String(s) => {
                    Value::Array(s.into_iter().map(|s| Value::String(s.into())).collect())
                }
            },
        }
    }
}

#[cfg(feature = "logs")]
impl From<ts_opentelemetry_api::logs::AnyValue> for Value {
    fn from(value: ts_opentelemetry_api::logs::AnyValue) -> Self {
        match value {
            ts_opentelemetry_api::logs::AnyValue::Boolean(b) => Value::Bool(b),
            ts_opentelemetry_api::logs::AnyValue::Int(i) => Value::Int(i),
            ts_opentelemetry_api::logs::AnyValue::Double(d) => Value::Double(d),
            ts_opentelemetry_api::logs::AnyValue::String(s) => Value::String(s.into()),
            ts_opentelemetry_api::logs::AnyValue::ListAny(a) => {
                Value::Array(a.into_iter().map(Into::into).collect())
            }
            ts_opentelemetry_api::logs::AnyValue::Map(m) => Value::KeyValues(
                m.into_iter()
                    .map(|(key, value)| KeyValue {
                        key: key.into(),
                        value: value.into(),
                    })
                    .collect(),
            ),
            ts_opentelemetry_api::logs::AnyValue::Bytes(b) => Value::BytesValue(b),
        }
    }
}

#[derive(Debug, Serialize, PartialEq, Clone)]
#[serde(rename_all = "camelCase")]
pub(crate) struct KeyValue {
    key: Key,
    value: Value,
}

#[cfg(feature = "logs")]
impl From<(ts_opentelemetry_api::Key, ts_opentelemetry_api::logs::AnyValue)> for KeyValue {
    fn from((key, value): (ts_opentelemetry_api::Key, ts_opentelemetry_api::logs::AnyValue)) -> Self {
        KeyValue {
            key: key.into(),
            value: value.into(),
        }
    }
}

impl From<ts_opentelemetry_api::KeyValue> for KeyValue {
    fn from(value: ts_opentelemetry_api::KeyValue) -> Self {
        KeyValue {
            key: value.key.into(),
            value: value.value.into(),
        }
    }
}

impl From<&ts_opentelemetry_api::KeyValue> for KeyValue {
    fn from(value: &ts_opentelemetry_api::KeyValue) -> Self {
        KeyValue {
            key: value.key.clone().into(),
            value: value.value.clone().into(),
        }
    }
}

impl From<(ts_opentelemetry_api::Key, ts_opentelemetry_api::Value)> for KeyValue {
    fn from((key, value): (ts_opentelemetry_api::Key, ts_opentelemetry_api::Value)) -> Self {
        KeyValue {
            key: key.into(),
            value: value.into(),
        }
    }
}

#[derive(Debug, Clone, Serialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub(crate) struct Scope {
    #[serde(skip_serializing_if = "str::is_empty")]
    name: Cow<'static, str>,
    #[serde(skip_serializing_if = "Option::is_none")]
    version: Option<Cow<'static, str>>,
    #[serde(skip_serializing_if = "Vec::is_empty")]
    attributes: Vec<KeyValue>,
    #[serde(skip_serializing_if = "is_zero")]
    dropped_attributes_count: u64,
}

impl From<ts_opentelemetry_sdk::Scope> for Scope {
    fn from(value: ts_opentelemetry_sdk::Scope) -> Self {
        Scope {
            name: value.name,
            version: value.version,
            attributes: Vec::new(),
            dropped_attributes_count: 0,
        }
    }
}

pub(crate) fn as_unix_nano<S>(time: &SystemTime, serializer: S) -> Result<S::Ok, S::Error>
where
    S: Serializer,
{
    let nanos = time
        .duration_since(UNIX_EPOCH)
        .unwrap_or_default()
        .as_nanos();

    serializer.serialize_u128(nanos)
}