Skip to main content

DevelopmentNodeEnvironment_MicrosoftVSCodeDependency_22NodeVersion_Bundle_Clean_Debug_ElectronProfile_EsbuildCompiler_Mountain/IPC/DevLog/
WriteToFile.rs

1//! Append a single formatted line to the session's
2//! `Mountain.dev.log`. The file sink is lazy: opens on first
3//! call, no-ops if `Record=0` or the directory cannot be
4//! created. Flushes per line so `tail -f` shows live output.
5
6use std::{
7	fs::{File, OpenOptions, create_dir_all},
8	io::{BufWriter, Write as IoWrite},
9	path::PathBuf,
10	sync::{Mutex, OnceLock},
11};
12
13use crate::IPC::DevLog::{AppDataPrefix, IsEnabled, IsShort, SessionTimestamp};
14
15static LOG_FILE:OnceLock<Mutex<Option<BufWriter<File>>>> = OnceLock::new();
16
17pub fn Fn(Line:&str) {
18	let Sink = InitFileSink();
19
20	if let Ok(mut Guard) = Sink.lock() {
21		if let Some(Writer) = Guard.as_mut() {
22			let _ = Writer.write_all(Line.as_bytes());
23
24			if !Line.ends_with('\n') {
25				let _ = Writer.write_all(b"\n");
26			}
27
28			let _ = Writer.flush();
29		}
30	}
31}
32
33pub(super) fn InitFileSink() -> &'static Mutex<Option<BufWriter<File>>> {
34	LOG_FILE.get_or_init(|| {
35		if !FileSinkEnabled() {
36			return Mutex::new(None);
37		}
38		let Dir = ResolveLogDirectory();
39		if create_dir_all(&Dir).is_err() {
40			eprintln!("[DEV:LOG] Failed to create log directory {}", Dir.display());
41			return Mutex::new(None);
42		}
43		let Path = Dir.join("Mountain.dev.log");
44		match OpenOptions::new().create(true).append(true).open(&Path) {
45			Ok(File) => {
46				let mut Writer = BufWriter::with_capacity(64 * 1024, File);
47				let Header = format!(
48					"# Land dev log - started {}, pid {}, short={}, ipc-enabled={}\n",
49					SessionTimestamp::Fn(),
50					std::process::id(),
51					IsShort::Fn(),
52					IsEnabled::Fn("ipc"),
53				);
54				let _ = Writer.write_all(Header.as_bytes());
55				let _ = Writer.flush();
56				eprintln!("[DEV:LOG] File sink → {}", Path.display());
57				Mutex::new(Some(Writer))
58			},
59			Err(Error) => {
60				eprintln!("[DEV:LOG] Failed to open {}: {}", Path.display(), Error);
61				Mutex::new(None)
62			},
63		}
64	})
65}
66
67fn FileSinkEnabled() -> bool {
68	static ENABLED:OnceLock<bool> = OnceLock::new();
69
70	*ENABLED.get_or_init(|| {
71		match std::env::var("Record") {
72			Ok(Value) => matches!(Value.as_str(), "1" | "true" | "yes" | "on"),
73			Err(_) => cfg!(debug_assertions) && std::env::var("Trace").is_ok(),
74		}
75	})
76}
77
78fn ResolveLogDirectory() -> PathBuf {
79	let Stamp = SessionTimestamp::Fn();
80
81	let Base = match AppDataPrefix::Fn() {
82		Some(Prefix) => PathBuf::from(Prefix).join("logs"),
83
84		None => std::env::temp_dir().join("land-editor-logs"),
85	};
86
87	Base.join(Stamp)
88}