Mountain/IPC/DevLog/
WriteToFile.rs1use 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
39 let Dir = ResolveLogDirectory();
40
41 if create_dir_all(&Dir).is_err() {
42 eprintln!("[DEV:LOG] Failed to create log directory {}", Dir.display());
43
44 return Mutex::new(None);
45 }
46
47 let Path = Dir.join("Mountain.dev.log");
48
49 match OpenOptions::new().create(true).append(true).open(&Path) {
50 Ok(File) => {
51 let mut Writer = BufWriter::with_capacity(64 * 1024, File);
52
53 let Header = format!(
54 "# Land dev log - started {}, pid {}, short={}, ipc-enabled={}\n",
55 SessionTimestamp::Fn(),
56 std::process::id(),
57 IsShort::Fn(),
58 IsEnabled::Fn("ipc"),
59 );
60
61 let _ = Writer.write_all(Header.as_bytes());
62
63 let _ = Writer.flush();
64
65 eprintln!("[DEV:LOG] File sink → {}", Path.display());
66
67 Mutex::new(Some(Writer))
68 },
69 Err(Error) => {
70 eprintln!("[DEV:LOG] Failed to open {}: {}", Path.display(), Error);
71
72 Mutex::new(None)
73 },
74 }
75 })
76}
77
78fn FileSinkEnabled() -> bool {
79 static ENABLED:OnceLock<bool> = OnceLock::new();
80
81 *ENABLED.get_or_init(|| {
82 match std::env::var("Record") {
83 Ok(Value) => matches!(Value.as_str(), "1" | "true" | "yes" | "on"),
84 Err(_) => cfg!(debug_assertions) && std::env::var("Trace").is_ok(),
85 }
86 })
87}
88
89fn ResolveLogDirectory() -> PathBuf {
90 let Stamp = SessionTimestamp::Fn();
91
92 let Base = match AppDataPrefix::Fn() {
93 Some(Prefix) => PathBuf::from(Prefix).join("logs"),
94
95 None => std::env::temp_dir().join("land-editor-logs"),
96 };
97
98 Base.join(Stamp)
99}