Skip to content

Commit 81dfbc1

Browse files
committed
feat(log): add append mode and timestamp options for serial log recording
1 parent f838387 commit 81dfbc1

9 files changed

Lines changed: 66 additions & 9 deletions

File tree

Tools/WebServer/app/routes/logs.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -160,16 +160,20 @@ def api_log_file_start():
160160

161161
data = request.json or {}
162162
path = data.get("path", "")
163+
append = data.get("append", True)
164+
timestamp = data.get("timestamp", True)
163165

164166
if not path:
165167
return jsonify({"success": False, "error": "No path provided"})
166168

167169
device = state.device
168-
success, error = log_recorder.start(path)
170+
success, error = log_recorder.start(path, append=append, timestamp=timestamp)
169171

170172
if success:
171173
device.log_file_enabled = True
172174
device.log_file_path = path
175+
device.log_file_append = append
176+
device.log_file_timestamp = timestamp
173177
state.save_config()
174178

175179
return jsonify({"success": success, "error": error})
@@ -203,6 +207,8 @@ def api_log_file_status():
203207
"path": log_recorder.path,
204208
"config_enabled": device.log_file_enabled,
205209
"config_path": device.log_file_path,
210+
"config_append": device.log_file_append,
211+
"config_timestamp": device.log_file_timestamp,
206212
}
207213
)
208214

Tools/WebServer/core/config_schema.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -342,6 +342,24 @@ def to_dict(self) -> dict:
342342
tooltip="Record serial communication logs to file",
343343
order=20,
344344
),
345+
ConfigItem(
346+
key="log_file_append",
347+
label="Append Mode",
348+
group=ConfigGroup.LOGGING,
349+
config_type=ConfigType.BOOLEAN,
350+
default=True,
351+
tooltip="Append to existing log file instead of truncating",
352+
order=25,
353+
),
354+
ConfigItem(
355+
key="log_file_timestamp",
356+
label="Record Timestamp",
357+
group=ConfigGroup.LOGGING,
358+
config_type=ConfigType.BOOLEAN,
359+
default=True,
360+
tooltip="Add timestamp prefix to each log line",
361+
order=26,
362+
),
345363
ConfigItem(
346364
key="serial_echo_enabled",
347365
label="Serial TX Echo",

Tools/WebServer/main.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -235,7 +235,11 @@ def restore_state():
235235
from services.log_recorder import log_recorder
236236

237237
logger.info(f"Restoring log file recording: {device.log_file_path}")
238-
success, error = log_recorder.start(device.log_file_path)
238+
success, error = log_recorder.start(
239+
device.log_file_path,
240+
append=device.log_file_append,
241+
timestamp=device.log_file_timestamp,
242+
)
239243
if success:
240244
logger.info("Log file recording restored")
241245
else:

Tools/WebServer/services/log_recorder.py

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,11 @@ def __init__(self):
2525
self._file = None
2626
self._enabled = False
2727
self._path = ""
28+
self._timestamp = True
2829

29-
def start(self, path: str) -> tuple[bool, str]:
30+
def start(
31+
self, path: str, append: bool = True, timestamp: bool = True
32+
) -> tuple[bool, str]:
3033
"""Start recording logs to file."""
3134
with self._lock:
3235
if self._enabled:
@@ -41,11 +44,15 @@ def start(self, path: str) -> tuple[bool, str]:
4144
if dir_path and not os.path.exists(dir_path):
4245
os.makedirs(dir_path, exist_ok=True)
4346

44-
self._file = open(path, "a", encoding="utf-8")
47+
mode = "a" if append else "w"
48+
self._file = open(path, mode, encoding="utf-8")
4549
self._enabled = True
4650
self._path = path
51+
self._timestamp = timestamp
4752

48-
logger.info(f"Log recording started: {path}")
53+
logger.info(
54+
f"Log recording started: {path} (append={append}, timestamp={timestamp})"
55+
)
4956
return True, ""
5057
except Exception as e:
5158
self._enabled = False
@@ -83,14 +90,16 @@ def write(self, message: str):
8390
return
8491

8592
try:
86-
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S.%f")[:-3]
8793
# Handle multi-line messages
8894
lines = message.split("\n")
8995
for i, line in enumerate(lines):
9096
if (
9197
line or i == 0
9298
): # Write first line even if empty, skip other empty lines
93-
if i == 0:
99+
if i == 0 and self._timestamp:
100+
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S.%f")[
101+
:-3
102+
]
94103
self._file.write(f"[{timestamp}] {line}\n")
95104
else:
96105
self._file.write(f"{line}\n")

Tools/WebServer/static/js/features/config.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -440,6 +440,10 @@ async function onLogFileEnabledChange() {
440440
if (pathInput) pathInput.value = path;
441441
}
442442

