Skip to main content

How to Monitor Application Logs and Alert on Errors from a Batch Script

Monitoring application logs in real time is one of the most effective ways to detect problems before they escalate. A Batch Script that watches log files for error patterns can trigger alerts, send notifications, and log incidents, providing a lightweight monitoring solution without requiring third-party tools. This approach is particularly valuable for legacy applications, small environments, and systems where installing dedicated monitoring agents is not practical.

In this guide, we will explore how to monitor application logs and alert on errors from a Batch Script, covering pattern matching, real-time tailing, email notifications, and structured alert logging.

Method 1: Scanning a Log File for Errors

The simplest approach scans a log file for known error patterns:

@echo off
setlocal enabledelayedexpansion

set "logfile=C:\Apps\MyApp\logs\application.log"
set "patterns=ERROR CRITICAL FATAL Exception"

:: Generate locale-safe timestamp for the alert log filename
for /f "tokens=2 delims==" %%T in ('wmic os get LocalDateTime /value') do set "dt=%%T"
set "alert_log=alerts_%dt:~0,4%%dt:~4,2%%dt:~6,2%.txt"

if not exist "%logfile%" (
echo [ERROR] Log file not found: %logfile%
pause
exit /b 1
)

echo =============================================
echo LOG SCANNER
echo File: %logfile%
echo Patterns: %patterns%
echo =============================================
echo.

set "found=0"

for %%P in (%patterns%) do (
for /f "tokens=*" %%L in ('findstr /i /n "%%P" "%logfile%" 2^>nul') do (
set /a found+=1
echo [%%P] %%L
echo [%dt:~0,4%-%dt:~4,2%-%dt:~6,2% %dt:~8,2%:%dt:~10,2%:%dt:~12,2%] %%L>> "!alert_log!"
)
)

echo.
if !found! gtr 0 (
echo [ALERT] Found !found! error(s^). Details in: !alert_log!
) else (
echo [OK] No errors found.
)

pause

Example of application.log:

2026-04-09 07:00:01 INFO Starting application
2026-04-09 07:01:15 INFO Loading configuration
2026-04-09 07:02:30 WARNING Low memory detected
2026-04-09 07:03:45 ERROR Failed to connect to database
2026-04-09 07:04:10 INFO Retrying connection
2026-04-09 07:05:25 CRITICAL Application crashed due to unhandled exception
2026-04-09 07:06:00 INFO Attempting automatic restart
2026-04-09 07:07:12 FATAL Could not recover from error
2026-04-09 07:08:30 INFO Shutdown complete

Example of corresponding alerts_20260409_221455.txt:

[2026-04-09 22:14:55] 2026-04-09 07:03:45 ERROR Failed to connect to database
[2026-04-09 22:14:55] 2026-04-09 07:05:25 CRITICAL Application crashed due to unhandled exception
[2026-04-09 22:14:55] 2026-04-09 07:07:12 FATAL Could not recover from error

Method 2: Real-Time Log Monitoring (Tail)

Continuously watch a log file for new error entries:

@echo off
title Log Monitor
setlocal enabledelayedexpansion

:: ================= CONFIGURATION =================
set "logfile=C:\Apps\MyApp\logs\application.log"
set "check_interval=5"
set "error_patterns=ERROR,CRITICAL,FATAL,OutOfMemory,StackOverflow"
set "alert_log=C:\Monitoring\alerts.log"
:: =================================================

:: Create alert log directory if needed
for %%D in ("%alert_log%") do (
if not exist "%%~dpD" mkdir "%%~dpD"
)

echo =============================================
echo REAL-TIME LOG MONITOR
echo Watching: %logfile%
echo Interval: %check_interval% seconds
echo Alerts: %alert_log%
echo Press Ctrl+C to stop
echo =============================================
echo.

