Added timestamps to project logging

This commit is contained in:
Eric Ratliff
2026-02-19 13:29:06 -06:00
parent c6f2dfc1b5
commit 9bda9123ea
5 changed files with 197 additions and 8 deletions

View File

@@ -17,6 +17,7 @@ const REFRESHABLE_FILES: &[&str] = &[
"monitor.sh", "monitor.sh",
"monitor.bat", "monitor.bat",
"_detect_port.ps1", "_detect_port.ps1",
"_monitor_filter.ps1",
"test/run_tests.sh", "test/run_tests.sh",
"test/run_tests.bat", "test/run_tests.bat",
]; ];

View File

@@ -0,0 +1,18 @@
param(
[Parameter(Mandatory=$true)][string]$Port,
[Parameter(Mandatory=$true)][string]$Baud,
[switch]$Timestamps,
[string]$LogFile
)
arduino-cli monitor -p $Port -c "baudrate=$Baud" | ForEach-Object {
$line = $_
if ($Timestamps) {
$ts = Get-Date -Format "[HH:mm:ss.fff]"
$line = "$ts $line"
}
Write-Host $line
if ($LogFile) {
Add-Content -Path $LogFile -Value $line
}
}

View File

@@ -10,6 +10,8 @@ setlocal enabledelayedexpansion
:: monitor.bat -p COM3 Specify port :: monitor.bat -p COM3 Specify port
:: monitor.bat -b 9600 Override baud rate :: monitor.bat -b 9600 Override baud rate
:: monitor.bat --board mega Use baud from a named board :: monitor.bat --board mega Use baud from a named board
:: monitor.bat --timestamps Prepend [HH:MM:SS.mmm] to each line
:: monitor.bat --log session.log Also write output to a file
set "SCRIPT_DIR=%~dp0" set "SCRIPT_DIR=%~dp0"
set "SCRIPT_DIR=%SCRIPT_DIR:~0,-1%" set "SCRIPT_DIR=%SCRIPT_DIR:~0,-1%"
@@ -60,6 +62,8 @@ if "%BAUD%"=="" set "BAUD=115200"
:: -- Parse arguments ------------------------------------------------------ :: -- Parse arguments ------------------------------------------------------
set "PORT=" set "PORT="
set "BOARD_NAME=" set "BOARD_NAME="
set "DO_TIMESTAMPS=0"
set "LOG_FILE="
:parse_args :parse_args
if "%~1"=="" goto done_args if "%~1"=="" goto done_args
@@ -68,6 +72,8 @@ if "%~1"=="--port" set "PORT=%~2" & shift & shift & goto parse_args
if "%~1"=="-b" set "BAUD=%~2" & shift & shift & goto parse_args if "%~1"=="-b" set "BAUD=%~2" & shift & shift & goto parse_args
if "%~1"=="--baud" set "BAUD=%~2" & shift & shift & goto parse_args if "%~1"=="--baud" set "BAUD=%~2" & shift & shift & goto parse_args
if "%~1"=="--board" set "BOARD_NAME=%~2" & shift & shift & goto parse_args if "%~1"=="--board" set "BOARD_NAME=%~2" & shift & shift & goto parse_args
if "%~1"=="--timestamps" set "DO_TIMESTAMPS=1" & shift & goto parse_args
if "%~1"=="--log" set "LOG_FILE=%~2" & shift & shift & goto parse_args
if "%~1"=="--help" goto show_help if "%~1"=="--help" goto show_help
if "%~1"=="-h" goto show_help if "%~1"=="-h" goto show_help
echo FAIL: Unknown option: %~1 echo FAIL: Unknown option: %~1
@@ -75,8 +81,11 @@ exit /b 1
:show_help :show_help
echo Usage: monitor.bat [-p PORT] [-b BAUD] [--board NAME] echo Usage: monitor.bat [-p PORT] [-b BAUD] [--board NAME]
echo [--timestamps] [--log FILE]
echo Opens serial monitor. Baud rate from .anvil.toml. echo Opens serial monitor. Baud rate from .anvil.toml.
echo --board NAME selects a board from [boards.NAME]. echo --board NAME selects a board from [boards.NAME].
echo --timestamps prepend [HH:MM:SS.mmm] to each line.
echo --log FILE also write output to a file.
exit /b 0 exit /b 0
:done_args :done_args
@@ -164,6 +173,27 @@ if "%PORT%"=="" (
:: -- Monitor -------------------------------------------------------------- :: -- Monitor --------------------------------------------------------------
echo Opening serial monitor on %PORT% at %BAUD% baud... echo Opening serial monitor on %PORT% at %BAUD% baud...
if "%DO_TIMESTAMPS%"=="1" echo Timestamps enabled.
if not "%LOG_FILE%"=="" echo Logging to: %LOG_FILE%
echo Press Ctrl+C to exit. echo Press Ctrl+C to exit.
echo. echo.
if "%DO_TIMESTAMPS%"=="1" if not "%LOG_FILE%"=="" goto monitor_ts_log
if "%DO_TIMESTAMPS%"=="1" goto monitor_ts
if not "%LOG_FILE%"=="" goto monitor_log
goto monitor_plain
:monitor_ts_log
powershell -NoProfile -File "%SCRIPT_DIR%\_monitor_filter.ps1" -Port "%PORT%" -Baud "%BAUD%" -Timestamps -LogFile "%LOG_FILE%"
goto :eof
:monitor_ts
powershell -NoProfile -File "%SCRIPT_DIR%\_monitor_filter.ps1" -Port "%PORT%" -Baud "%BAUD%" -Timestamps
goto :eof
:monitor_log
powershell -NoProfile -File "%SCRIPT_DIR%\_monitor_filter.ps1" -Port "%PORT%" -Baud "%BAUD%" -LogFile "%LOG_FILE%"
goto :eof
:monitor_plain
arduino-cli monitor -p %PORT% -c "baudrate=%BAUD%" arduino-cli monitor -p %PORT% -c "baudrate=%BAUD%"

View File

@@ -10,6 +10,8 @@
# ./monitor.sh -b 9600 Override baud rate # ./monitor.sh -b 9600 Override baud rate
# ./monitor.sh --board mega Use baud from a named board # ./monitor.sh --board mega Use baud from a named board
# ./monitor.sh --watch Reconnect after reset/replug # ./monitor.sh --watch Reconnect after reset/replug
# ./monitor.sh --timestamps Prepend [HH:MM:SS.mmm] to each line
# ./monitor.sh --log session.log Also write output to a file
# #
# Prerequisites: arduino-cli in PATH # Prerequisites: arduino-cli in PATH
@@ -63,6 +65,8 @@ fi
PORT="" PORT=""
DO_WATCH=0 DO_WATCH=0
BOARD_NAME="" BOARD_NAME=""
DO_TIMESTAMPS=0
LOG_FILE=""
while [[ $# -gt 0 ]]; do while [[ $# -gt 0 ]]; do
case "$1" in case "$1" in
@@ -70,10 +74,15 @@ while [[ $# -gt 0 ]]; do
-b|--baud) BAUD="$2"; shift 2 ;; -b|--baud) BAUD="$2"; shift 2 ;;
--board) BOARD_NAME="$2"; shift 2 ;; --board) BOARD_NAME="$2"; shift 2 ;;
--watch) DO_WATCH=1; shift ;; --watch) DO_WATCH=1; shift ;;
--timestamps) DO_TIMESTAMPS=1; shift ;;
--log) LOG_FILE="$2"; shift 2 ;;
-h|--help) -h|--help)
echo "Usage: ./monitor.sh [-p PORT] [-b BAUD] [--board NAME] [--watch]" echo "Usage: ./monitor.sh [-p PORT] [-b BAUD] [--board NAME] [--watch]"
echo " [--timestamps] [--log FILE]"
echo " Opens serial monitor. Baud rate from .anvil.toml." echo " Opens serial monitor. Baud rate from .anvil.toml."
echo " --board NAME selects a board from [boards.NAME]." echo " --board NAME selects a board from [boards.NAME]."
echo " --timestamps prepend [HH:MM:SS.mmm] to each line."
echo " --log FILE also write output to a file."
exit 0 exit 0
;; ;;
*) die "Unknown option: $1" ;; *) die "Unknown option: $1" ;;
@@ -180,10 +189,33 @@ if [[ -z "$PORT" ]]; then
fi fi
fi fi
# -- Output filter ---------------------------------------------------------
# Pipes stdin through optional timestamping and file logging.
monitor_filter() {
if [[ $DO_TIMESTAMPS -eq 1 ]] && [[ -n "$LOG_FILE" ]]; then
while IFS= read -r line; do
local ts
ts="[$(date '+%H:%M:%S.%3N')]"
printf "%s %s\n" "$ts" "$line"
printf "%s %s\n" "$ts" "$line" >> "$LOG_FILE"
done
elif [[ $DO_TIMESTAMPS -eq 1 ]]; then
while IFS= read -r line; do
printf "[%s] %s\n" "$(date '+%H:%M:%S.%3N')" "$line"
done
elif [[ -n "$LOG_FILE" ]]; then
tee -a "$LOG_FILE"
else
cat
fi
}
# -- Watch mode ------------------------------------------------------------ # -- Watch mode ------------------------------------------------------------
if [[ $DO_WATCH -eq 1 ]]; then if [[ $DO_WATCH -eq 1 ]]; then
echo "${CYN}${BLD}Persistent monitor on ${PORT} at ${BAUD} baud${RST}" echo "${CYN}${BLD}Persistent monitor on ${PORT} at ${BAUD} baud${RST}"
echo "Reconnects after upload / reset / replug." echo "Reconnects after upload / reset / replug."
[[ $DO_TIMESTAMPS -eq 1 ]] && echo "Timestamps enabled."
[[ -n "$LOG_FILE" ]] && echo "Logging to: $LOG_FILE"
echo "Press Ctrl+C to exit." echo "Press Ctrl+C to exit."
echo "" echo ""
@@ -191,7 +223,8 @@ if [[ $DO_WATCH -eq 1 ]]; then
while true; do while true; do
if [[ -e "$PORT" ]]; then if [[ -e "$PORT" ]]; then
arduino-cli monitor -p "$PORT" -c "baudrate=$BAUD" 2>/dev/null || true arduino-cli monitor -p "$PORT" -c "baudrate=$BAUD" 2>/dev/null \
| monitor_filter || true
echo "${YLW}--- ${PORT} disconnected ---${RST}" echo "${YLW}--- ${PORT} disconnected ---${RST}"
else else
echo "${CYN}--- Waiting for ${PORT} ...${RST}" echo "${CYN}--- Waiting for ${PORT} ...${RST}"
@@ -205,7 +238,9 @@ if [[ $DO_WATCH -eq 1 ]]; then
done done
else else
echo "Opening serial monitor on $PORT at $BAUD baud..." echo "Opening serial monitor on $PORT at $BAUD baud..."
[[ $DO_TIMESTAMPS -eq 1 ]] && echo "Timestamps enabled."
[[ -n "$LOG_FILE" ]] && echo "Logging to: $LOG_FILE"
echo "Press Ctrl+C to exit." echo "Press Ctrl+C to exit."
echo "" echo ""
arduino-cli monitor -p "$PORT" -c "baudrate=$BAUD" arduino-cli monitor -p "$PORT" -c "baudrate=$BAUD" | monitor_filter
fi fi

