feat: always write a fixed-name copy of server/sud logs, use .json for sud logs
Alongside the dated log files, ServerLogTask/SudLogTask now also write (and overwrite) a fixed-name copy on every write() - logs/log_latest.json and logs/log_latest_sud.json - so a dashboard/tail-style consumer can point at one unchanging path instead of tracking the current run's timestamped filename. Also switches SudLogTask's own dated/latest filenames from .log to .json, matching what they've always actually contained. This incidentally fixes utils/analyze_log.py's two-argument CLI form, which already assumed a .json extension for sud logs. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JFDeoTKBDkXRhPfnyDEoBt
This commit is contained in:
@@ -744,7 +744,7 @@ same sample format and `log_interval` checkpointing, but only records while
|
|||||||
a Sud run is actually active. A fresh run starts recording on a genuine
|
a Sud run is actually active. A fresh run starts recording on a genuine
|
||||||
`Start` from `IDLE`/`DONE` (a `Pause`→resume `Start` is a no-op - it's still
|
`Start` from `IDLE`/`DONE` (a `Pause`→resume `Start` is a no-op - it's still
|
||||||
the same run) and is written out on `Stop` or natural completion (`DONE`),
|
the same run) and is written out on `Stop` or natural completion (`DONE`),
|
||||||
to `logs/log_<date>T<time>_<sud-name>.log`.
|
to `logs/log_<date>T<time>_<sud-name>.json`.
|
||||||
|
|
||||||
`server/brewpi.py` also mirrors everything it prints (every component's
|
`server/brewpi.py` also mirrors everything it prints (every component's
|
||||||
`print()`-based status/debug output) to a plain text log,
|
`print()`-based status/debug output) to a plain text log,
|
||||||
|
|||||||
+13
-2
@@ -89,6 +89,13 @@ class ServerLogTask(ATask):
|
|||||||
def _filename(self):
|
def _filename(self):
|
||||||
return 'log_{}.json'.format(self._run_id)
|
return 'log_{}.json'.format(self._run_id)
|
||||||
|
|
||||||
|
def _latest_filename(self):
|
||||||
|
"""Fixed (no-date) name written alongside the dated one on every
|
||||||
|
write() - always overwritten, so a dashboard/tail-style consumer
|
||||||
|
can point at one unchanging path instead of tracking the current
|
||||||
|
run's own timestamped filename."""
|
||||||
|
return 'log_latest.json'
|
||||||
|
|
||||||
def _extra_log_data(self):
|
def _extra_log_data(self):
|
||||||
"""Hook for subclasses (SudLogTask) to contribute additional keys
|
"""Hook for subclasses (SudLogTask) to contribute additional keys
|
||||||
to write()'s output - see there."""
|
to write()'s output - see there."""
|
||||||
@@ -105,7 +112,6 @@ class ServerLogTask(ATask):
|
|||||||
return
|
return
|
||||||
os.makedirs(self.path, exist_ok=True)
|
os.makedirs(self.path, exist_ok=True)
|
||||||
filename = self._filename()
|
filename = self._filename()
|
||||||
path = os.path.join(self.path, filename)
|
|
||||||
log_data = {'Name': self._log_name()}
|
log_data = {'Name': self._log_name()}
|
||||||
if self.dt is not None:
|
if self.dt is not None:
|
||||||
log_data['dt'] = self.dt
|
log_data['dt'] = self.dt
|
||||||
@@ -122,6 +128,11 @@ class ServerLogTask(ATask):
|
|||||||
log_data['PlantParams'] = self._param_events
|
log_data['PlantParams'] = self._param_events
|
||||||
log_data.update(self._extra_log_data())
|
log_data.update(self._extra_log_data())
|
||||||
log_data['Samples'] = self._samples
|
log_data['Samples'] = self._samples
|
||||||
with open(path, 'w') as f:
|
# Written to both the dated filename (this run/session's own
|
||||||
|
# permanent record) and a fixed, always-overwritten one (see
|
||||||
|
# _latest_filename()) - same content, so a consumer wanting
|
||||||
|
# "whatever's most current" never has to track the dated name.
|
||||||
|
for name in (filename, self._latest_filename()):
|
||||||
|
with open(os.path.join(self.path, name), 'w') as f:
|
||||||
json.dump(log_data, f, indent='\t')
|
json.dump(log_data, f, indent='\t')
|
||||||
print('Server log: wrote {} samples to {}'.format(len(self._samples), filename))
|
print('Server log: wrote {} samples to {}'.format(len(self._samples), filename))
|
||||||
|
|||||||
+9
-2
@@ -11,7 +11,7 @@ class SudLogTask(ServerLogTask):
|
|||||||
"""Records temp/power samples for a single Sud run - same sample format
|
"""Records temp/power samples for a single Sud run - same sample format
|
||||||
as ServerLogTask, but only while a run is active: recording starts on
|
as ServerLogTask, but only while a run is active: recording starts on
|
||||||
Play and is written out on Stop (or natural completion), to
|
Play and is written out on Stop (or natural completion), to
|
||||||
logs/log_{date_time}_{sud_name}.log. A Pause/resume doesn't start a new
|
logs/log_{date_time}_{sud_name}.json. A Pause/resume doesn't start a new
|
||||||
file - only a fresh Play (from IDLE/DONE) does."""
|
file - only a fresh Play (from IDLE/DONE) does."""
|
||||||
|
|
||||||
def __init__(self, tc: APid, heater: AHeater, heater_task, sud, interval, path='./logs', config=None, dt=None, log_interval=None, sim_warp_factor=None):
|
def __init__(self, tc: APid, heater: AHeater, heater_task, sud, interval, path='./logs', config=None, dt=None, log_interval=None, sim_warp_factor=None):
|
||||||
@@ -26,7 +26,14 @@ class SudLogTask(ServerLogTask):
|
|||||||
|
|
||||||
def _filename(self):
|
def _filename(self):
|
||||||
name_part = _safe_filename_part(self.sud.name or 'Sud')
|
name_part = _safe_filename_part(self.sud.name or 'Sud')
|
||||||
return 'log_{}_{}.log'.format(self._run_id, name_part)
|
return 'log_{}_{}.json'.format(self._run_id, name_part)
|
||||||
|
|
||||||
|
def _latest_filename(self):
|
||||||
|
# Fixed regardless of which schedule is running, unlike _filename()
|
||||||
|
# above - a dashboard/tail-style consumer can point at one
|
||||||
|
# unchanging path across different Suds, not just different runs
|
||||||
|
# of the same one.
|
||||||
|
return 'log_latest_sud.json'
|
||||||
|
|
||||||
def _log_name(self):
|
def _log_name(self):
|
||||||
return self.sud.name
|
return self.sud.name
|
||||||
|
|||||||
Reference in New Issue
Block a user