Windows Ping Logging: Continuous Network Monitoring with Timestamps (2026)
Windows ping logging gives you a timestamped trail of every ICMP echo exchange between your machine and a target host. When your VPN drops at 2 AM or a flaky WAN link kills a remote desktop session, that log is the first piece of evidence you reach for. This guide builds that evidence trail — from a single-line batch command all the way to PowerShell 7.5 structured CSV logging, log rotation, jitter baselining, alerting, and a clear handoff to enterprise tools when scripted monitoring is no longer enough.
What this covers:
– Core ICMP and TTL concepts every network troubleshooter needs
– Batch scripting and PowerShell 7.5 Test-Connection for continuous logging
– Timestamped CSV output with rotation and automatic alerting
– Jitter measurement and latency baselining
– Real-world flaky-WAN diagnosis walkthrough
– When to graduate from scripts to Zabbix, PRTG, or Datadog
Last Updated: June 2026
TL;DR
Run this PowerShell 7.5 one-liner to start logging ping results to a timestamped CSV immediately:
$target = "8.8.8.8"
while ($true) {
$r = Test-Connection -TargetName $target -Count 1 -TimeoutSeconds 2 -ErrorAction SilentlyContinue
[PSCustomObject]@{
Timestamp = (Get-Date -Format "yyyy-MM-dd HH:mm:ss")
Target = $target
RTT_ms = if ($r) { $r.Latency } else { $null }
Status = if ($r) { "OK" } else { "TIMEOUT" }
} | Export-Csv "C:\Logs\ping_log_$(Get-Date -Format 'yyyy-MM-dd').csv" -Append -NoTypeInformation
Start-Sleep -Seconds 5
}
For production use, scroll to the full logging + alerting script in the PowerShell section below.
Terminology Primer
Before diving into scripts, anchor these terms. They appear throughout every monitoring tool, log file, and vendor dashboard you will ever read.
RTT (Round-Trip Time): The elapsed milliseconds from sending an ICMP Echo Request to receiving the Echo Reply. High RTT means congestion, a slow link, or heavy CPU load on the target.
Packet loss: A sent echo request that receives no reply within the timeout window. Isolated loss (1–2%) is usually transient queue drops. Sustained loss above 5% signals a degraded link.
Jitter: The variance in RTT between successive pings. A stable link might average 12 ms with ±1 ms jitter. A jittery link averages 12 ms but swings from 4 ms to 40 ms — destructive for VoIP and real-time protocols even when mean latency looks acceptable.
TTL (Time to Live): An 8-bit integer in every IP packet header, decremented by one at each router hop. When TTL reaches zero the router discards the packet and sends an ICMP Time Exceeded message back to the sender. Windows default TTL is 128; Linux is 64; Cisco IOS is 255.
ICMP (Internet Control Message Protocol): The layer-3 protocol used by ping. Type 8 = Echo Request; Type 0 = Echo Reply; Type 3 = Destination Unreachable; Type 11 = Time Exceeded. Defined in RFC 792 (https://www.rfc-editor.org/rfc/rfc792).
Baseline: A statistical model of normal behavior — mean RTT, standard deviation, expected loss rate — measured over days or weeks. Alerts that lack a baseline generate constant noise. Alerts anchored to a baseline catch real anomalies.
Why Continuous Ping Monitoring Exists
Network faults are rarely instantaneous. A WAN link degrades over minutes before it fails. A firewall rule blocks ICMP sporadically. A cloud VM’s CPU spikes during a backup job and inflates latency only on weekday mornings. A single ping at the moment you investigate tells you nothing about when the problem started.
Continuous Windows ping logging turns ephemeral network state into a durable, queryable record. That record answers four questions:
- When did it start? Correlate the first bad entry against deployment logs, maintenance windows, or weather events.
- How often does it happen? A link losing 0.1% of packets is background noise; losing 0.1% in 30-second bursts every four hours is a pattern.
- Is it getting worse? Trend analysis over days reveals slow hardware degradation before it becomes an outage.
- Is it my segment or upstream? Logging to a gateway, ISP router, and a public DNS server simultaneously isolates where the fault lives.
For IoT deployments — where edge devices transmit sensor telemetry over constrained WAN links — ping logs are often the only visibility tool before full observability stacks are deployed. The same ICMP logging patterns used here apply directly to monitoring OPC UA server reachability; see our OPC UA protocol deep dive for how edge connectivity feeds into digital twin data pipelines.
ICMP Fundamentals
How a Ping Works
When you run ping 8.8.8.8, Windows sends an IP datagram with:
– Protocol field = 1 (ICMP)
– ICMP Type = 8 (Echo Request)
– A sequence number and a timestamp payload
The target replies with ICMP Type 0 (Echo Reply), copying the sequence number and payload. Windows measures the elapsed time and reports RTT. If no reply arrives within the timeout (default 4 000 ms on Windows), the attempt is recorded as a timeout.
TTL in Practice
The TTL field exists to prevent routing loops from circulating packets forever. In diagnostic work it tells you how many hops your reply traversed. If you ping 8.8.8.8 and receive a reply with TTL=116, that means Google’s DNS server starts replies at 128 (Windows default) and 12 hops reduced it to 116.
Sudden TTL changes in a ping log indicate a routing change — your traffic is now taking a different path. That is useful context when troubleshooting latency spikes.
What Blocks ICMP
ICMP is filtered more aggressively than most administrators expect:
- Windows Defender Firewall blocks inbound ICMP Echo Request by default unless you enable the “File and Printer Sharing (Echo Request)” rule.
- Cloud security groups (AWS, Azure, GCP) often block ICMP unless explicitly permitted.
- Some ISP CDN edges rate-limit or deprioritize ICMP to prevent abuse.
- QoS policies on enterprise routers mark ICMP as best-effort and drop it first under congestion.
A host that stops replying to pings may be perfectly healthy. Cross-reference with TCP or UDP probes before concluding a host is down.
Batch Scripting Approach
CMD batch scripts have run on every Windows version since NT. They require no execution policy changes, no module installations, and run as scheduled tasks on Windows Server Core. They are the right tool when PowerShell is unavailable or when a quick one-file solution is all that is needed.
Basic Continuous Ping to File
@echo off
setlocal
set TARGET=8.8.8.8
set LOGFILE=C:\Logs\ping_basic_%DATE:~-4,4%-%DATE:~-7,2%-%DATE:~-10,2%.txt
:loop
for /f "tokens=1-3 delims=:." %%a in ("%TIME%") do (
set TIMESTAMP=%DATE% %%a:%%b:%%c
)
ping -n 1 -w 2000 %TARGET% | findstr /i "reply request bytes" >> %LOGFILE%
echo %TIMESTAMP% >> %LOGFILE%
timeout /t 5 /nobreak >nul
goto loop
This appends the raw ping output with a timestamp for each probe cycle. The findstr filter removes header lines. For analysis you will need to parse the RTT from text like Reply from 8.8.8.8: bytes=32 time=14ms TTL=116.
Structured Batch Output
A cleaner approach writes one delimited line per probe so tools like Excel or PowerShell can ingest it directly:
@echo off
setlocal enabledelayedexpansion
set TARGET=8.8.8.8
set LOGFILE=C:\Logs\ping_structured.csv
if not exist %LOGFILE% (
echo Timestamp,Target,RTT_ms,Status > %LOGFILE%
)
:loop
for /f "tokens=2 delims==" %%i in ('wmic os get LocalDateTime /value') do set DT=%%i
set TS=!DT:~0,4!-!DT:~4,2!-!DT:~6,2! !DT:~8,2!:!DT:~10,2!:!DT:~12,2!
for /f "tokens=5 delims== " %%r in ('ping -n 1 -w 2000 %TARGET% ^| findstr /i "time="') do (
set RTT=%%r
set RTT=!RTT:ms=!
echo !TS!,%TARGET%,!RTT!,OK >> %LOGFILE%
goto next
)
echo !TS!,%TARGET%,,TIMEOUT >> %LOGFILE%
:next
timeout /t 10 /nobreak >nul
goto loop
Limitations of batch scripts: No native alerting, no statistics, no jitter calculation, limited error handling. Once you need any of those, move to PowerShell.
PowerShell Structured Logging: Timestamps, CSV, and Alerting
PowerShell 7.5 (released January 2025, current stable as of mid-2026) ships Test-Connection with a significantly improved API compared to Windows PowerShell 5.1. In PS7, Test-Connection returns TestConnectionCommand+PingMttpReport objects — you can access .Latency directly rather than parsing strings.
Reference: Microsoft Learn — Test-Connection
Core Logging Function
#Requires -Version 7.2
<#
.SYNOPSIS
Continuous Windows ping logging with timestamped CSV output.
.DESCRIPTION
Pings a target at a configurable interval and appends results to a
daily rotating CSV. Triggers an email alert on sustained packet loss.
.NOTES
Tested: PowerShell 7.5.1 on Windows 11 24H2 / Windows Server 2025
#>
param(
[string]$Target = "8.8.8.8",
[int]$IntervalSeconds = 5,
[int]$TimeoutSeconds = 2,
[string]$LogDirectory = "C:\Logs\PingMonitor",
[int]$AlertThresholdPct = 20, # % loss in rolling window to trigger alert
[int]$AlertWindowCount = 10, # probes in rolling window
[string]$SmtpServer = "",
[string]$AlertFrom = "",
[string]$AlertTo = ""
)
# Ensure log directory exists
if (-not (Test-Path $LogDirectory)) {
New-Item -ItemType Directory -Path $LogDirectory -Force | Out-Null
}
$rollingWindow = [System.Collections.Generic.Queue[bool]]::new()
$lastAlertTime = [datetime]::MinValue
$alertCooldownMinutes = 15
function Get-LogPath {
Join-Path $LogDirectory "ping_log_$(Get-Date -Format 'yyyy-MM-dd').csv"
}
function Write-PingRecord {
param($timestamp, $target, $rtt, $status, $ttl)
[PSCustomObject]@{
Timestamp = $timestamp
Target = $target
RTT_ms = $rtt
TTL = $ttl
Status = $status
} | Export-Csv -Path (Get-LogPath) -Append -NoTypeInformation
}
function Send-LossAlert {
param([double]$LossPct)
if (-not $SmtpServer -or -not $AlertTo) { return }
$now = Get-Date
if (($now - $lastAlertTime).TotalMinutes -lt $alertCooldownMinutes) { return }
$script:lastAlertTime = $now
$body = "Ping monitor alert: $([math]::Round($LossPct,1))% loss to $Target in last $AlertWindowCount probes. $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')"
Send-MailMessage -SmtpServer $SmtpServer -From $AlertFrom -To $AlertTo `
-Subject "PING LOSS ALERT: $Target" -Body $body -ErrorAction SilentlyContinue
}
Write-Host "Starting ping monitor → $Target | Interval: ${IntervalSeconds}s | Log: $(Get-LogPath)"
while ($true) {
$ts = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
$result = Test-Connection -TargetName $Target -Count 1 `
-TimeoutSeconds $TimeoutSeconds -ErrorAction SilentlyContinue
if ($result -and $result.Status -eq "Success") {
$rtt = $result.Latency
$ttl = $result.Reply.Options.Ttl
$status = "OK"
$rollingWindow.Enqueue($true)
} else {
$rtt = $null
$ttl = $null
$status = "TIMEOUT"
$rollingWindow.Enqueue($false)
}
while ($rollingWindow.Count -gt $AlertWindowCount) {
$rollingWindow.Dequeue() | Out-Null
}
Write-PingRecord -timestamp $ts -target $Target -rtt $rtt -status $status -ttl $ttl
if ($rollingWindow.Count -eq $AlertWindowCount) {
$lossCount = ($rollingWindow | Where-Object { -not $_ }).Count
$lossPct = ($lossCount / $AlertWindowCount) * 100
if ($lossPct -ge $AlertThresholdPct) {
Send-LossAlert -LossPct $lossPct
}
}
$color = if ($status -eq "OK") { "Green" } else { "Red" }
Write-Host "[$ts] $Target RTT: $(if ($rtt) {"${rtt}ms"} else {'—'}) Status: $status" -ForegroundColor $color
Start-Sleep -Seconds $IntervalSeconds
}
Key PS7.5 notes:
– Test-Connection in PS7 uses the .NET Ping class and returns TestConnectionCommand+PingMttpReport. The .Latency property gives RTT in milliseconds as an integer.
– -TimeoutSeconds is the correct parameter (not -Count for timeout).
– $result.Reply.Options.Ttl gives the received TTL when the ping succeeds.
– Export-Csv -Append creates the file if it does not exist and appends if it does, with no duplicate header.
Log Rotation, Alerting, and Escalation
Long-running monitors produce large log files. Without rotation, a 5-second interval writes ~17 000 rows per day — roughly 2 MB of CSV. Over a month that is 60 MB in a single directory. Rotation keeps logs manageable and ensures old data is archived rather than lost.
Daily Log Rotation Script
#Requires -Version 7.2
<#
.SYNOPSIS
Rotate and compress ping log CSV files older than RetentionDays.
.NOTES
Schedule this as a daily Task Scheduler job at 00:05.
#>
param(
[string]$LogDirectory = "C:\Logs\PingMonitor",
[int]$RetentionDays = 7,
[string]$ArchivePath = "C:\Logs\PingMonitor\Archive"
)
if (-not (Test-Path $ArchivePath)) {
New-Item -ItemType Directory -Path $ArchivePath -Force | Out-Null
}
$cutoff = (Get-Date).AddDays(-$RetentionDays)
Get-ChildItem -Path $LogDirectory -Filter "ping_log_*.csv" |
Where-Object { $_.LastWriteTime -lt $cutoff } |
ForEach-Object {
$zipName = Join-Path $ArchivePath "$($_.BaseName).zip"
Compress-Archive -Path $_.FullName -DestinationPath $zipName -CompressionLevel Optimal -Force
Remove-Item $_.FullName -Force
Write-Host "Archived: $($_.Name) → $zipName"
}
Windows Event Log Integration
For environments where SMTP is locked down or where you need audit trails in a centralized SIEM, write loss events to the Windows Application event log:
# Register event source once (run as Administrator)
if (-not [System.Diagnostics.EventLog]::SourceExists("PingMonitor")) {
New-EventLog -LogName Application -Source "PingMonitor"
}
# In the monitoring loop, replace Send-LossAlert with:
function Write-LossEvent {
param([string]$Target, [double]$LossPct)
$msg = "Ping loss alert: $([math]::Round($LossPct,1))% loss to $Target. $(Get-Date -Format 'o')"
Write-EventLog -LogName Application -Source PingMonitor `
-EntryType Warning -EventId 1001 -Message $msg
}
Event ID 1001 can be forwarded to a SIEM via Windows Event Forwarding (WEF) or a Winlogbeat agent, making your simple ping script part of a real observability pipeline.
Escalation Stack
The diagram below shows how data flows from the ping script through to an enterprise monitoring platform.

Figure 1: The complete ping monitoring and escalation stack. The script writes to a daily rotating CSV, evaluates a rolling loss window, triggers SMTP or Event Log alerts on threshold breach, and feeds into enterprise dashboards via Zabbix/PRTG/Datadog agents when deployed.
Jitter Measurement and Baseline Detection
Mean RTT is a poor sole indicator of link health. A link averaging 14 ms RTT but swinging between 2 ms and 80 ms on consecutive probes will break VoIP, disrupt remote desktop sessions, and cause TCP retransmit storms — even though its average looks healthy.
Calculating Jitter in PowerShell
Jitter is conventionally measured as the mean absolute difference between consecutive RTT samples (the RFC 3550 method used in RTP/VoIP systems):
#Requires -Version 7.2
<#
.SYNOPSIS
Adds jitter and rolling statistics to the ping logging loop.
#>
param(
[string]$Target = "8.8.8.8",
[int]$IntervalSeconds = 5,
[int]$TimeoutSeconds = 2,
[string]$LogDirectory = "C:\Logs\PingMonitor",
[int]$BaselineWindowSize = 100 # probes to use for rolling baseline
)
if (-not (Test-Path $LogDirectory)) {
New-Item -ItemType Directory -Path $LogDirectory -Force | Out-Null
}
$rttHistory = [System.Collections.Generic.Queue[double]]::new()
$prevRtt = $null
$jitterHistory = [System.Collections.Generic.Queue[double]]::new()
function Get-LogPath {
Join-Path $LogDirectory "ping_jitter_$(Get-Date -Format 'yyyy-MM-dd').csv"
}
while ($true) {
$ts = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
$result = Test-Connection -TargetName $Target -Count 1 `
-TimeoutSeconds $TimeoutSeconds -ErrorAction SilentlyContinue
$rtt = if ($result -and $result.Status -eq "Success") { [double]$result.Latency } else { $null }
$status = if ($rtt -ne $null) { "OK" } else { "TIMEOUT" }
# Jitter: |current RTT - previous RTT|
$jitter = $null
if ($rtt -ne $null -and $prevRtt -ne $null) {
$jitter = [math]::Abs($rtt - $prevRtt)
$jitterHistory.Enqueue($jitter)
}
if ($rtt -ne $null) {
$rttHistory.Enqueue($rtt)
$prevRtt = $rtt
}
while ($rttHistory.Count -gt $BaselineWindowSize) { $rttHistory.Dequeue() | Out-Null }
while ($jitterHistory.Count -gt $BaselineWindowSize) { $jitterHistory.Dequeue() | Out-Null }
$meanRtt = if ($rttHistory.Count -gt 0) { [math]::Round(($rttHistory | Measure-Object -Average).Average, 2) } else { $null }
$stddevRtt = if ($rttHistory.Count -gt 1) {
$mean = ($rttHistory | Measure-Object -Average).Average
$variance = ($rttHistory | ForEach-Object { [math]::Pow($_ - $mean, 2) } | Measure-Object -Average).Average
[math]::Round([math]::Sqrt($variance), 2)
} else { $null }
$meanJitter = if ($jitterHistory.Count -gt 0) { [math]::Round(($jitterHistory | Measure-Object -Average).Average, 2) } else { $null }
[PSCustomObject]@{
Timestamp = $ts
Target = $Target
RTT_ms = $rtt
Jitter_ms = $jitter
MeanRTT_ms = $meanRtt
StdDev_ms = $stddevRtt
MeanJitter = $meanJitter
Status = $status
} | Export-Csv -Path (Get-LogPath) -Append -NoTypeInformation
Write-Host "[$ts] RTT: $(if ($rtt){"${rtt}ms"} else {'—'}) Jitter: $(if($jitter){"${jitter}ms"} else {'—'}) MeanRTT: $(if($meanRtt){"${meanRtt}ms"} else {'—'}) StdDev: $(if($stddevRtt){"${stddevRtt}ms"} else {'—'})"
Start-Sleep -Seconds $IntervalSeconds
}
Anomaly Detection with Baseline Thresholds
Once you have a rolling mean and standard deviation, you can flag anomalies statistically rather than with an arbitrary threshold. A probe is anomalous if its RTT exceeds mean + 3 * stddev (a three-sigma rule):
# Inside the loop, after calculating $meanRtt and $stddevRtt:
if ($rtt -ne $null -and $meanRtt -ne $null -and $stddevRtt -ne $null -and $stddevRtt -gt 0) {
$zScore = ($rtt - $meanRtt) / $stddevRtt
if ($zScore -gt 3.0) {
Write-Warning "[$ts] Anomalous RTT: ${rtt}ms (z-score $([math]::Round($zScore,1)), baseline mean ${meanRtt}ms ±${stddevRtt}ms)"
# Optionally: Write-EventLog or Send-MailMessage here
}
}
A three-sigma alert fires when a probe is more than three standard deviations above the rolling mean. For a stable link with mean 14 ms and stddev 1 ms, that threshold is 17 ms — tight enough to catch real anomalies without constant noise.
ICMP Internals and TTL Deep Dive
Understanding the ICMP packet lifecycle helps you interpret unusual entries in ping logs — particularly entries that show unexpected TTL values, ICMP Type 11 responses, or asymmetric latency.
The Full ICMP Lifecycle
The sequence diagram below traces three scenarios: a successful round trip, a TTL expiry mid-path, and a destination unreachable response.

Figure 2: ICMP echo request / reply lifecycle across router hops. Scenario 1 shows a successful RTT measurement. Scenario 2 shows TTL exhaustion generating an ICMP Type 11 (Time Exceeded) response before reaching the target. Scenario 3 shows an ICMP Type 3 (Destination Unreachable) relayed back when the target has no route or the port is closed.
Interpreting TTL Values in Logs
| Received TTL range | Probable OS default | Approximate hop count |
|---|---|---|
| 121–128 | Windows (128) | 0–7 hops |
| 57–64 | Linux / macOS (64) | 0–7 hops |
| 248–255 | Cisco IOS (255) | 0–7 hops |
| 113–120 | Windows via 8+ hops | 8–15 hops |
A sudden drop in received TTL — say from 116 to 108 — on a previously stable target means routing changed and your traffic now traverses more hops. That extra latency is not a target-side issue; it is a path change.
ICMP Rate Limiting and Probe Intervals
Most enterprise firewalls and carrier routers rate-limit ICMP responses to 100–1 000 packets per second. A 5-second probe interval from a single host generates 0.2 pps — far below any rate limit. Do not decrease probe intervals below 1 second for remote targets; you risk having your probe traffic itself appear as a DoS signal and being filtered at the edge.
Enterprise Tool Integration
Scripts are the right tool for ad-hoc investigation and lightweight edge monitoring. They are the wrong tool for production network monitoring at scale. Here is where to draw the line.
When to Stay with Scripts
- Single host or small set of targets (fewer than 20)
- Short-duration investigations (hours to days, not months)
- Constrained environments where no monitoring agent can be installed
- IoT edge nodes where lightweight footprint matters
When to Move to Enterprise Tools
- More than 20 targets or multi-site monitoring
- SLA reporting that requires signed-off audit trails
- On-call alerting with escalation, ack, and silence management
- Correlation with other metrics (CPU, disk, interface counters)
Tools Worth Evaluating in 2026
Zabbix 7.2 (open source): Native ICMP ping checks with configurable intervals, loss thresholds, and RTT trend graphs. Zabbix passive agents on Windows hosts send all metrics to a central server. The ICMP Simple Check requires no agent — the Zabbix server pings targets directly. Free for any scale; operational overhead is significant.
PRTG Network Monitor (Paessler): Sensor-based SaaS/on-prem hybrid with a dedicated Ping sensor supporting packet size, interval, and timeout configuration. PRTG calculates jitter natively and can send alerts via email, Slack, PagerDuty, or webhook. Free up to 100 sensors.
Datadog Network Performance Monitoring: Agent-based; the Datadog Agent on Windows hosts reports ICMP metrics via the network_path integration. Strong for cloud-native environments where hosts run in AWS/Azure and you want unified infra + APM dashboards. Cost scales with host count.
Microsoft Azure Network Watcher: For Azure-hosted workloads, Network Watcher’s Connection Monitor provides continuous TCP/ICMP probes between agents with millisecond resolution, path visualization, and Azure Monitor integration. No extra software for Azure VMs.
OpenNMS Horizon / Meridian: Enterprise-grade open-source NMS with ICMP poller, topology discovery, and a REST API. Suited for ISP-scale or campus-scale deployments.
For IoT systems where edge connectivity is critical to digital twin data fidelity, connecting ping monitoring into an observability stack matters as much as the sensors themselves. Network-layer telemetry feeds directly into the reliability models that OPC UA gateways depend on for consistent upstream data delivery.
Real-World Flaky-WAN Scenario
This walkthrough illustrates how the scripts above diagnose a real problem class: a WAN link that loses packets intermittently, only at certain hours, and only to certain destinations.
Symptom
A branch office reports that their ERP application loses connection to the datacenter “about twice a day, for 5–10 minutes.” The application team blames the network. The network team says the link shows green in their dashboard. No one has logs.
Step 1: Deploy the Monitor at Both Ends
On the branch office workstation, start the full logging script targeting the datacenter firewall IP, the ERP server IP, and a public DNS server (8.8.8.8) simultaneously:
# Run three background jobs for parallel monitoring
$targets = @("10.1.1.1", "10.10.5.20", "8.8.8.8")
$targets | ForEach-Object {
$t = $_
Start-Job -ScriptBlock {
param($target, $logDir)
# paste the full logging function here or dot-source it
$interval = 5
while ($true) {
$ts = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
$r = Test-Connection -TargetName $target -Count 1 -TimeoutSeconds 2 -ErrorAction SilentlyContinue
[PSCustomObject]@{
Timestamp = $ts; Target = $target
RTT_ms = if ($r) { $r.Latency } else { $null }
Status = if ($r) { "OK" } else { "TIMEOUT" }
} | Export-Csv "$logDir\ping_$($target -replace '\.','-')_$(Get-Date -Format 'yyyy-MM-dd').csv" -Append -NoTypeInformation
Start-Sleep -Seconds $interval
}
} -ArgumentList $t, "C:\Logs\PingMonitor"
}
Step 2: Collect 24–48 Hours of Data
After the next reported outage, pull the CSVs and analyze them:
$log = Import-Csv "C:\Logs\PingMonitor\ping_10-1-1-1_$(Get-Date -Format 'yyyy-MM-dd').csv"
# Find all TIMEOUT windows
$timeouts = $log | Where-Object { $_.Status -eq "TIMEOUT" }
$timeouts | Select-Object Timestamp, Target | Format-Table -AutoSize
# Count loss by hour
$log | Group-Object { (Get-Date $_.Timestamp).Hour } |
Select-Object Name, @{n='Total';e={$_.Count}},
@{n='Timeouts';e={($_.Group | Where-Object {$_.Status -eq 'TIMEOUT'}).Count}},
@{n='LossPct';e={[math]::Round((($_.Group | Where-Object {$_.Status -eq 'TIMEOUT'}).Count / $_.Count) * 100, 1)}} |
Sort-Object Name | Format-Table -AutoSize
Step 3: Interpret the Pattern
The output reveals:
– 8.8.8.8 — 0.0% loss all day → the branch internet link is healthy
– 10.1.1.1 (datacenter firewall) — 0.1% loss all day, spikes to 18% at 09:00 and 14:00
– 10.10.5.20 (ERP server) — mirrors the firewall pattern exactly
Conclusion: The fault is between the branch and the datacenter firewall, not in the ERP application or datacenter LAN. The 09:00 and 14:00 windows correlate with daily backup jobs on the datacenter side that saturate the WAN link. The fix is QoS prioritization or backup scheduling, not application tuning.
Without ping logs, this investigation would have taken days. With 48 hours of CSV data it takes minutes.
Common Pitfalls
1. Logging to the System Drive Without Rotation
Writing indefinitely to C:\Windows\Temp or the system drive fills the volume and causes unexpected system behavior. Always log to a dedicated directory on a data drive, and always run the rotation script as a scheduled task.
2. Running as a Non-Elevated User Without ICMP Permissions
On Windows, Test-Connection in PowerShell 7 uses the .NET Ping.SendAsync method, which does NOT require elevation. However, writing to system-owned log directories and using Write-EventLog (to register an event source) requires Administrator rights. Run setup steps as Administrator; the monitoring loop itself can run as a service account.
3. Treating Packet Loss as Binary
A link dropping 0.5% of packets is not “fine.” For a 5-second interval monitor sending 17 280 probes per day, 0.5% loss means 86 timeouts. In a VoIP or real-time sensor stream those 86 gaps per day are audible or cause data gaps in a historian. Always analyze loss as a percentage and trend it over time.
4. Not Accounting for ICMP Deprioritization
Enterprise routers often place ICMP in a best-effort queue. Your ping log may show 2–5% loss to a router that has zero actual packet loss for production traffic. Cross-reference ICMP loss with TCP probes or interface error counters before concluding the link has genuine packet loss.
5. Ignoring Asymmetric Latency
Ping measures round-trip time. A 50 ms RTT might be 5 ms outbound and 45 ms return, or 25 ms each way. traceroute/tracert and tools like PathPing give per-hop directionality. If an RTT spike appears in logs, follow it with a Test-NetConnection -TraceRoute to find which hop is responsible.
# PS7: trace route with hop-level RTT
Test-NetConnection -ComputerName 8.8.8.8 -TraceRoute |
Select-Object -ExpandProperty TraceRoute
6. Using Windows PowerShell 5.1 Test-Connection Syntax in PS7
In Windows PowerShell 5.1, Test-Connection returns a Win32_PingStatus WMI object. In PowerShell 7, it returns a different object with .Latency and .Status properties. Scripts written for PS5.1 that reference .ResponseTime or .StatusCode will fail silently in PS7. Always test your scripts against the PowerShell version on the target host.
# PS5.1 style (do NOT use in PS7)
$r = Test-Connection 8.8.8.8 -Count 1
$r.ResponseTime # works in 5.1, $null in PS7
# PS7 style
$r = Test-Connection -TargetName 8.8.8.8 -Count 1 -TimeoutSeconds 2 -ErrorAction SilentlyContinue
$r.Latency # correct for PS7
7. No Alerting Cooldown
Without a cooldown, a 10-minute outage at 5-second probe intervals triggers 120 alert emails. Always implement a cooldown window (15–30 minutes minimum) and a de-duplication mechanism. The main logging script above includes a 15-minute cooldown via $lastAlertTime.
Conclusion: When to Move to Enterprise Monitoring
PowerShell ping logging is a powerful, zero-cost diagnostic tool. It suits edge monitoring, short-term investigations, and constrained environments where no commercial agent can be installed. But it has hard limits:
- No topology discovery: It does not know about links, interfaces, or routing changes unless you explicitly configure targets.
- No correlation: It cannot correlate ICMP loss with CPU spikes, interface errors, or BGP flaps on the upstream router.
- No persistent on-call management: Scripts send emails. They do not page the right person, track who acknowledged the alert, or silence during maintenance windows.
- No retention policy or compliance reporting: CSVs in a directory are not an audit trail.
Graduate to an enterprise tool — Zabbix, PRTG, Datadog, or Azure Network Watcher — when you have more than 20 persistent monitoring targets, when you need SLA reporting, or when ICMP data needs to feed into a broader observability stack alongside application metrics and infrastructure telemetry.
Until then, a well-structured PowerShell 7.5 ping monitor running as a Windows service under a dedicated service account, writing to a rotating CSV, and pushing alerts to the event log, is a legitimate production-grade solution for small-to-medium environments.
FAQ
Q: Does Windows ping logging require Administrator privileges?
The monitoring loop itself does not. Test-Connection in PowerShell 7 uses System.Net.NetworkInformation.Ping which works for standard users. You need Administrator rights only for the one-time setup steps: registering a Windows Event Log source with New-EventLog, creating log directories under system-owned paths, and registering the monitor as a Windows service.
Q: How do I run the ping monitor as a Windows service so it survives reboots?
Use the built-in sc.exe with NSSM (Non-Sucking Service Manager) or the newer New-Service approach. The simplest method with PowerShell 7 is to register a scheduled task that runs at startup with the “Run whether user is logged on or not” option and the SYSTEM or a dedicated service account. Task Scheduler is sufficient for most environments and requires no additional software.
$action = New-ScheduledTaskAction -Execute "pwsh.exe" `
-Argument "-NonInteractive -File C:\Scripts\PingMonitor.ps1 -Target 8.8.8.8"
$trigger = New-ScheduledTaskTrigger -AtStartup
$settings = New-ScheduledTaskSettingsSet -ExecutionTimeLimit ([TimeSpan]::Zero)
Register-ScheduledTask -TaskName "PingMonitor" -Action $action `
-Trigger $trigger -Settings $settings -RunLevel Highest -Force
Q: What is a normal jitter value for a healthy network link?
For a corporate LAN, jitter below 1 ms is typical. For a broadband WAN (fiber or cable), jitter below 5 ms is healthy. For a 4G/LTE link, jitter below 20 ms is acceptable. VoIP systems typically require jitter below 30 ms for acceptable call quality (G.711 codec). Above 50 ms jitter, real-time applications degrade noticeably regardless of mean RTT.
Q: Can I log pings to multiple targets simultaneously without multiple scripts?
Yes. Use PowerShell background jobs (Start-Job) or PowerShell runspaces for higher-performance parallel monitoring. The flaky-WAN scenario section above shows the Start-Job approach for three targets. For more than 10 targets, use the ThreadJob module (Install-Module ThreadJob) which is lower overhead than full process-based jobs.
Q: Why does my ping log show timeouts even though the host is reachable?
The most common causes are: (1) the target host’s firewall blocks ICMP Echo Request; (2) a QoS policy on a transit router drops ICMP under congestion; (3) your TimeoutSeconds value is too short for a high-latency link (increase to 4–5 seconds for satellite or intercontinental targets); (4) the target is rate-limiting ICMP responses to prevent abuse. Cross-reference with a TCP probe using Test-NetConnection -Port 443 to the same host — if TCP succeeds but ICMP fails, the host is up and ICMP is filtered.
Q: How do I analyze a large CSV ping log without loading it all into memory?
Use PowerShell’s Import-Csv pipeline with filtering to avoid loading millions of rows at once:
# Stream-process a large CSV: count timeouts per hour without full in-memory load
$hourBuckets = @{}
Import-Csv "C:\Logs\PingMonitor\ping_log_2026-06-10.csv" | ForEach-Object {
$hour = (Get-Date $_.Timestamp).Hour
if (-not $hourBuckets[$hour]) { $hourBuckets[$hour] = @{Total=0; Loss=0} }
$hourBuckets[$hour].Total++
if ($_.Status -eq "TIMEOUT") { $hourBuckets[$hour].Loss++ }
}
$hourBuckets.GetEnumerator() | Sort-Object Name |
Select-Object @{n='Hour';e={$_.Key}},
@{n='Total';e={$_.Value.Total}},
@{n='Loss';e={$_.Value.Loss}},
@{n='LossPct';e={[math]::Round($_.Value.Loss/$_.Value.Total*100,1)}} |
Format-Table -AutoSize
For truly large historical datasets (months of data), import the CSVs into SQLite via the PSSQLite module or into InfluxDB for time-series querying.
Further Reading
- Microsoft Learn: Test-Connection (PowerShell 7) — official reference for all parameters and return object properties in PowerShell 7.
- RFC 792: Internet Control Message Protocol — the original ICMP specification defining Echo Request, Echo Reply, Destination Unreachable, and Time Exceeded message types.
- Microsoft Learn: Test-NetConnection — TCP-level connectivity testing with optional traceroute, useful for cross-referencing ICMP results with TCP-layer reachability.
- OPC UA Protocol: Complete Technical Guide — how edge connectivity and network reliability tie into OPC UA data pipelines and digital twin architectures.
Riju is a technical writer and IoT systems practitioner at iotdigitaltwinplm.com, focused on industrial connectivity, edge monitoring, and digital twin infrastructure.
