#![cfg_attr(docsrs, feature(doc_cfg))]
#![doc(issue_tracker_base_url = "https://github.com/primait/prima_datadog.rs/issues")]
use std::future::Future;
use configuration::Configuration;
pub use dogstatsd::{EventAlertType, EventOptions, EventPriority, ServiceCheckOptions, ServiceStatus};
use once_cell::sync::OnceCell;
pub use client::DogstatsdClient;
pub use tracker::*;
use crate::error::Error;
mod client;
pub mod configuration;
pub mod error;
mod macros;
pub mod timing_guard;
pub mod tracker;
#[cfg(test)]
#[path = "tests/mod.rs"]
mod tests;
pub trait TagsProvider<S>
where
Self: AsRef<[S]>,
S: AsRef<str>,
{
}
impl<S, T> TagsProvider<S> for T
where
T: AsRef<[S]>,
S: AsRef<str>,
{
}
pub const EMPTY_TAGS: &[&str] = &[];
static INSTANCE: OnceCell<Datadog<dogstatsd::Client>> = OnceCell::new();
pub struct Datadog<C: DogstatsdClient> {
inner: C,
tag_tracker: Tracker,
}
impl Datadog<dogstatsd::Client> {
pub fn init(mut configuration: Configuration) -> Result<(), Error> {
let mut initialized: bool = false;
let _ = INSTANCE.get_or_try_init::<_, Error>(|| {
initialized = true;
let tracker_config = configuration.take_tracker_config();
let dogstatsd_client_options: dogstatsd::Options = configuration.into();
let client: dogstatsd::Client = dogstatsd::Client::new(dogstatsd_client_options)?;
Ok(Self::new(client, tracker_config))
})?;
if initialized {
Ok(())
} else {
Err(Error::OnceCellAlreadyInitialized)
}
}
pub fn incr<S: AsRef<str>>(metric: impl AsRef<str>, tags: impl TagsProvider<S>) {
if let Some(instance) = INSTANCE.get() {
instance.do_incr(metric.as_ref(), tags);
}
}
pub fn decr<S: AsRef<str>>(metric: impl AsRef<str>, tags: impl TagsProvider<S>) {
if let Some(instance) = INSTANCE.get() {
instance.do_decr(metric.as_ref(), tags);
}
}
pub fn count<S: AsRef<str>>(metric: impl AsRef<str>, count: i64, tags: impl TagsProvider<S>) {
if let Some(instance) = INSTANCE.get() {
instance.do_count(metric.as_ref(), count, tags);
}
}
pub fn time<S, F, O>(metric: impl AsRef<str>, tags: impl TagsProvider<S>, block: F) -> O
where
S: AsRef<str>,
F: FnOnce() -> O,
{
if let Some(instance) = INSTANCE.get() {
instance.do_time(metric.as_ref(), tags, block)
} else {
block()
}
}
pub async fn async_time<S, F, T, O>(metric: &str, tags: impl TagsProvider<S> + Send + Sync, block: F) -> O
where
S: AsRef<str> + Sync,
F: FnOnce() -> T + Send,
T: Future<Output = O> + Send,
{
if let Some(instance) = INSTANCE.get() {
instance.do_async_time(metric.as_ref(), tags, block).await
} else {
block().await
}
}
pub fn timing<S: AsRef<str>>(metric: impl AsRef<str>, ms: i64, tags: impl TagsProvider<S>) {
if let Some(instance) = INSTANCE.get() {
instance.do_timing(metric.as_ref(), ms, tags);
}
}
pub fn gauge<S: AsRef<str>>(metric: impl AsRef<str>, value: impl AsRef<str>, tags: impl TagsProvider<S>) {
if let Some(instance) = INSTANCE.get() {
instance.do_gauge(metric.as_ref(), value.as_ref(), tags);
}
}
pub fn histogram<S: AsRef<str>>(metric: impl AsRef<str>, value: impl AsRef<str>, tags: impl TagsProvider<S>) {
if let Some(instance) = INSTANCE.get() {
instance.do_histogram(metric.as_ref(), value.as_ref(), tags);
}
}
pub fn distribution<S: AsRef<str>>(metric: impl AsRef<str>, value: impl AsRef<str>, tags: impl TagsProvider<S>) {
if let Some(instance) = INSTANCE.get() {
instance.do_distribution(metric.as_ref(), value.as_ref(), tags);
}
}
pub fn set<S: AsRef<str>>(metric: impl AsRef<str>, value: impl AsRef<str>, tags: impl TagsProvider<S>) {
if let Some(instance) = INSTANCE.get() {
instance.do_set(metric.as_ref(), value.as_ref(), tags);
}
}
pub fn service_check<S: AsRef<str>>(
metric: impl AsRef<str>,
value: ServiceStatus,
tags: impl TagsProvider<S>,
options: Option<ServiceCheckOptions>,
) {
if let Some(instance) = INSTANCE.get() {
instance.do_service_check(metric.as_ref(), value, tags, options);
}
}
pub fn event<S: AsRef<str>>(metric: impl AsRef<str>, text: impl AsRef<str>, tags: impl TagsProvider<S>) {
if let Some(instance) = INSTANCE.get() {
instance.do_event(metric.as_ref(), text.as_ref(), tags);
}
}
pub fn event_with_options<S: AsRef<str>>(
metric: impl AsRef<str>,
text: impl AsRef<str>,
tags: impl TagsProvider<S>,
options: Option<EventOptions>,
) {
if let Some(instance) = INSTANCE.get() {
instance.do_event_with_options(metric.as_ref(), text.as_ref(), tags, options);
}
}
pub fn enter_timing<S: AsRef<str>, P: TagsProvider<S>>(
metric: impl AsRef<str>,
tags: P,
) -> timing_guard::TimingGuard<S, P> {
timing_guard::TimingGuard::new(metric, tags)
}
}
impl<C: DogstatsdClient> Datadog<C> {
fn new(client: C, tracker_config: TagTrackerConfiguration) -> Self {
Self {
inner: client,
tag_tracker: tracker_config.build(),
}
}
pub(crate) fn do_incr<S: AsRef<str>>(&self, metric: impl AsRef<str>, tags: impl TagsProvider<S>) {
self.inner.incr(
metric.as_ref(),
self.tag_tracker.track(&self.inner, metric.as_ref(), tags),
);
}
pub(crate) fn do_decr<S: AsRef<str>>(&self, metric: impl AsRef<str>, tags: impl TagsProvider<S>) {
self.inner.decr(
metric.as_ref(),
self.tag_tracker.track(&self.inner, metric.as_ref(), tags),
);
}
pub(crate) fn do_count<S: AsRef<str>>(&self, metric: impl AsRef<str>, count: i64, tags: impl TagsProvider<S>) {
self.inner.count(
metric.as_ref(),
count,
self.tag_tracker.track(&self.inner, metric.as_ref(), tags),
);
}
pub(crate) fn do_time<S, F, O>(&self, metric: impl AsRef<str>, tags: impl TagsProvider<S>, block: F) -> O
where
S: AsRef<str>,
F: FnOnce() -> O,
{
self.inner.time(
metric.as_ref(),
self.tag_tracker.track(&self.inner, metric.as_ref(), tags),
block,
)
}
pub(crate) async fn do_async_time<S, F, T, O>(
&self,
metric: &str,
tags: impl TagsProvider<S> + Send + Sync,
block: F,
) -> O
where
S: AsRef<str> + Sync,
F: FnOnce() -> T + Send,
T: Future<Output = O> + Send,
{
self.inner
.async_time(
metric.as_ref(),
self.tag_tracker.track(&self.inner, metric.as_ref(), tags),
block,
)
.await
}
pub(crate) fn do_timing<S: AsRef<str>>(&self, metric: impl AsRef<str>, ms: i64, tags: impl TagsProvider<S>) {
self.inner.timing(
metric.as_ref(),
ms,
self.tag_tracker.track(&self.inner, metric.as_ref(), tags),
);
}
pub(crate) fn do_gauge<S: AsRef<str>>(
&self,
metric: impl AsRef<str>,
value: impl AsRef<str>,
tags: impl TagsProvider<S>,
) {
self.inner.gauge(
metric.as_ref(),
value.as_ref(),
self.tag_tracker.track(&self.inner, metric.as_ref(), tags),
);
}
pub(crate) fn do_histogram<S: AsRef<str>>(
&self,
metric: impl AsRef<str>,
value: impl AsRef<str>,
tags: impl TagsProvider<S>,
) {
self.inner.histogram(
metric.as_ref(),
value.as_ref(),
self.tag_tracker.track(&self.inner, metric.as_ref(), tags),
);
}
pub(crate) fn do_distribution<S: AsRef<str>>(
&self,
metric: impl AsRef<str>,
value: impl AsRef<str>,
tags: impl TagsProvider<S>,
) {
self.inner.distribution(
metric.as_ref(),
value.as_ref(),
self.tag_tracker.track(&self.inner, metric.as_ref(), tags),
);
}
pub(crate) fn do_set<S: AsRef<str>>(
&self,
metric: impl AsRef<str>,
value: impl AsRef<str>,
tags: impl TagsProvider<S>,
) {
self.inner.set(
metric.as_ref(),
value.as_ref(),
self.tag_tracker.track(&self.inner, metric.as_ref(), tags),
);
}
pub(crate) fn do_service_check<S: AsRef<str>>(
&self,
metric: impl AsRef<str>,
value: ServiceStatus,
tags: impl TagsProvider<S>,
options: Option<ServiceCheckOptions>,
) {
self.inner.service_check(
metric.as_ref(),
value,
self.tag_tracker.track(&self.inner, metric.as_ref(), tags),
options,
);
}
pub(crate) fn do_event<S: AsRef<str>>(
&self,
metric: impl AsRef<str>,
text: impl AsRef<str>,
tags: impl TagsProvider<S>,
) {
self.inner.event(
metric.as_ref(),
text.as_ref(),
self.tag_tracker.track(&self.inner, metric.as_ref(), tags),
);
}
pub(crate) fn do_event_with_options<S: AsRef<str>>(
&self,
metric: impl AsRef<str>,
text: impl AsRef<str>,
tags: impl TagsProvider<S>,
options: Option<EventOptions>,
) {
self.inner.event_with_options(
metric.as_ref(),
text.as_ref(),
self.tag_tracker.track(&self.inner, metric.as_ref(), tags),
options,
);
}
}