:: Initial position: Start at the end of the file to avoid processing old logs
set "last_size=0"
if exist "%logfile%" (
for %%F in ("%logfile%") do set "last_size=%%~zF"
echo [INFO] Started monitoring from end of file (Byte: !last_size!^)
) else (
echo [WARN] Log file not found. Waiting for it to be created...
)

:monitor
timeout /t %check_interval% /nobreak >nul

:: Check if file exists
if not exist "%logfile%" goto monitor

:: Get current file size
for %%F in ("%logfile%") do set "current_size=%%~zF"

:: HANDLE FILE ROTATION: If file is smaller than before, it was reset/rotated
if !current_size! LSS !last_size! (
echo [INFO] Log file rotated or cleared. Resetting pointer...
set "last_size=0"
)

:: Only process if the file has grown
if !current_size! LEQ !last_size! goto monitor

:: Read new content and check for errors using PowerShell
:: We pass variables as arguments to avoid quoting hell inside the PS command
powershell -NoProfile -ExecutionPolicy Bypass -Command ^
"$logPath = '%logfile%'; $alertPath = '%alert_log%'; $pos = %last_size%; $patterns = '%error_patterns%' -split ',';" ^
"$stream = [System.IO.File]::Open($logPath, 'Open', 'Read', 'ReadWrite');" ^
"try {" ^
" $stream.Position = $pos;" ^
" $reader = New-Object System.IO.StreamReader($stream);" ^
" $content = $reader.ReadToEnd();" ^
" $lines = $content -split '\r?\n';" ^
" foreach ($line in $lines) {" ^
" if ([string]::IsNullOrWhiteSpace($line)) { continue };" ^
" foreach ($p in $patterns) {" ^
" if ($line -match $p) {" ^
" Write-Host ('[ALERT] ' + $line) -ForegroundColor Red;" ^
" $timestamp = Get-Date -Format 'yyyy-MM-dd HH:mm:ss';" ^
" Add-Content -Path $alertPath -Value (\"$timestamp $line\");" ^
" break" ^
" }" ^
" }" ^
" }" ^
"} finally { $stream.Close() }"

set "last_size=!current_size!"
goto monitor

Method 3: Log Monitor with Email Alerts

Send email notifications when critical errors are detected:

@echo off
setlocal enabledelayedexpansion

set "logfile=C:\Apps\MyApp\logs\application.log"
set "smtp_server=smtp.company.com"
set "from=alerts@company.com"
set "to=ops-team@company.com"
set "tail_lines=100"

if not exist "%logfile%" (
echo [ERROR] Log file not found: %logfile%
exit /b 1
)

echo Scanning last %tail_lines% lines for critical errors...

:: Find recent errors
set "error_file=%temp%\error_report_%random%.txt"

for /f "tokens=2 delims==" %%T in ('wmic os get LocalDateTime /value') do set "dt=%%T"
set "report_time=%dt:~0,4%-%dt:~4,2%-%dt:~6,2% %dt:~8,2%:%dt:~10,2%:%dt:~12,2%"

(
echo Error Report - !report_time!
echo Server: %COMPUTERNAME%
echo Log: %logfile%
echo ================================
) > "%error_file%"

powershell -NoProfile -Command ^
"$lines = Get-Content '%logfile%' -Tail %tail_lines%;" ^
"$errors = @($lines | Where-Object { $_ -match 'ERROR|CRITICAL|FATAL|Exception' });" ^
"if ($errors.Count -gt 0) {" ^
" $errors | ForEach-Object { Add-Content '%error_file%' $_ };" ^
" Write-Host ('Found ' + $errors.Count + ' error(s).');" ^
" exit 1" ^
"} else { exit 0 }"

