Skip to content

How to Monitor Packet Loss Over Time in Batch Script

Learn how to create a Batch script that detects and logs packet loss percentages over time, helping identify network instability for ISP troubleshooting and performance optimization.

Packet loss is the ultimate "silent killer" of network performance. Unlike a total outage, packet loss causes stuttering Zoom calls, slow file transfers, and frequent web page timeouts. Detecting a single failed ping isn't enough; you need to know if your network is losing 1% or 20% of its data over several hours. A Batch script can perform a "stress test" by sending hundreds of pings and capturing the final summary statistics provided by the ping command, allowing you to prove network instability to your provider.

This guide will explain how to monitor and log packet loss percentages.

Method 1: The "Bulk Statistics" Scan

This method sends 100 pings to a host and captures the final summary statistics from the ping output.

@echo off
setlocal enabledelayedexpansion

set "Target=8.8.8.8"
set "Count=100"
set "LogFile=%USERPROFILE%\packet_loss_audit.csv"
set "TempFile=%TEMP%\ping_result.tmp"

echo [TEST] Monitoring %Target% for Packet Loss (%Count% packets)...
echo         This will take approximately %Count% seconds.
echo.

:: Run the full ping and capture output to a temp file
ping -n %Count% %Target% > "%TempFile%" 2>&1

:: Extract the summary line containing loss statistics
:: Format varies by locale, so we parse multiple patterns
set "sent="
set "received="
set "lost="
set "pct_lost="

:: Try parsing English format: "Sent = X, Received = X, Lost = X (Y% loss)"
for /f "tokens=*" %%L in ('findstr /i "Lost" "%TempFile%" 2^>nul') do (
    set "summaryLine=%%L"
)