443+
const append = document.getElementById('logFileAppend')?.checked ?? true;
444+
const timestamp =
445+
document.getElementById('logFileTimestamp')?.checked ?? true;
446+
443447
try {
444448
// Check current status first
445449
const statusRes = await fetch('/api/log_file/status');
@@ -457,7 +461,7 @@ async function onLogFileEnabledChange() {
457461
const res = await fetch('/api/log_file/start', {
458462
method: 'POST',
459463
headers: { 'Content-Type': 'application/json' },
460-
body: JSON.stringify({ path }),
464+
body: JSON.stringify({ path, append, timestamp }),
461465
});
462466
const data = await res.json();
463467

Tools/WebServer/static/js/locales/en.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ window.i18nResources['en'] = {
4040
wakeup_shell_cnt: 'Wakeup Count',
4141
log_file_path: 'Log Path',
4242
log_file_enabled: 'Record Serial Logs',
43+
log_file_append: 'Append Mode',
44+
log_file_timestamp: 'Record Timestamp',
4345
serial_echo_enabled: 'Serial TX Echo',
4446
external_gdb_port: 'External GDB Port',
4547
ghidra_path: 'Ghidra Path',
@@ -471,6 +473,8 @@ window.i18nResources['en'] = {
471473
'Number of newlines to send before entering fl mode to wake up shell.',
472474
log_file_path: 'Path to save serial logs',
473475
log_file_enabled: 'Record serial communication logs to file',
476+
log_file_append: 'Append to existing log file instead of truncating',
477+
log_file_timestamp: 'Add timestamp prefix to each log line',
474478
serial_echo_enabled: 'Echo TX commands to SERIAL panel (for debugging)',
475479
ghidra_path:
476480
'Path to Ghidra installation directory (containing support/analyzeHeadless)',

Tools/WebServer/static/js/locales/zh-CN.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ window.i18nResources['zh-CN'] = {
4040
wakeup_shell_cnt: '唤醒次数',
4141
log_file_path: '日志路径',
4242
log_file_enabled: '记录串口日志',
43+
log_file_append: '追加模式',
44+
log_file_timestamp: '记录时间戳',
4345
serial_echo_enabled: '串口发送回显',
4446
external_gdb_port: '外部 GDB 端口',
4547
ghidra_path: 'Ghidra 路径',
@@ -458,6 +460,8 @@ window.i18nResources['zh-CN'] = {
458460
wakeup_shell_cnt: '进入 fl 模式前发送换行符的次数,用于唤醒 shell。',
459461
log_file_path: '串口日志保存路径',
460462
log_file_enabled: '将串口通信日志记录到文件',
463+
log_file_append: '追加到已有日志文件,关闭则截断文件重新写入',
464+
log_file_timestamp: '在每行日志前添加时间戳',
461465
serial_echo_enabled: '在串口面板回显发送的命令(用于调试)',
462466
ghidra_path: 'Ghidra 安装目录路径(包含 support/analyzeHeadless)',
463467
enable_decompile: '创建补丁模板时启用反编译(需要 Ghidra)',

Tools/WebServer/static/js/locales/zh-TW.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ window.i18nResources['zh-TW'] = {
4040
wakeup_shell_cnt: '喚醒次數',
4141
log_file_path: '日誌路徑',
4242
log_file_enabled: '記錄串列埠日誌',
43+
log_file_append: '追加模式',
44+
log_file_timestamp: '記錄時間戳',
4345
serial_echo_enabled: '串列埠傳送回顯',
4446
external_gdb_port: '外部 GDB 連接埠',
4547
ghidra_path: 'Ghidra 路徑',
@@ -459,6 +461,8 @@ window.i18nResources['zh-TW'] = {
459461
wakeup_shell_cnt: '進入 fl 模式前傳送換行符的次數,用於喚醒 shell。',
460462
log_file_path: '串列埠日誌儲存路徑',
461463
log_file_enabled: '將串列埠通訊日誌記錄到檔案',
464+
log_file_append: '追加到已有日誌檔案,關閉則截斷檔案重新寫入',
465+
log_file_timestamp: '在每行日誌前新增時間戳',
462466
serial_echo_enabled: '在串列埠面板回顯傳送的命令(用於除錯)',
463467
ghidra_path: 'Ghidra 安裝目錄路徑(包含 support/analyzeHeadless)',
464468
enable_decompile: '建立補丁範本時啟用反編譯(需要 Ghidra)',

Tools/WebServer/tests/test_main.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -301,7 +301,11 @@ def test_restore_state_log_recorder_success(self, mock_recorder):
301301

302302
main.restore_state()
303303

304-
mock_recorder.start.assert_called_once_with("/tmp/test.log")
304+
mock_recorder.start.assert_called_once_with(
305+
"/tmp/test.log",
306+
append=state.device.log_file_append,
307+
timestamp=state.device.log_file_timestamp,
308+
)
305309

306310
@patch("services.log_recorder.log_recorder")
307311
def test_restore_state_log_recorder_failure(self, mock_recorder):

0 commit comments

Comments
 (0)