if !errorlevel!==1 (
echo [ALERT] Errors found. Sending notification...

powershell -NoProfile -Command ^
"try {" ^
" Send-MailMessage " ^
" -From '%from%' " ^
" -To '%to%' " ^
" -Subject '[ALERT] Errors detected on %COMPUTERNAME%' " ^
" -Body (Get-Content '%error_file%' -Raw) " ^
" -SmtpServer '%smtp_server%' " ^
" -ErrorAction Stop;" ^
" Write-Host '[OK] Alert sent to %to%.'; exit 0" ^
"} catch { Write-Host ('[FAIL] Email failed: ' + $_.Exception.Message); exit 1 }"

if !errorlevel! neq 0 (
echo [WARNING] Could not send email. Report saved: %error_file%
) else (
del "%error_file%" 2>nul
)
) else (
echo [OK] No errors in last %tail_lines% log entries.
del "%error_file%" 2>nul
)

pause

Method 4: Multi-Log Monitor Dashboard

Monitor multiple log files simultaneously:

@echo off
title Multi-Log Monitor
setlocal enabledelayedexpansion

:: Define log files to monitor
set "log_count=3"
set "log[0]=C:\Apps\WebApp\logs\app.log"
set "log[1]=C:\Apps\API\logs\api.log"
set "log[2]=C:\Apps\Worker\logs\worker.log"

set "check_interval=10"
set "tail_lines=50"

:dashboard
cls
echo =============================================
echo MULTI-LOG MONITOR
echo %date% %time:~0,8%
echo =============================================
echo.

set /a last=log_count - 1
for /L %%I in (0,1,!last!) do (
set "lf=!log[%%I]!"

if exist "!lf!" (
:: Count recent errors and get last modification time in one call
for /f "tokens=1,2 delims=|" %%A in ('powershell -NoProfile -Command ^
"$f = Get-Item '!lf!';" ^
"$errs = @(Get-Content '!lf!' -Tail %tail_lines% -ErrorAction SilentlyContinue | Where-Object { $_ -match 'ERROR|CRITICAL' }).Count;" ^
"Write-Host ($errs.ToString() + '|' + $f.LastWriteTime.ToString('HH:mm:ss'))"') do (
set "err_count=%%A"
set "last_mod=%%B"
)

if !err_count! gtr 0 (
echo [!!] !lf!
echo Errors: !err_count! in last %tail_lines% lines Updated: !last_mod!
) else (
echo [OK] !lf!
echo No errors Updated: !last_mod!
)
) else (
echo [??] !lf! (not found^)
)
echo.
)

echo Refreshing in %check_interval% seconds. Press Ctrl+C to stop.
timeout /t %check_interval% /nobreak >nul
goto dashboard

Method 5: Scheduled Log Scan with Report

For use with Windows Task Scheduler to run periodic checks:

@echo off
setlocal enabledelayedexpansion

set "logfile=C:\Apps\MyApp\logs\application.log"
set "report_dir=C:\Monitoring\Reports"
set "tail_lines=500"

if not exist "%logfile%" (
echo [ERROR] Log file not found: %logfile%
exit /b 1
)

if not exist "%report_dir%" mkdir "%report_dir%"

for /f "tokens=2 delims==" %%T in ('wmic os get LocalDateTime /value') do set "dt=%%T"
set "timestamp=%dt:~0,4%%dt:~4,2%%dt:~6,2%_%dt:~8,2%%dt:~10,2%"
set "report_time=%dt:~0,4%-%dt:~4,2%-%dt:~6,2% %dt:~8,2%:%dt:~10,2%:%dt:~12,2%"
set "report=%report_dir%\log_report_%timestamp%.txt"

(
echo =============================================
echo LOG ANALYSIS REPORT
echo Generated: !report_time!
echo Server: %COMPUTERNAME%
echo Log: %logfile%
echo Scope: Last %tail_lines% lines
echo =============================================
echo.
) > "%report%"

