use std::sync::Arc;
use std::time::Duration;
use ff_core::engine_backend::EngineBackend;
use tracing::{Level, event, span};
use super::EngineBackendLayer;
use super::hooks::{Admit, HookOutcome, HookedBackend, LayerHooks};
#[derive(Debug, Default, Clone, Copy)]
pub struct TracingLayer {
level: TracingLevel,
}
#[derive(Debug, Default, Clone, Copy)]
enum TracingLevel {
Trace,
Debug,
#[default]
Info,
Warn,
}
impl TracingLayer {
pub fn new() -> Self {
Self::default()
}
pub fn trace() -> Self {
Self {
level: TracingLevel::Trace,
}
}
pub fn debug() -> Self {
Self {
level: TracingLevel::Debug,
}
}
pub fn warn() -> Self {
Self {
level: TracingLevel::Warn,
}
}
fn at_level(level: TracingLevel) -> Self {
Self { level }
}
}
impl super::sealed::SealedLayer for TracingLayer {}
impl EngineBackendLayer for TracingLayer {
fn layer(&self, inner: Arc<dyn EngineBackend>) -> Arc<dyn EngineBackend> {
Arc::new(HookedBackend::new(
inner,
TracingHooks { level: self.level },
))
}
}
pub(crate) struct TracingHooks {
level: TracingLevel,
}
impl LayerHooks for TracingHooks {
fn before(&self, _method_name: &'static str) -> Admit {
Admit::Proceed
}
fn after(&self, method_name: &'static str, elapsed: Duration, outcome: HookOutcome<'_>) {
let elapsed_us = elapsed.as_micros() as u64;
let ok = matches!(outcome, HookOutcome::Ok);
match self.level {
TracingLevel::Trace => {
let sp = span!(Level::TRACE, "ff_sdk.backend", method = method_name);
let _g = sp.enter();
event!(
Level::TRACE,
method = method_name,
elapsed_us,
ok,
"engine_backend.call"
);
}
TracingLevel::Debug => {
let sp = span!(Level::DEBUG, "ff_sdk.backend", method = method_name);
let _g = sp.enter();
event!(
Level::DEBUG,
method = method_name,
elapsed_us,
ok,
"engine_backend.call"
);
}
TracingLevel::Info => {
let sp = span!(Level::INFO, "ff_sdk.backend", method = method_name);
let _g = sp.enter();
event!(
Level::INFO,
method = method_name,
elapsed_us,
ok,
"engine_backend.call"
);
}
TracingLevel::Warn => {
let sp = span!(Level::WARN, "ff_sdk.backend", method = method_name);
let _g = sp.enter();
event!(
Level::WARN,
method = method_name,
elapsed_us,
ok,
"engine_backend.call"
);
}
}
}
}
#[allow(dead_code)]
const _: fn(TracingLevel) -> TracingLayer = TracingLayer::at_level;
#[cfg(test)]
mod tests {
use super::*;
use crate::layer::{EngineBackendLayerExt, test_support::PassthroughBackend};
use ff_core::backend::{CapabilitySet, ClaimPolicy};
use ff_core::types::{LaneId, WorkerId, WorkerInstanceId};
#[tokio::test]
async fn delegates_and_counts_call() {
let raw = Arc::new(PassthroughBackend::default());
let raw_clone = raw.clone();
let inner: Arc<dyn EngineBackend> = raw;
let layered = inner.layer(TracingLayer::default());
let lane = LaneId::new("main");
let caps = CapabilitySet::new::<_, String>(Vec::<String>::new());
let policy = ClaimPolicy::new(
WorkerId::new("w"),
WorkerInstanceId::new("w-1"),
30_000,
None,
);
let _ = layered.claim(&lane, &caps, policy).await;
assert_eq!(raw_clone.calls("claim"), 1);
}
}