Skip to content

Commit 6eea32b

Browse files
feat: implement writable datastore directory resolution and update logging path in engine
1 parent cd1c46f commit 6eea32b

2 files changed

Lines changed: 41 additions & 6 deletions

File tree

engine/config.py

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@
55
Modules import from this file to ensure consistent behavior across the engine.
66
"""
77

8+
from __future__ import annotations
9+
10+
import os
811
from pathlib import Path
912

1013
# ---------------------------------------------------------------------------
@@ -14,8 +17,42 @@
1417
# Root directory of the app package
1518
APP_DIR = Path(__file__).parent
1619

17-
# Persistent data storage directory
18-
DATASTORE_DIR = APP_DIR / "datastore"
20+
def _writable_datastore_dir() -> Path:
21+
"""
22+
Resolve a writable datastore directory.
23+
24+
- Dev mode: using `engine/datastore/` is fine.
25+
- Installed mode (e.g. under Program Files): we must not write into the install dir.
26+
27+
Order:
28+
1) SENTRACORE_DATA_DIR (explicit override)
29+
2) Local package datastore if it can be created/written
30+
3) %LOCALAPPDATA%/SentraCore/datastore (Windows)
31+
4) ~/.local/share/SentraCore/datastore (fallback)
32+
"""
33+
override = os.environ.get("SENTRACORE_DATA_DIR")
34+
if override:
35+
return Path(override)
36+
37+
local = APP_DIR / "datastore"
38+
try:
39+
local.mkdir(parents=True, exist_ok=True)
40+
test = local / ".write_test"
41+
test.write_text("ok", encoding="utf-8")
42+
test.unlink(missing_ok=True)
43+
return local
44+
except OSError:
45+
pass
46+
47+
local_appdata = os.environ.get("LOCALAPPDATA")
48+
if local_appdata:
49+
return Path(local_appdata) / "SentraCore" / "datastore"
50+
51+
return Path.home() / ".local" / "share" / "SentraCore" / "datastore"
52+
53+
54+
# Persistent data storage directory (writable)
55+
DATASTORE_DIR = _writable_datastore_dir()
1956

2057
# Baseline model persistence file
2158
BASELINE_FILE = DATASTORE_DIR / "baseline.json"

engine/main.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
from engine.baseline.baseline_model import BaselineModel
3232
from engine.buffer.time_series_buffer import TimeSeriesBuffer
3333
from engine.collector.system_collector import SystemCollector
34-
from engine.config import API_HOST, API_PORT, COLLECTION_INTERVAL_SEC
34+
from engine.config import API_HOST, API_PORT, COLLECTION_INTERVAL_SEC, DATASTORE_DIR
3535
from engine.events.event_logger import EventLogger
3636
from engine.normalization.normalizer import NormalizedSnapshot, Normalizer
3737
from engine.process.process_tracker import ProcessImpact, ProcessTracker
@@ -273,16 +273,14 @@ def _configure_logging() -> None:
273273
"""Configure structured logging for the engine."""
274274
# When packaged with PyInstaller + --noconsole, sys.stdout/sys.stderr can be None.
275275
# In that case, log to a file so the engine can still start and we can diagnose issues.
276-
from pathlib import Path
277-
278276
log_format = "%(asctime)s | %(levelname)-7s | %(name)s | %(message)s"
279277
datefmt = "%H:%M:%S"
280278

281279
handlers: list[logging.Handler] = []
282280
if getattr(sys, "stdout", None) is not None:
283281
handlers.append(logging.StreamHandler(sys.stdout))
284282
else:
285-
log_dir = Path(__file__).parent / "datastore" / "logs"
283+
log_dir = DATASTORE_DIR / "logs"
286284
log_dir.mkdir(parents=True, exist_ok=True)
287285
handlers.append(logging.FileHandler(log_dir / "engine.log", encoding="utf-8"))
288286

0 commit comments

Comments
 (0)