:: Analyze recent log entries
powershell -NoProfile -Command ^
"$lines = Get-Content '%logfile%' -Tail %tail_lines%;" ^
"$errors = @($lines | Where-Object { $_ -match 'ERROR|CRITICAL|FATAL' });" ^
"$warnings = @($lines | Where-Object { $_ -match 'WARN' });" ^
"Add-Content '%report%' ('Errors: ' + $errors.Count);" ^
"Add-Content '%report%' ('Warnings: ' + $warnings.Count);" ^
"Add-Content '%report%' '';" ^
"if ($errors.Count -gt 0) {" ^
" Add-Content '%report%' 'ERROR DETAILS:';" ^
" Add-Content '%report%' '=============';" ^
" foreach ($e in $errors) { Add-Content '%report%' $e };" ^
" exit 1" ^
"} else { exit 0 }"

set "has_errors=!errorlevel!"

if !has_errors!==1 (
echo [ALERT] Errors detected. Report: %report%
) else (
echo [OK] No errors. Report: %report%
)

exit /b !has_errors!

Pattern-Based Alert Rules

Define different alert levels based on log patterns:

@echo off
setlocal enabledelayedexpansion

set "logfile=C:\Apps\MyApp\logs\application.log"

if not exist "%logfile%" (
echo [ERROR] Log file not found: %logfile%
exit /b 1
)

:: Define severity rules
set "critical=OutOfMemoryException StackOverflowException FATAL"
set "error=ERROR NullReferenceException SqlException"
set "warning=WARN timeout retry"

echo Analyzing log patterns...
echo.

set "crit_count=0"
set "err_count=0"
set "warn_count=0"

:: Count by severity
for %%P in (%critical%) do (
for /f %%C in ('findstr /i /c:"%%P" "%logfile%" 2^>nul ^| find /c /v ""') do set /a crit_count+=%%C
)

for %%P in (%error%) do (
for /f %%C in ('findstr /i /c:"%%P" "%logfile%" 2^>nul ^| find /c /v ""') do set /a err_count+=%%C
)

for %%P in (%warning%) do (
for /f %%C in ('findstr /i /c:"%%P" "%logfile%" 2^>nul ^| find /c /v ""') do set /a warn_count+=%%C
)

echo Critical: !crit_count!
echo Errors: !err_count!
echo Warnings: !warn_count!
echo.

if !crit_count! gtr 0 (
echo [CRITICAL] Immediate attention required!
exit /b 2
)
if !err_count! gtr 0 (
echo [ERROR] Errors detected. Investigate.
exit /b 1
)
echo [OK] No significant issues.

pause

Common Mistakes

The Wrong Way: Scanning the Entire Log Every Time

:: WRONG - Re-scans the entire file on every check
findstr /i "ERROR" application.log
:: For a 500MB log file, this takes minutes and finds old errors repeatedly

Output Concern: Scanning the entire log file on every check is slow for large files and reports old, already-known errors. Track the file position or use Get-Content -Tail N to only check recent entries.

The Wrong Way: No Context Around Errors

:: INCOMPLETE - Shows the error line but no surrounding context
findstr "ERROR" app.log
:: Output: "ERROR: Operation failed" with no timestamp or stack trace

Isolated error lines often lack the context needed for diagnosis. Capture surrounding lines or ensure the log format includes timestamps, request IDs, and stack traces.

Best Practices

  1. Monitor incrementally: Track file position and only scan new content to avoid re-alerting on old errors.
  2. Define severity levels: Distinguish between critical, error, and warning patterns for appropriate responses.
  3. Include context: Capture timestamps, surrounding lines, and error counts in alerts.
  4. Rate-limit alerts: Avoid flooding inboxes by aggregating multiple errors into a single periodic report.
  5. Schedule with Task Scheduler: Run log scans periodically for consistent monitoring coverage.

Conclusion

Monitoring application logs and alerting on errors from a Batch Script provides a lightweight, dependency-free monitoring solution. By scanning for error patterns with findstr, tailing log files for real-time detection, sending email alerts for critical issues, and generating periodic analysis reports, administrators build effective log monitoring without third-party tools. The key principles are incremental scanning (only checking new content), severity-based pattern matching, and including sufficient context for diagnosis.