use alloc::sync::Arc;
use alloc::vec::Vec;
use core::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
use super::event::EventusVestigium;
struct SpinMutex<T> {
locked: AtomicBool,
data: core::cell::UnsafeCell<T>,
}
unsafe impl<T: Send> Send for SpinMutex<T> {}
unsafe impl<T: Send> Sync for SpinMutex<T> {}
impl<T> SpinMutex<T> {
fn new(data: T) -> Self {
SpinMutex {
locked: AtomicBool::new(false),
data: core::cell::UnsafeCell::new(data),
}
}
fn lock(&self) -> SpinMutexGuard<'_, T> {
while self
.locked
.compare_exchange_weak(false, true, Ordering::Acquire, Ordering::Relaxed)
.is_err()
{
core::hint::spin_loop();
}
SpinMutexGuard {
mutex: self,
_marker: core::marker::PhantomData,
}
}
}
struct SpinMutexGuard<'a, T> {
mutex: &'a SpinMutex<T>,
_marker: core::marker::PhantomData<T>,
}
unsafe impl<T: Sync> Sync for SpinMutexGuard<'_, T> {}
unsafe impl<T: Send> Send for SpinMutexGuard<'_, T> {}
impl<T> core::ops::Deref for SpinMutexGuard<'_, T> {
type Target = T;
fn deref(&self) -> &T {
unsafe { &*self.mutex.data.get() }
}
}
impl<T> core::ops::DerefMut for SpinMutexGuard<'_, T> {
fn deref_mut(&mut self) -> &mut T {
unsafe { &mut *self.mutex.data.get() }
}
}
impl<T> Drop for SpinMutexGuard<'_, T> {
fn drop(&mut self) {
self.mutex.locked.store(false, Ordering::Release);
}
}
pub trait CollectorVestigium: Send + Sync {
fn record(&self, event: EventusVestigium);
fn flush(&self);
#[inline]
fn is_enabled(&self, effect_id: u64) -> bool {
let _ = effect_id;
true
}
}
pub struct CollectorNullus;
impl CollectorNullus {
#[inline]
pub fn new() -> Self {
CollectorNullus
}
}
impl Default for CollectorNullus {
fn default() -> Self {
Self::new()
}
}
impl CollectorVestigium for CollectorNullus {
#[inline]
fn record(&self, _event: EventusVestigium) {
}
#[inline]
fn flush(&self) {
}
#[inline]
fn is_enabled(&self, _effect_id: u64) -> bool {
false
}
}
pub struct CollectorMemoriae {
events: SpinMutex<Vec<EventusVestigium>>,
max_events: usize,
dropped: AtomicUsize,
}
impl CollectorMemoriae {
pub fn new(max_events: usize) -> Self {
CollectorMemoriae {
events: SpinMutex::new(Vec::with_capacity(max_events.min(1024))),
max_events,
dropped: AtomicUsize::new(0),
}
}
#[inline]
pub fn events(&self) -> Vec<EventusVestigium> {
self.events.lock().clone()
}
#[inline]
pub fn event_count(&self) -> usize {
self.events.lock().len()
}
#[inline]
pub fn dropped_count(&self) -> usize {
self.dropped.load(Ordering::Relaxed)
}
#[inline]
pub fn clear(&self) {
self.events.lock().clear();
self.dropped.store(0, Ordering::Relaxed);
}
}
impl Default for CollectorMemoriae {
fn default() -> Self {
Self::new(10000)
}
}
impl CollectorVestigium for CollectorMemoriae {
#[inline]
fn record(&self, event: EventusVestigium) {
let mut events = self.events.lock();
if events.len() >= self.max_events {
self.dropped.fetch_add(1, Ordering::Relaxed);
} else {
events.push(event);
}
}
#[inline]
fn flush(&self) {
}
}
pub struct CollectorFiltrans<C: CollectorVestigium> {
inner: C,
min_level: super::Gradus,
include_effects: Vec<u64>,
exclude_effects: Vec<u64>,
}
impl<C: CollectorVestigium> CollectorFiltrans<C> {
pub fn new(inner: C) -> Self {
CollectorFiltrans {
inner,
min_level: super::Gradus::Vestigium,
include_effects: Vec::with_capacity(4),
exclude_effects: Vec::with_capacity(4),
}
}
#[inline]
pub fn with_min_level(mut self, level: super::Gradus) -> Self {
self.min_level = level;
self
}
#[inline]
pub fn include_effects(mut self, effects: Vec<u64>) -> Self {
self.include_effects = effects;
self
}
#[inline]
pub fn exclude_effects(mut self, effects: Vec<u64>) -> Self {
self.exclude_effects = effects;
self
}
#[inline]
fn should_record(&self, event: &EventusVestigium) -> bool {
if event.level() < self.min_level {
return false;
}
if self.exclude_effects.contains(&event.effect_id()) {
return false;
}
if !self.include_effects.is_empty() && !self.include_effects.contains(&event.effect_id()) {
return false;
}
true
}
}
impl<C: CollectorVestigium> CollectorVestigium for CollectorFiltrans<C> {
#[inline]
fn record(&self, event: EventusVestigium) {
if self.should_record(&event) {
self.inner.record(event);
}
}
#[inline]
fn flush(&self) {
self.inner.flush();
}
#[inline]
fn is_enabled(&self, effect_id: u64) -> bool {
if self.exclude_effects.contains(&effect_id) {
return false;
}
if !self.include_effects.is_empty() && !self.include_effects.contains(&effect_id) {
return false;
}
self.inner.is_enabled(effect_id)
}
}
pub struct CollectorCompositus {
collectors: Vec<Arc<dyn CollectorVestigium>>,
}
impl CollectorCompositus {
pub fn new() -> Self {
CollectorCompositus {
collectors: Vec::with_capacity(4),
}
}
#[inline]
pub fn with_collector<C: CollectorVestigium + 'static>(mut self, collector: C) -> Self {
self.collectors.push(Arc::new(collector));
self
}
#[inline]
pub fn with_collector_arc(mut self, collector: Arc<dyn CollectorVestigium>) -> Self {
self.collectors.push(collector);
self
}
}
impl Default for CollectorCompositus {
fn default() -> Self {
Self::new()
}
}
impl CollectorVestigium for CollectorCompositus {
#[inline]
fn record(&self, event: EventusVestigium) {
for collector in &self.collectors {
collector.record(event.clone());
}
}
#[inline]
fn flush(&self) {
for collector in &self.collectors {
collector.flush();
}
}
#[inline]
fn is_enabled(&self, effect_id: u64) -> bool {
self.collectors.iter().any(|c| c.is_enabled(effect_id))
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::tracing::{SpatiumId, VestigiumId};
fn make_event(effect_name: &str) -> EventusVestigium {
EventusVestigium::new(
VestigiumId::generate(),
SpatiumId::generate(),
1,
effect_name,
"operation",
)
}
#[test]
fn test_null_collector() {
let collector = CollectorNullus::new();
collector.record(make_event("Test"));
assert!(!collector.is_enabled(1));
}
#[test]
fn test_memory_collector() {
let collector = CollectorMemoriae::new(100);
collector.record(make_event("Effect1"));
collector.record(make_event("Effect2"));
assert_eq!(collector.event_count(), 2);
assert_eq!(collector.dropped_count(), 0);
}
#[test]
fn test_memory_collector_overflow() {
let collector = CollectorMemoriae::new(2);
collector.record(make_event("Effect1"));
collector.record(make_event("Effect2"));
collector.record(make_event("Effect3"));
assert_eq!(collector.event_count(), 2);
assert_eq!(collector.dropped_count(), 1);
}
#[test]
fn test_filtered_collector() {
let inner = CollectorMemoriae::new(100);
let collector = CollectorFiltrans::new(inner).with_min_level(super::super::Gradus::Info);
let event = make_event("Test");
collector.record(event);
}
#[test]
fn test_composite_collector() {
let collector1 = Arc::new(CollectorMemoriae::new(100));
let collector2 = Arc::new(CollectorMemoriae::new(100));
let composite = CollectorCompositus::new()
.with_collector_arc(collector1.clone())
.with_collector_arc(collector2.clone());
composite.record(make_event("Test"));
assert_eq!(collector1.event_count(), 1);
assert_eq!(collector2.event_count(), 1);
}
}