Skip to main content

actl_core/
trace.rs

1//! stderr 阶段打点(`ACTL_TRACE=1` 启用):慢环境归因仪器。
2//!
3//! stdout 契约不变(ADR-002)——trace 只进 stderr,禁用时零输出。
4//! 用途:慢日把"命令总耗时"分解到引擎阶段(窗口解析/唤醒/树遍历/L4/pattern),
5//! 与 envelope 的 duration_ms/wake_ms/waited_ms 构成完整观测面:
6//! envelope 字段 = 机器消费的常态数据;trace = 人类诊断的按需旁路。
7//! env 只在进程内读一次(OnceLock,同 timing.rs 纪律)。
8
9use std::sync::OnceLock;
10use std::time::Instant;
11
12/// 是否启用(ACTL_TRACE 存在且非 "0"/空)。
13pub fn enabled() -> bool {
14    static ON: OnceLock<bool> = OnceLock::new();
15    *ON.get_or_init(|| {
16        std::env::var("ACTL_TRACE")
17            .map(|v| !v.is_empty() && v != "0")
18            .unwrap_or(false)
19    })
20}
21
22/// 打一个阶段耗时点:`[actl-trace] <stage> <ms>ms`(未启用时 no-op)。
23pub fn stage(stage: &str, started: Instant) {
24    if enabled() {
25        eprintln!("[actl-trace] {stage} {}ms", started.elapsed().as_millis());
26    }
27}
28
29/// 区间计时 guard:`let _t = trace::scope("locate.dfs");` 离开作用域自动打点。
30/// 未启用时 Drop 内不输出(构造开销一次 Instant,纳秒级)。
31pub struct TraceScope {
32    stage: &'static str,
33    started: Instant,
34}
35
36pub fn scope(stage: &'static str) -> TraceScope {
37    TraceScope {
38        stage,
39        started: Instant::now(),
40    }
41}
42
43impl Drop for TraceScope {
44    fn drop(&mut self) {
45        stage(self.stage, self.started);
46    }
47}
48
49#[cfg(test)]
50mod tests {
51    use super::*;
52
53    #[test]
54    fn disabled_by_default_and_stage_is_silent_noop() {
55        // 不设 env(测试进程默认未设)→ enabled() 为 false;stage 不 panic 即可
56        let ok = std::panic::catch_unwind(|| {
57            stage("test.point", Instant::now());
58            let _t = scope("test.scope");
59        });
60        assert!(ok.is_ok());
61    }
62
63    #[test]
64    fn scope_drop_invokes_stage_without_panic() {
65        let _t = scope("test.lifetime");
66        drop(_t);
67    }
68}