View File

@@ -831,6 +831,7 @@ fn test_full_project_structure() {
"monitor.sh", "monitor.sh",
"monitor.bat", "monitor.bat",
"_detect_port.ps1", "_detect_port.ps1",
"_monitor_filter.ps1",
"test/CMakeLists.txt", "test/CMakeLists.txt",
"test/test_unit.cpp", "test/test_unit.cpp",
"test/run_tests.sh", "test/run_tests.sh",
@@ -1414,6 +1415,7 @@ fn test_refresh_freshly_extracted_is_up_to_date() {
"upload.sh", "upload.bat", "upload.sh", "upload.bat",
"monitor.sh", "monitor.bat", "monitor.sh", "monitor.bat",
"_detect_port.ps1", "_detect_port.ps1",
"_monitor_filter.ps1",
"test/run_tests.sh", "test/run_tests.bat", "test/run_tests.sh", "test/run_tests.bat",
]; ];
@@ -1478,6 +1480,7 @@ fn test_refresh_does_not_list_user_files() {
"upload.sh", "upload.bat", "upload.sh", "upload.bat",
"monitor.sh", "monitor.bat", "monitor.sh", "monitor.bat",
"_detect_port.ps1", "_detect_port.ps1",
"_monitor_filter.ps1",
"test/run_tests.sh", "test/run_tests.bat", "test/run_tests.sh", "test/run_tests.bat",
]; ];
@@ -1866,3 +1869,105 @@ fn test_script_errors_mention_toml_section_syntax() {
); );
} }
} }
// ==========================================================================
// Monitor: --timestamps and --log flags
// ==========================================================================
#[test]
fn test_monitor_scripts_accept_timestamps_flag() {
let tmp = TempDir::new().unwrap();
let ctx = TemplateContext {
project_name: "ts_test".to_string(),
anvil_version: "1.0.0".to_string(),
board_name: "uno".to_string(),
fqbn: "arduino:avr:uno".to_string(),
baud: 115200,
};
TemplateManager::extract("basic", tmp.path(), &ctx).unwrap();
for script in &["monitor.sh", "monitor.bat"] {
let content = fs::read_to_string(tmp.path().join(script)).unwrap();
assert!(
content.contains("--timestamps"),
"{} should accept --timestamps flag",
script
);
}
}
#[test]
fn test_monitor_scripts_accept_log_flag() {
let tmp = TempDir::new().unwrap();
let ctx = TemplateContext {
project_name: "log_test".to_string(),
anvil_version: "1.0.0".to_string(),
board_name: "uno".to_string(),
fqbn: "arduino:avr:uno".to_string(),
baud: 115200,
};
TemplateManager::extract("basic", tmp.path(), &ctx).unwrap();
for script in &["monitor.sh", "monitor.bat"] {
let content = fs::read_to_string(tmp.path().join(script)).unwrap();
assert!(
content.contains("--log"),
"{} should accept --log flag for file output",
script
);
}
}
#[test]
fn test_monitor_sh_has_timestamp_format() {
// The timestamp format should include hours, minutes, seconds, and millis
let tmp = TempDir::new().unwrap();
let ctx = TemplateContext {
project_name: "ts_fmt".to_string(),
anvil_version: "1.0.0".to_string(),
board_name: "uno".to_string(),
fqbn: "arduino:avr:uno".to_string(),
baud: 115200,
};
TemplateManager::extract("basic", tmp.path(), &ctx).unwrap();
let content = fs::read_to_string(tmp.path().join("monitor.sh")).unwrap();
assert!(
content.contains("%H:%M:%S"),
"monitor.sh should use HH:MM:SS timestamp format"
);
assert!(
content.contains("%3N"),
"monitor.sh should include milliseconds in timestamps"
);
}
#[test]
fn test_monitor_sh_timestamps_work_in_watch_mode() {
// The timestamp filter should also apply in --watch mode
let tmp = TempDir::new().unwrap();
let ctx = TemplateContext {
project_name: "watch_ts".to_string(),
anvil_version: "1.0.0".to_string(),
board_name: "uno".to_string(),
fqbn: "arduino:avr:uno".to_string(),
baud: 115200,
};
TemplateManager::extract("basic", tmp.path(), &ctx).unwrap();
let content = fs::read_to_string(tmp.path().join("monitor.sh")).unwrap();
// The filter function should be called in the watch loop
assert!(
content.contains("monitor_filter"),
"monitor.sh should use a filter function for timestamps"
);
// Count usages of monitor_filter - should appear in both watch and non-watch
let filter_count = content.matches("monitor_filter").count();
assert!(
filter_count >= 3,
"monitor_filter should be defined and used in both watch and normal mode (found {} refs)",
filter_count
);
}