# Define the IP addresses to monitor $addresses = @() $addressDefinitions = @( "1.1.1.1", "1.0.0.1", "8.8.8.8-9" # Using range notation for cleaner definition ) # Set up logging configuration $logFolder = ".\Logs" $logFile = Join-Path $logFolder "network_monitor.log" $statLogFile = Join-Path $logFolder "statistics.log" $csvLogFile = Join-Path $logFolder "ping_data.csv" $errorLogFile = Join-Path $logFolder "errors.log" # Create log directory if it doesn't exist if (-not (Test-Path $logFolder)) { New-Item -Path $logFolder -ItemType Directory | Out-Null } # Initialize log files with headers if (-not (Test-Path $csvLogFile)) { "Timestamp,IPAddress,Status,ResponseTime,SuccessRate" | Out-File -FilePath $csvLogFile } # Function to write to log file with timestamp function Write-Log { param( [Parameter(Mandatory=$true)] [string]$Message, [string]$LogFile = $logFile, [switch]$NoConsole ) $timestamp = Get-Date -Format 'yyyy-MM-dd HH:mm:ss' "$timestamp - $Message" | Out-File -FilePath $LogFile -Append if (-not $NoConsole) { Write-Host "$timestamp - $Message" } } # Log script start Write-Log "Network monitoring script started" foreach ($addr in $addressDefinitions) { if ($addr -match "(\d+\.\d+\.\d+\.)(\d+)-(\d+)") { $prefix = $matches[1] $start = [int]$matches[2] $end = [int]$matches[3] for ($i = $start; $i -le $end; $i++) { $addresses += "$prefix$i" } } else { $addresses += $addr } } # Log monitored addresses Write-Log "Monitoring IP addresses: $($addresses -join ', ')" # Initialize hashtables to store statistics $stats = @{} foreach ($addr in $addresses) { $stats[$addr] = @{ MinTime = [int]::MaxValue MaxTime = 0 TotalTime = 0 SuccessCount = 0 FailCount = 0 Samples = [System.Collections.Queue]::new(60) # Fixed-size queue for better memory management LastStatus = $null StatusChanged = $false LastLoggedStats = [DateTime]::MinValue } } # Function to calculate mean function Get-Mean { param($Numbers) if ($Numbers.Count -eq 0) { return 0 } $array = [array]($Numbers) return ($array | Measure-Object -Average).Average } # Function to clear the console and position cursor function Reset-Display { Clear-Host $host.UI.RawUI.CursorPosition = @{X=0; Y=0} } # Function to format number with fixed width function Format-Number { param( [Parameter(Mandatory=$true)] [double]$Number, [int]$Width = 6, [int]$Decimals = 1 ) if ($Number -eq [int]::MaxValue) { return "".PadLeft($Width) } return $Number.ToString("F$Decimals").PadLeft($Width) } # Function to log status changes function Write-StatusLog { param( [string]$Address, [string]$OldStatus, [string]$NewStatus ) $statusText = if ($NewStatus -eq $true) { "UP" } else { "DOWN" } $oldStatusText = if ($OldStatus -eq $true) { "UP" } else { "DOWN" } $logMessage = "$Address status changed from $oldStatusText to $statusText" Write-Log $logMessage Write-Host "`n$logMessage" -ForegroundColor Yellow } # Function to log statistics periodically function Write-StatisticsLog { param( [string]$Address, [hashtable]$Statistics, [int]$IntervalMinutes = 15 ) $now = Get-Date if (($now - $Statistics.LastLoggedStats).TotalMinutes -ge $IntervalMinutes) { $totalRequests = $Statistics.SuccessCount + $Statistics.FailCount $successRate = if ($totalRequests -gt 0) { [math]::Round(($Statistics.SuccessCount / $totalRequests) * 100, 1) } else { 0 } $meanTime = if ($Statistics.Samples.Count -gt 0) { [math]::Round((Get-Mean $Statistics.Samples), 1) } else { 0 } $statsMessage = "$Address Stats - Min: $($Statistics.MinTime)ms, Max: $($Statistics.MaxTime)ms, " + "Mean: ${meanTime}ms, Success Rate: ${successRate}%, " + "Success Count: $($Statistics.SuccessCount), Fail Count: $($Statistics.FailCount)" Write-Log -Message $statsMessage -LogFile $statLogFile -NoConsole $Statistics.LastLoggedStats = $now } } # Function to log ping data to CSV function Write-PingDataLog { param( [string]$Address, [bool]$Status, [int]$ResponseTime, [double]$SuccessRate ) $statusText = if ($Status) { "UP" } else { "DOWN" } $timestamp = Get-Date -Format 'yyyy-MM-dd HH:mm:ss' "$timestamp,$Address,$statusText,$ResponseTime,$SuccessRate" | Out-File -FilePath $csvLogFile -Append } # Function to handle errors function Write-ErrorLog { param( [Parameter(Mandatory=$true)] [System.Management.Automation.ErrorRecord]$ErrorRecord, [string]$Context = "General" ) $errorMessage = "[$Context] $($ErrorRecord.Exception.GetType().Name): $($ErrorRecord.Exception.Message)" $errorDetails = $ErrorRecord | Format-List * -Force | Out-String Write-Log -Message $errorMessage -LogFile $errorLogFile -NoConsole Write-Log -Message "Details: $errorDetails" -LogFile $errorLogFile -NoConsole Write-Host "Error: $errorMessage" -ForegroundColor Red } # Main monitoring loop try { $pingInterval = 1 # seconds between pings $statsLogInterval = 15 # minutes between logging statistics $csvLogFrequency = 5 # log every Nth ping to CSV to avoid huge files $pingCounter = 0 Write-Log "Monitoring started with ping interval of ${pingInterval}s and stats logging every ${statsLogInterval} minutes" while ($true) { Reset-Display $currentTime = Get-Date -Format "HH:mm:ss" Write-Host "Network Monitor - Last Updated: $currentTime" -ForegroundColor Cyan Write-Host "Press Ctrl+C to exit`n" -ForegroundColor DarkGray # Header with fixed widths Write-Host "IP Address Status Min Max Mean Success% Fails Trend" -ForegroundColor Yellow Write-Host "--------------- -------- ----- ----- ----- -------- ------ -------" -ForegroundColor Yellow $pingCounter++ foreach ($addr in $addresses) { $ping = New-Object System.Net.NetworkInformation.Ping try { $result = $ping.Send($addr, 1000) # 1 second timeout $status = $result.Status -eq 'Success' $responseTime = if ($status) { $result.RoundtripTime } else { 0 } } catch { $status = $false $responseTime = 0 Write-ErrorLog -ErrorRecord $_ -Context "Ping to $addr" } if ($status) { $stats[$addr].TotalTime += $responseTime $stats[$addr].SuccessCount++ # Update rolling queue of samples - suppress output if ($stats[$addr].Samples.Count -eq 60) { [void]$stats[$addr].Samples.Dequeue() } [void]$stats[$addr].Samples.Enqueue($responseTime) # Update min/max $stats[$addr].MinTime = [Math]::Min($stats[$addr].MinTime, $responseTime) $stats[$addr].MaxTime = [Math]::Max($stats[$addr].MaxTime, $responseTime) $statusText = "UP" $color = "Green" } else { $stats[$addr].FailCount++ $statusText = "DOWN" $color = "Red" } # Calculate statistics $totalRequests = $stats[$addr].SuccessCount + $stats[$addr].FailCount $successRate = if ($totalRequests -gt 0) { [math]::Round(($stats[$addr].SuccessCount / $totalRequests) * 100, 1) } else { 0 } $meanTime = if ($stats[$addr].Samples.Count -gt 0) { [math]::Round((Get-Mean $stats[$addr].Samples), 1) } else { 0 } # Check for status changes and log them if ($null -ne $stats[$addr].LastStatus -and $stats[$addr].LastStatus -ne $status) { Write-StatusLog -Address $addr -OldStatus $stats[$addr].LastStatus -NewStatus $status } $stats[$addr].LastStatus = $status # Log statistics periodically Write-StatisticsLog -Address $addr -Statistics $stats[$addr] -IntervalMinutes $statsLogInterval # Log to CSV (but not every ping to avoid huge files) if ($pingCounter % $csvLogFrequency -eq 0) { Write-PingDataLog -Address $addr -Status $status -ResponseTime $responseTime -SuccessRate $successRate } # Calculate trend based on last 5 samples - suppress output $trend = if ($stats[$addr].Samples.Count -ge 5) { $recent = @([array]($stats[$addr].Samples)) $lastFive = $recent[-[Math]::Min(5, $recent.Count)..-1] if ($lastFive.Count -ge 2) { $slope = ($lastFive[-1] - $lastFive[0]) / [Math]::Max(1, ($lastFive.Count - 1)) if ([Math]::Abs($slope) -lt 1) { "━" } elseif ($slope -lt 0) { "↓" } else { "↑" } } else { "━" } } else { "━" } # Format output fields $ipField = $addr.PadRight(15) $statusField = $statusText.PadRight(8) $minField = Format-Number -Number $stats[$addr].MinTime -Width 5 -Decimals 0 $maxField = Format-Number -Number $stats[$addr].MaxTime -Width 5 -Decimals 0 $meanField = Format-Number -Number $meanTime -Width 5 -Decimals 1 $successField = Format-Number -Number $successRate -Width 8 -Decimals 1 $failsField = $stats[$addr].FailCount.ToString().PadLeft(6) # Display the line Write-Host $ipField -NoNewline Write-Host $statusField -NoNewline -ForegroundColor $color Write-Host "$minField $maxField $meanField $successField $failsField $trend" } Start-Sleep -Seconds $pingInterval } } catch { # Log any unhandled exceptions Write-ErrorLog -ErrorRecord $_ -Context "Main monitoring loop" } finally { # Cleanup when script is interrupted $endMessage = "Monitoring stopped at $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')" Write-Log $endMessage Write-Host "`n$endMessage" -ForegroundColor Cyan }