Skip to main content

Mountain/IPC/DevLog/
mod.rs

1#![allow(non_snake_case)]
2
3//! # DevLog - Tag-filtered development logging
4//!
5//! Tag-gated logging used across Mountain. Controlled by the
6//! `Trace` env var: `Trace=vfs,ipc` for selective tags,
7//! `Trace=all` for everything, `Trace=short` for the
8//! everything-but-firehose preset (path aliasing + dedupe).
9//! Mirror to a session log file when `Record=1` (or when
10//! `Trace` is set in a debug build).
11//!
12//! Layout: every public Fn/Struct lives in its own sibling
13//! file. The two macros (`dev_log!`, `otel_span!`) live here
14//! so `#[macro_export]` puts them at the crate root and the
15//! callsite spelling stays `dev_log!("ipc", "…")`.
16
17pub mod AliasPath;
18pub mod AppDataPrefix;
19pub mod DebugOnce;
20pub mod DedupState;
21pub mod EmitOTLPSpan;
22pub mod FlushDedup;
23pub mod InitEager;
24pub mod IsBenignEnoent;
25pub mod IsEnabled;
26pub mod IsShort;
27pub mod NowNano;
28pub mod SessionTimestamp;
29pub mod WriteToFile;
30
31/// Tag-gated dev log. Compiled out in release builds.
32///
33/// Under `Trace=short` aliases the long Tauri app-data prefix
34/// to `$APP` and collapses consecutive duplicates with a
35/// `(xN)` tail. The body is fully gated on
36/// `cfg!(debug_assertions)` so release builds get zero runtime
37/// cost (LLVM dead-codes the format / IsEnabled / file-sink
38/// path).
39#[macro_export]
40macro_rules! dev_log {
41	($Tag:expr, $($Arg:tt)*) => {
42		if cfg!(debug_assertions) && $crate::IPC::DevLog::IsEnabled::Fn($Tag) {
43			let RawMessage = format!($($Arg)*);
44			let TagUpper = $Tag.to_uppercase();
45			if $crate::IPC::DevLog::IsShort::Fn() {
46				let Aliased = $crate::IPC::DevLog::AliasPath::Fn(&RawMessage);
47				let Key = format!("{}:{}", TagUpper, Aliased);
48				let ShouldPrint = {
49					if let Ok(mut State) = $crate::IPC::DevLog::DedupState::DEDUP.lock() {
50						if State.LastKey == Key {
51							State.Count += 1;
52							false
53						} else {
54							let PrevCount = State.Count;
55							let HadPrev = !State.LastKey.is_empty();
56							State.LastKey = Key;
57							State.Count = 1;
58							if HadPrev && PrevCount > 1 {
59								let Tail = format!("  (x{})", PrevCount);
60								eprintln!("{}", Tail);
61								$crate::IPC::DevLog::WriteToFile::Fn(&Tail);
62							}
63							true
64						}
65					} else {
66						true
67					}
68				};
69				if ShouldPrint {
70					let Formatted = format!("[DEV:{}] {}", TagUpper, Aliased);
71					eprintln!("{}", Formatted);
72					$crate::IPC::DevLog::WriteToFile::Fn(&Formatted);
73				}
74			} else {
75				let Formatted = format!("[DEV:{}] {}", TagUpper, RawMessage);
76				eprintln!("{}", Formatted);
77				$crate::IPC::DevLog::WriteToFile::Fn(&Formatted);
78			}
79		}
80	};
81}
82
83/// Convenience macro: emit an OTLP span for an IPC handler.
84/// Usage: `otel_span!("file:readFile", StartNano, &[("path", &Path)]);`
85#[macro_export]
86macro_rules! otel_span {
87	($Name:expr, $Start:expr, $Attrs:expr) => {
88		$crate::IPC::DevLog::EmitOTLPSpan::Fn($Name, $Start, $crate::IPC::DevLog::NowNano::Fn(), $Attrs)
89	};
90	($Name:expr, $Start:expr) => {
91		$crate::IPC::DevLog::EmitOTLPSpan::Fn($Name, $Start, $crate::IPC::DevLog::NowNano::Fn(), &[])
92	};
93}