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