:: Extract individual values using PowerShell for reliable parsing
:: This handles all locale formats
for /f "delims=" %%p in ('powershell -NoProfile -Command ^
  "$lines = Get-Content '%TempFile%';"^
  "$stats = $lines | Where-Object { $_ -match '\d+%%' } | Select-Object -Last 1;"^
  "if ($stats -match '(\d+).*?(\d+).*?(\d+).*?\((\d+)%%') {"^
  "  Write-Output (\"$($Matches[1]),$($Matches[2]),$($Matches[3]),$($Matches[4])\")"^
  "} else { Write-Output 'ERROR' }"') do set "parsed=%%p"

if "!parsed!"=="ERROR" (
    echo [ERROR] Could not parse ping statistics. Raw output:
    type "%TempFile%"
    del "%TempFile%" >nul 2>&1
    pause
    endlocal
    exit /b 1
)

:: Split parsed values
for /f "tokens=1-4 delims=," %%a in ("!parsed!") do (
    set "sent=%%a"
    set "received=%%b"
    set "lost=%%c"
    set "pct_lost=%%d"
)

echo ------------------------------------------
echo    PACKET LOSS REPORT: !date! !time!
echo ------------------------------------------
echo    Target:       %Target%
echo    Packets Sent: !sent!
echo    Received:     !received!
echo    Lost:         !lost! (!pct_lost!%%)
echo ------------------------------------------

:: Interpret the result
if !pct_lost! equ 0 (
    echo    [OK] No packet loss detected.
) else if !pct_lost! leq 2 (
    echo    [INFO] Minor packet loss - generally acceptable.
) else if !pct_lost! leq 10 (
    echo    [WARNING] Moderate packet loss - may affect calls and streaming.
) else (
    echo    [CRITICAL] Severe packet loss - network is unstable!
)

echo.

:: Log to CSV
if not exist "%LogFile%" echo Date,Time,Target,Sent,Received,Lost,LossPercent >> "%LogFile%"
echo !date!,!time!,%Target%,!sent!,!received!,!lost!,!pct_lost!%% >> "%LogFile%"
echo [LOGGED] Results appended to: %LogFile%

:: Clean up
del "%TempFile%" >nul 2>&1

pause
endlocal

Method 3: Continuous Packet Loss Dashboard

A persistent monitor that runs a batch of pings at regular intervals and displays a rolling summary.

@echo off
setlocal enabledelayedexpansion

set "Host=8.8.8.8"
set "BatchSize=20"
set "Interval=60"
set "LogFile=%USERPROFILE%\packetloss_dashboard.csv"
set "Round=0"

echo [DASHBOARD] Continuous Packet Loss Monitor
echo           Target:     %Host%
echo           Batch Size: %BatchSize% pings per round
echo           Interval:   %Interval% seconds between rounds
echo           Press CTRL+C to stop.
echo.

:: CSV header - Removed the % from the header to keep it as a numeric column for Excel
if not exist "%LogFile%" echo Date,Time,Target,Sent,LossPercent >> "%LogFile%"

:Loop
set /a Round+=1
set "pct=UNKNOWN"

echo ------------------------------------------
echo  Round !Round! - %time%

:: IMPROVED PowerShell logic:
:: 1. Runs ping
:: 2. Finds the line containing '%'
:: 3. Uses a Regular Expression to grab only the digits immediately preceding the '%' sign
for /f %%p in ('powershell -NoProfile -Command ^
  "$r = ping -n %BatchSize% -w 1000 %Host%;"^
  "$line = $r | Where-Object { $_ -match '\d+%%' } | Select-Object -Last 1;"^
  "if ($line -match '(\d+)%%') { $Matches[1] } else { 'ERROR' }"') do set "pct=%%p"

if "!pct!"=="ERROR" (
    echo  [ERROR] Could not parse ping statistics. Target might be unreachable.
) else (
    :: Display with severity
    if !pct! equ 0 (
        echo  [OK]       Loss: !pct!%%
    ) else if !pct! leq 5 (
        echo  [WARNING]  Loss: !pct!%%
    ) else (
        echo  [CRITICAL] Loss: !pct!%%
    )

    :: Log to CSV (Stored as raw number for easy graphing in Excel/Sheets)
    echo %date%,%time%,%Host%,%BatchSize%,!pct! >> "%LogFile%"
)

echo  Next round in %Interval% seconds...
echo ------------------------------------------
echo.

timeout /t %Interval% >nul
goto :Loop

Method 4: Dual-Target Loss Comparison (Gateway vs. Internet)

To pinpoint whether packet loss is on your local network or your ISP's infrastructure.

@echo off
setlocal enabledelayedexpansion

set "Gateway=192.168.1.1"
set "Internet=8.8.8.8"
set "Count=50"
set "LogFile=%USERPROFILE%\loss_comparison.csv"

echo [AUDIT] Dual-Target Packet Loss Comparison
echo         Gateway:  %Gateway%
echo         Internet: %Internet%
echo         Packets:  %Count% each
echo.

:: CSV header
if not exist "%LogFile%" echo Date,Time,Gateway,GW_Loss,Internet,INET_Loss,Diagnosis >> "%LogFile%"

:: Test Gateway
echo [1/2] Pinging gateway %Gateway%...
set "gw_loss=ERROR"
for /f %%p in ('powershell -NoProfile -Command ^
  "$r = ping -n %Count% -w 1000 %Gateway%;"^
  "$line = $r | Where-Object { $_ -match '\d+%%' } | Select-Object -Last 1;"^
  "if ($line -match '\((\d+)%%') { $Matches[1] } else { 'ERROR' }"') do set "gw_loss=%%p"

:: Test Internet
echo [2/2] Pinging internet %Internet%...
set "inet_loss=ERROR"
for /f %%p in ('powershell -NoProfile -Command ^
  "$r = ping -n %Count% -w 1000 %Internet%;"^
  "$line = $r | Where-Object { $_ -match '\d+%%' } | Select-Object -Last 1;"^
  "if ($line -match '\((\d+)%%') { $Matches[1] } else { 'ERROR' }"') do set "inet_loss=%%p"

echo.
echo ==========================================
echo   PACKET LOSS COMPARISON REPORT
echo   %date% %time%
echo ==========================================
echo.

:: Display results
if "!gw_loss!"=="ERROR" (
    echo   Gateway  (%Gateway%):  COULD NOT TEST
) else (
    echo   Gateway  (%Gateway%):  !gw_loss!%% loss
)

if "!inet_loss!"=="ERROR" (
    echo   Internet (%Internet%): COULD NOT TEST
) else (
    echo   Internet (%Internet%): !inet_loss!%% loss
)

echo.

:: Diagnosis
set "diagnosis=UNKNOWN"
if "!gw_loss!" neq "ERROR" if "!inet_loss!" neq "ERROR" (
    if !gw_loss! leq 1 if !inet_loss! leq 1 (
        set "diagnosis=ALL_CLEAR"
        echo   [OK] Network is healthy - minimal loss on both targets.
    )
    if !gw_loss! leq 1 if !inet_loss! gtr 5 (
        set "diagnosis=ISP_PROBLEM"
        echo   [ALERT] Loss is on ISP side - local network is fine.
        echo           Contact your Internet Service Provider.
    )
    if !gw_loss! gtr 5 if !inet_loss! gtr 5 (
        set "diagnosis=LOCAL_PROBLEM"
        echo   [ALERT] Loss starts at gateway - local network issue.
        echo           Check cables, switch, and router.
    )
    if !gw_loss! gtr 5 if !inet_loss! leq 1 (
        set "diagnosis=GATEWAY_ANOMALY"
        echo   [INFO] Gateway has loss but internet is fine - unusual.
        echo          Gateway may be deprioritizing ICMP responses.
    )
)

echo.
echo ==========================================

:: Log
echo !date!,!time!,%Gateway%,!gw_loss!%%,%Internet%,!inet_loss!%%,!diagnosis! >> "%LogFile%"
echo [LOGGED] Results saved to: %LogFile%

pause
endlocal

How to read the diagnosis:

  • ALL_CLEAR: Less than 1% loss everywhere. Network is healthy.
  • ISP_PROBLEM: Local gateway is fine but internet target has loss. The issue is upstream with your ISP.
  • LOCAL_PROBLEM: Both targets have loss, starting at the gateway. Check your cables, switch, and router.
  • GATEWAY_ANOMALY: Gateway shows loss but internet is fine. Some routers deprioritize ICMP responses under load, this may be a false positive.

How to Avoid Common Errors

Wrong Way: Using Only 4 Packets

A standard ping (4 packets) is too small a sample size. You can lose 1 packet and it shows as "25% loss," which might just be a single random collision.

Correct Way: Use at least 50 to 100 packets for a valid statistical audit. This provides enough data to distinguish between a "glitch" and a "trend."

Wrong Way: Parsing Ping Output with Fixed Token Positions

The ping summary format Lost = X (Y% loss) varies between Windows languages. Hardcoding tokens=4,7 delims=(, works in English but breaks completely on German, French, Spanish, and other localized systems.

Correct Way: Use a regex-based approach (via PowerShell) that matches the numeric pattern (\d+%) regardless of surrounding text:

powershell -NoProfile -Command ^
  "$line = ping -n 100 8.8.8.8 | Where-Object { $_ -match '\d+%%' } | Select-Object -Last 1;"^
  "if ($line -match '\((\d+)%%') { $Matches[1] }"

Wrong Way: Using cls in a Monitoring Loop

using cls to clear the screen every cycle will destroy all previous output: you can never scroll back to see what happened 10 minutes ago.

Correct Way: Append new results below existing output. Use separators between rounds for readability.

Wrong Way: Missing enabledelayedexpansion in Loops

Variables set inside a goto :Loop construct or FOR block need delayed expansion. Without it, !ms! and !pct_lost! show stale values from before the loop began.

Correct Way: Enable delayed expansion and use !variable! syntax:

setlocal enabledelayedexpansion

Problem: Parsing Percentage in Different Languages

The ping output (0% loss) is different in other languages (e.g., German: (0% Verlust), French: (perte 0%)).

Solution: Use the PowerShell regex approach shown in Methods 1, 3, and 4, which matches the numeric percentage pattern regardless of locale.

Best Practices and Rules

1. Identify the Location of Loss

To pinpoint where loss is happening, test two targets simultaneously (Method 4):

  1. Your local gateway (192.168.1.1)
  2. A public DNS (8.8.8.8)

If the gateway has 0% loss but the public target has 10% loss, the problem is your ISP's infrastructure.

2. Time of Day Matters

Network congestion usually peaks in evenings. Run your script for 24 hours to see if packet loss correlates with high-traffic hours. Method 3's continuous dashboard is designed for this.

3. Log as CSV

Format your log as CSV so you can share it with your ISP support team. Having a spreadsheet that shows "12% loss between 7 PM and 10 PM" is far more effective than saying "the internet is slow."

4. Packet Loss Severity Guide

Loss Percentage Severity Impact
0% Normal No issues
1–2% Minor Barely noticeable for browsing
3–5% Moderate VoIP calls may stutter, video may buffer
5–10% Severe Significant degradation across all services
10%+ Critical Effectively unusable for real-time applications

5. Log File Location

Avoid using %CD% for log files, it depends on where the script is launched from. Use %USERPROFILE% or a dedicated log directory for reliable write access.

6. Always Use setlocal / endlocal

Without setlocal, every variable your script creates persists in the parent shell session, causing potential conflicts when running multiple scripts in sequence.

Conclusions

Monitoring packet loss over time provides the "smoking gun" needed to solve complex connectivity problems. By moving beyond simple "Up/Down" checks and analyzing the quality of the connection, you gain the data needed to optimize your network and hold service providers accountable. This proactive auditing ensures that your professional environment remains stable, clear, and ready for high-bandwidth tasks like VoIP and video conferencing.

Navigation