use std::{
ops::Deref,
pin::Pin,
sync::atomic::{AtomicU64, Ordering},
task::{Context, Poll},
time::Duration,
};
use fastrace::prelude::*;
use futures::{ready, Future};
use pin_project::pin_project;
use serde::{Deserialize, Serialize};
#[derive(Debug, Default)]
pub struct TracingConfig {
record_hybrid_insert_threshold_us: AtomicU64,
record_hybrid_get_threshold_us: AtomicU64,
record_hybrid_obtain_threshold_us: AtomicU64,
record_hybrid_remove_threshold_us: AtomicU64,
record_hybrid_fetch_threshold_us: AtomicU64,
}
impl TracingConfig {
pub fn update(&self, options: TracingOptions) {
if let Some(threshold) = options.record_hybrid_insert_threshold {
self.record_hybrid_insert_threshold_us
.store(threshold.as_micros() as _, Ordering::Relaxed);
}
if let Some(threshold) = options.record_hybrid_get_threshold {
self.record_hybrid_get_threshold_us
.store(threshold.as_micros() as _, Ordering::Relaxed);
}
if let Some(threshold) = options.record_hybrid_obtain_threshold {
self.record_hybrid_obtain_threshold_us
.store(threshold.as_micros() as _, Ordering::Relaxed);
}
if let Some(threshold) = options.record_hybrid_remove_threshold {
self.record_hybrid_remove_threshold_us
.store(threshold.as_micros() as _, Ordering::Relaxed);
}
if let Some(threshold) = options.record_hybrid_fetch_threshold {
self.record_hybrid_fetch_threshold_us
.store(threshold.as_micros() as _, Ordering::Relaxed);
}
}
pub fn record_hybrid_insert_threshold(&self) -> Duration {
Duration::from_micros(self.record_hybrid_insert_threshold_us.load(Ordering::Relaxed))
}
pub fn record_hybrid_get_threshold(&self) -> Duration {
Duration::from_micros(self.record_hybrid_get_threshold_us.load(Ordering::Relaxed))
}
pub fn record_hybrid_obtain_threshold(&self) -> Duration {
Duration::from_micros(self.record_hybrid_obtain_threshold_us.load(Ordering::Relaxed))
}
pub fn record_hybrid_remove_threshold(&self) -> Duration {
Duration::from_micros(self.record_hybrid_remove_threshold_us.load(Ordering::Relaxed))
}
pub fn record_hybrid_fetch_threshold(&self) -> Duration {
Duration::from_micros(self.record_hybrid_fetch_threshold_us.load(Ordering::Relaxed))
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TracingOptions {
record_hybrid_insert_threshold: Option<Duration>,
record_hybrid_get_threshold: Option<Duration>,
record_hybrid_obtain_threshold: Option<Duration>,
record_hybrid_remove_threshold: Option<Duration>,
record_hybrid_fetch_threshold: Option<Duration>,
}
impl Default for TracingOptions {
fn default() -> Self {
Self::new()
}
}
impl TracingOptions {
pub fn new() -> Self {
Self {
record_hybrid_insert_threshold: None,
record_hybrid_get_threshold: None,
record_hybrid_obtain_threshold: None,
record_hybrid_remove_threshold: None,
record_hybrid_fetch_threshold: None,
}
}
pub fn with_record_hybrid_insert_threshold(mut self, threshold: Duration) -> Self {
self.record_hybrid_insert_threshold = Some(threshold);
self
}
pub fn with_record_hybrid_get_threshold(mut self, threshold: Duration) -> Self {
self.record_hybrid_get_threshold = Some(threshold);
self
}
pub fn with_record_hybrid_obtain_threshold(mut self, threshold: Duration) -> Self {
self.record_hybrid_obtain_threshold = Some(threshold);
self
}
pub fn with_record_hybrid_remove_threshold(mut self, threshold: Duration) -> Self {
self.record_hybrid_remove_threshold = Some(threshold);
self
}
pub fn with_record_hybrid_fetch_threshold(mut self, threshold: Duration) -> Self {
self.record_hybrid_fetch_threshold = Some(threshold);
self
}
}
#[pin_project]
pub struct InRootSpan<F> {
#[pin]
inner: F,
root: Option<Span>,
threshold: Option<Duration>,
}
impl<F> InRootSpan<F> {
pub fn new(inner: F, root: Span) -> Self
where
F: Future,
{
Self {
inner,
root: Some(root),
threshold: None,
}
}
pub fn with_threshold(mut self, threshold: Duration) -> Self {
self.threshold = Some(threshold);
self
}
}
impl<F> Future for InRootSpan<F>
where
F: Future,
{
type Output = F::Output;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let this = self.project();
let _guard = this.root.as_ref().map(|s| s.set_local_parent());
let res = ready!(this.inner.poll(cx));
let mut root = this.root.take().unwrap();
if let (Some(elapsed), Some(threshold)) = (root.elapsed(), this.threshold.as_ref()) {
if &elapsed < threshold {
root.cancel();
}
}
Poll::Ready(res)
}
}
impl<F> Deref for InRootSpan<F> {
type Target = F;
fn deref(&self) -> &Self::Target {
&self.inner
}
}