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