alibabacloud-rum 0.1.0

Alibaba Cloud RUM SDK for native Rust applications.
Documentation
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;

use once_cell::sync::OnceCell;
use tokio::sync::Mutex;
use uuid::Uuid;

use crate::config::RumConfig;
use crate::context::{RumContext, RumContextSnapshot};
use crate::error::{Result, RumError};
use crate::event::{CustomEvent, CustomException, CustomLog, CustomResource, RumEvent, ViewEvent};
use crate::exporter::ExporterHandle;

static GLOBAL: OnceCell<Rum> = OnceCell::new();

#[derive(Clone)]
pub struct Rum {
    pub(crate) inner: Arc<RumInner>,
}

pub(crate) struct RumInner {
    pub config: RumConfig,
    pub context: Mutex<RumContext>,
    pub exporter: ExporterHandle,
    pub shutdown: AtomicBool,
    pub instance_id: String,
}

impl Rum {
    pub async fn init(config: RumConfig) -> Result<Self> {
        let exporter = ExporterHandle::start(config.clone());
        let context = RumContext::new();
        let initial_context = context.initial_view_snapshot();
        let rum = Self {
            inner: Arc::new(RumInner {
                config,
                context: Mutex::new(context),
                exporter,
                shutdown: AtomicBool::new(false),
                instance_id: Uuid::new_v4().hyphenated().to_string(),
            }),
        };
        rum.enqueue_event(RumEvent::View(ViewEvent::initial(initial_context)))?;
        Ok(rum)
    }

    pub async fn flush(&self) -> Result<()> {
        self.inner.exporter.flush().await
    }

    pub async fn shutdown(&self) -> Result<()> {
        if self.inner.shutdown.swap(true, Ordering::SeqCst) {
            return Ok(());
        }
        self.inner.exporter.shutdown().await
    }

    pub async fn report_custom_event(&self, event: CustomEvent) -> Result<()> {
        self.ensure_running()?;
        let context = self.snapshot_context().await;
        self.enqueue_event(RumEvent::Custom(event.into_event(context)))
    }

    pub async fn report_custom_log(&self, log: CustomLog) -> Result<()> {
        self.ensure_running()?;
        let context = self.snapshot_context().await;
        self.enqueue_event(RumEvent::Custom(log.into_event(context)))
    }

    pub async fn report_custom_exception(&self, exception: CustomException) -> Result<()> {
        self.ensure_running()?;
        let context = self.snapshot_context().await;
        self.enqueue_event(RumEvent::Exception(exception.into_event(context)))
    }

    pub async fn report_custom_resource(&self, resource: CustomResource) -> Result<()> {
        self.ensure_running()?;
        let context = self.snapshot_context().await;
        self.enqueue_event(RumEvent::Resource(Box::new(
            resource.into_resource_event(context, self.config()),
        )))
    }

    pub(crate) fn enqueue_event(&self, event: RumEvent) -> Result<()> {
        if self.is_shutdown() {
            return Err(RumError::Shutdown);
        }
        self.inner.exporter.try_enqueue(event)
    }

    pub(crate) async fn snapshot_context(&self) -> RumContextSnapshot {
        self.inner.context.lock().await.snapshot()
    }

    pub(crate) fn config(&self) -> &RumConfig {
        &self.inner.config
    }

    pub(crate) fn instance_id(&self) -> &str {
        &self.inner.instance_id
    }

    pub(crate) fn is_shutdown(&self) -> bool {
        self.inner.shutdown.load(Ordering::SeqCst)
    }

    fn ensure_running(&self) -> Result<()> {
        if self.is_shutdown() {
            return Err(RumError::Shutdown);
        }
        Ok(())
    }

    #[cfg(feature = "reqwest")]
    pub fn wrap_reqwest(
        &self,
        client: reqwest::Client,
    ) -> Result<crate::instrumentation::reqwest::RumReqwestClient> {
        Ok(crate::instrumentation::reqwest::RumReqwestClient::new(
            client,
            Some(self.clone()),
        ))
    }
}

pub async fn init_as_global(config: RumConfig) -> Result<()> {
    if GLOBAL.get().is_some() {
        return Err(RumError::AlreadyInitialized);
    }
    let rum = Rum::init(config).await?;
    GLOBAL.set(rum).map_err(|_| RumError::AlreadyInitialized)
}

pub fn global() -> Result<Rum> {
    GLOBAL.get().cloned().ok_or(RumError::NotInitialized)
}

pub async fn flush() -> Result<()> {
    global()?.flush().await
}

pub async fn shutdown() -> Result<()> {
    global()?.shutdown().await
}

pub async fn report_custom_event(event: CustomEvent) -> Result<()> {
    global()?.report_custom_event(event).await
}

pub async fn report_custom_log(log: CustomLog) -> Result<()> {
    global()?.report_custom_log(log).await
}

pub async fn report_custom_exception(exception: CustomException) -> Result<()> {
    global()?.report_custom_exception(exception).await
}

pub async fn report_custom_resource(resource: CustomResource) -> Result<()> {
    global()?.report_custom_resource(resource).await
}