-
-
Save changbowen/9dc1c9910740e7474354a6018adefade to your computer and use it in GitHub Desktop.
A simple Powershell script that monitors a remote hosts connection. Loops constantly reporting to the screen when the status changes. Optionally also records to the event log, makes an audible beep, and/or sends emails.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<# | |
.SYNOPSIS | |
Tests the connection to the supplied host or IP and reports back on changes to its status. | |
.DESCRIPTION | |
This script will check the connection to the supplied hostname or IP address every 5 second's to | |
monitor its status. If the status changes, a message is recorded, the Eventlog is update (optional), and an email is sent | |
to a supplied email address (optional). | |
If a TCPPort parameter is suppied the script will attempt to connect to this port. Otherwise a simple ICMP Ping is used. | |
To end monitoring, press CTRL-C | |
.PARAMETER RemoteHost | |
A String specifying host or IP address you'd like to monitor. You can also specify a TCPport here using host:port syntax. | |
.PARAMETER EmailAddress | |
A String specifying the email address to notify of status changes. If not supplied, or empty, emails are not sent. | |
In order to use this, first edit the script to supply a valid SMTP server, port, and from address. | |
.PARAMETER SMTPServer | |
The SMTP server address. Port 25 will be used. | |
.PARAMETER SMTPFromAddress | |
The From address of the notification email. | |
.PARAMETER EventLog | |
If true, status changes are also recorded in the Windows Application EventLog. | |
In order to use this, first run this command at an Administrator Powershell Prompt: | |
> New-EventLog -LogName Application -Source MonitoringConnection | |
.PARAMETER NoNotify | |
If true, status changes will not show up as Windows notifications. | |
.PARAMETER Silent | |
If true, the script will not create any beeps as the status changes. | |
.PARAMETER Pause | |
The number of seconds to wait between tests. Default 5 seconds. | |
.PARAMETER ConsecThreshold | |
Hold alert until the specified number of consecutive success or failure. | |
Set to 0 will alert each time the status changes. Default is 3. | |
.PARAMETER FailRateThreshold | |
Send alert when status check failures reaches the specified percentage during a period of time set by FailWindowMin. | |
Set to 0 to disable failure check. Default is 10. | |
.PARAMETER FailWindowMin | |
See FailRateThreshold. Default is 10. | |
.PARAMETER FlipRateThreshold | |
Send alert when status flips reaches the specified percentage during a period of time set by FlipWindowMin. | |
Set to 0 to disable flipping check. Default is 10. | |
.PARAMETER FlipWindowMin | |
See FlipRateThreshold. Default is 10. | |
.PARAMETER TCPPort | |
If supplied, the script will test a connection to the supplied TCP port. If not supplied a ICMP Ping test is used. | |
.PARAMETER TCPTimeoutMs | |
Time in milliseconds to wait for response on TCP connect. Default is 2000 ms. | |
.PARAMETER Namespace | |
If supplied, the script will run a query on this WMI Namespace on the supplied Host | |
.PARAMETER Class | |
If supplied, the script will run a query on this WMI Class of the supplied namespace on the supplied Host | |
.PARAMETER WMIProperty | |
The WMI Property to monitor. Any changes in value will be reported | |
.PARAMETER WMIFilter | |
The Filter used to identify the correct result in the WMI query. | |
.PARAMETER WMIValidator | |
A scriptblock that returns bool to indicate success or failure. Use $args[0] to access the WMI property value. | |
.EXAMPLE | |
./Start-Monitoring google.com | |
.EXAMPLE | |
./Start-Monitoring -RemoteHost 8.8.8.8 -EmailAddress [email protected] | |
.EXAMPLE | |
./Start-Monitoring -RemoteHost 8.8.8.8 -EmailAddress [email protected] -EventLog $true | |
.EXAMPLE | |
./Start-Monitoring -RemoteHost 8.8.8.8 -TCPPort 53 | |
.EXAMPLE | |
./Start-Monitoring -RemoteHost 8.8.8.8:53 | |
.EXAMPLE | |
./Start-Monitoring -RemoteHost @("www.google.com:443","8.8.8.8:53") | |
.EXAMPLE | |
./Start-Monitoring -RemoteHost WebServer1 -Namespace "root\cimv2" -Class Win32_PerfRawData_APPPOOLCountersProvider_APPPOOLWAS -WMIProperty CurrentApplicationPoolState -WMIFilter "{$_.Name -like 'SLSS'}" | |
#> | |
[CmdletBinding()] | |
Param( | |
[Parameter(Mandatory=$True,Position=1)] | |
[ValidateScript({ | |
if ($_ -like "*:*") { | |
if($_.Split(":")[0] -As [IPAddress] -and $_.Split(":")[1] -As [Int]){ | |
$True | |
}else { | |
if(([System.Net.Dns]::GetHostEntry($_.Split(":")[0])) -and $_.Split(":")[1] -As [Int]){ | |
$True | |
}else{ | |
Throw "$($_) is not a valid IP or hostname. Try again." | |
} | |
} | |
} elseif($_ -As [IPAddress]){ | |
$True | |
} else { | |
if([System.Net.Dns]::GetHostEntry($_)){ | |
$True | |
}else{ | |
Throw "$($_) is not a valid IP or hostname. Try again." | |
} | |
} | |
})] | |
[string[]]$RemoteHost, | |
[ValidateScript({ | |
if($_ -Match "\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}\b" -or $_ -eq $null){ | |
$True | |
} else { | |
Throw "$($_) is not a valid email address. Try again, or remove this parameter to skip email alerts." | |
} | |
})] | |
[mailaddress]$EmailAddress = $null, | |
[string]$SMTPServer, | |
[string]$SMTPFromAddress, | |
[switch]$EventLog, | |
[switch]$NoNotify, | |
[switch]$Silent, | |
[int]$Pause = 5, | |
[int]$ConsecThreshold = 3, | |
[int]$FailRateThreshold = 10, | |
[int]$FailWindowMin = 10, | |
[int]$FlipRateThreshold = 10, | |
[int]$FlipWindowMin = 10, | |
[Parameter(Mandatory=$True, ParameterSetName = 'Port')] | |
[int]$TCPPort, | |
[Parameter(ParameterSetName = 'Port')] | |
[int]$TCPTimeoutMs = 2000, | |
[Parameter(Mandatory=$true, ParameterSetName = 'WMI')] | |
[string]$Namespace, | |
[Parameter(Mandatory=$true,ParameterSetName = 'WMI')] | |
[string]$Class, | |
[Parameter(Mandatory=$true, ParameterSetName = 'WMI')] | |
[string]$WMIProperty, | |
[Parameter(Mandatory=$False, ParameterSetName = 'WMI')] | |
[scriptblock]$WMIFilter, | |
[Parameter(Mandatory=$True, ParameterSetName = 'WMI')] | |
[scriptblock]$WMIValidator | |
) | |
$SMTPPort = 25 | |
#Error Check | |
#Confirm EventLog Source is configured if required | |
if($EventLog){ | |
try { | |
[System.Diagnostics.EventLog]::SourceExists("MonitoringConnection") | Out-Null | |
} catch{ | |
throw "In order to record to the EventLog, first run this command at an Administrator Powershell Prompt: `n` | |
> New-EventLog -LogName Application -Source MonitoringConnection" | |
} | |
} | |
#notification / alert function | |
function Alert { | |
param ( | |
[bool]$Success, | |
[string]$Hostname, | |
[string]$StatusMsg, | |
[switch]$StatusOnly # only update status in the console and skip all other forms of notification | |
) | |
$resultTimeStr = [datetime]::UtcNow.ToString('u'); | |
$statusLabel = if ($Success) { 'UP' } else { 'DOWN' } | |
$statusColor = if ($Success) { 'green' } else { 'red' } #needs to be css-compatible | |
Write-Host "$Hostname $StatusMsg at $resultTimeStr" -ForegroundColor $statusColor | |
if ($Host.Name -ne "ServerRemoteHost") { $Host.PrivateData.ProgressBackgroundColor = $statusColor } | |
Write-Progress -Id $PID -Activity " Monitoring connection status of $Hostname" -Status "$StatusMsg at $resultTimeStr" | |
if ($StatusOnly) { return } | |
if (-not $Silent) { | |
if ($Success) { [console]::beep(1000, 300) } else { [console]::beep(500, 300) } | |
} | |
if (-not $NoNotify) { | |
Write-Debug "Showing notification" | |
$XmlString = @(" | |
<toast duration='short'> | |
<visual> | |
<binding template='ToastGeneric'> | |
<text>$Hostname $StatusMsg</text> | |
<text>$Hostname $StatusMsg at $resultTimeStr</text> | |
<image src='$(if ($Success) { $InfoLogo } else { $WarningLogo })' placement='appLogoOverride' hint-crop='circle' /> | |
</binding> | |
</visual> | |
<audio src='ms-winsoundevent:Notification.Default' /> | |
</toast>") | |
$ToastXml = [Windows.Data.Xml.Dom.XmlDocument]::new() | |
$ToastXml.LoadXml($XmlString) | |
$Toast = [Windows.UI.Notifications.ToastNotification]::new($ToastXml) | |
[Windows.UI.Notifications.ToastNotificationManager]::CreateToastNotifier($AppId).Show($Toast) | |
} | |
if ($EmailAddress -ne $null) { | |
Send-MailMessage -To $EmailAddress -BodyAsHtml -From $SMTPFromAddress -SmtpServer $SMTPServer -Port $SMTPPort ` | |
-Subject "$Hostname $statusLabel - $resultTimeStr" ` | |
-Body ` | |
"<p> | |
<b>Host Name: </b>$Hostname<br/> | |
<b>Timestamp: </b>$resultTimeStr<br/> | |
<b>Status: </b><span style='color: $statusColor'>$StatusMsg</span></br> | |
</p>" | |
} | |
if ($EventLog) { | |
Write-EventLog -LogName Application -Source MonitoringConnection -Message "$Hostname $StatusMsg" ` | |
-EventId $(if ($Success) { 8000 } else { 8001 }) ` | |
-EntryType $(if ($Success) { 'Information' } else { 'Warning' }) | |
} | |
} | |
#Deal with an Array first | |
if ($RemoteHost.Count -gt 1) { | |
Write-Host "Monitoring the connection status of $($RemoteHost -join ', ')" -ForegroundColor Blue | |
Write-Host "Press CTRL-C to stop" -ForegroundColor Blue | |
Write-Host | |
Write-Progress -Activity "Monitoring connection status of $($RemoteHost -join ', ')" -Id $PID -Status "..." | |
# Change the default behavior of CTRL-C so that the script can intercept and use it versus just terminating the script. | |
[Console]::TreatControlCAsInput = $True | |
# Sleep for 1 second and then flush the key buffer so any previously pressed keys are discarded and the loop can monitor for the use of | |
# CTRL-C. The sleep command ensures the buffer flushes correctly. | |
Start-Sleep -Seconds 1 | |
$Host.UI.RawUI.FlushInputBuffer() | |
$Jobs = @() | |
$ScriptPath = $MyInvocation.MyCommand.Definition | |
#Shared args | |
$ScriptArgs = @{ | |
EventLog = $EventLog | |
NoNotify = $NoNotify | |
Silent = $Silent | |
Pause = $Pause | |
ConsecThreshold = $ConsecThreshold | |
FlipRateThreshold = $FlipRateThreshold | |
FlipWindowMin = $FlipWindowMin | |
FailRateThreshold = $FailRateThreshold | |
FailWindowMin = $FailWindowMin | |
SMTPServer = $SMTPServer | |
SMTPFromAddress = $SMTPFromAddress | |
} | |
if ($EmailAddress) { $ScriptArgs.Add("EmailAddress", $EmailAddress) } | |
switch ($PSCmdlet.ParameterSetName) { | |
'Port' { | |
$ScriptArgs.Add("TCPPort", $TCPPort) | |
$ScriptArgs.Add("TCPTimeoutMs", $TCPTimeoutMs) | |
} | |
'WMI' { | |
$ScriptArgs.Add("Namespace", $Namespace) | |
$ScriptArgs.Add("Class", $Class) | |
$ScriptArgs.Add("WMIProperty", $WMIProperty) | |
$ScriptArgs.Add("WMIFilter", $WMIFilter) | |
$ScriptArgs.Add("WMIValidator", $WMIValidator) | |
} | |
Default {} | |
} | |
$RemoteHost | ForEach-Object { | |
$Jobs += Start-Job -Name "Monitor-$($_)" -ScriptBlock { | |
param($p1, $p2, $p3) | |
$p3.Add("RemoteHost", $p2) | |
& $p1 @p3 | |
} -ArgumentList @($ScriptPath, $_, $ScriptArgs) | |
} | |
While ((Get-Job | Where-Object {$_.Name -like "Monitor-*"}).Count -gt 0) { | |
Receive-Job -Job $Jobs | |
# If a key was pressed during the loop execution, check to see if it was CTRL-C (aka "3"), and if so exit the script after clearing | |
# out any running jobs and setting CTRL-C back to normal. | |
if ($Host.UI.RawUI.KeyAvailable -and ($Key = $Host.UI.RawUI.ReadKey("AllowCtrlC,NoEcho,IncludeKeyUp"))) { | |
if ([Int]$Key.Character -eq 3) { | |
Write-Host "" | |
Write-Warning "CTRL-C was used - Shutting down any running jobs before exiting." | |
Get-Job | Where-Object {$_.Name -like "Monitor-*"} | Remove-Job -Force -Confirm:$False | |
[Console]::TreatControlCAsInput = $False | |
Return | |
} | |
# Flush the key buffer again for the next loop. | |
$Host.UI.RawUI.FlushInputBuffer() | |
} | |
#Add a small pause to ease CPU load | |
Start-Sleep -Milliseconds 200 | |
} | |
} | |
#Extract TCP Port if encoded in Host | |
if ($RemoteHost -like "*:*") { | |
$TCPPort = [int]$RemoteHost.Split(":")[1] | |
$RemoteHost = $RemoteHost.Split(":")[0] | |
} | |
if ($Host.Name -ne "ServerRemoteHost"){ | |
Write-Host "Monitoring the connection status of $RemoteHost" -ForegroundColor Blue | |
Write-Host "Press CTRL-C to stop" -ForegroundColor Blue | |
Write-Host | |
if ($Host.Name -ne "ServerRemoteHost") { $Host.PrivateData.ProgressForegroundColor = 'Black' } | |
} | |
#Setup Nofitications | |
if (-not $NoNotify) { | |
$AppId = '{1AC14E77-02E7-4E5D-B744-2EB1AE5198B7}\WindowsPowerShell\v1.0\powershell.exe' | |
$null = [Windows.UI.Notifications.ToastNotificationManager, Windows.UI.Notifications, ContentType = WindowsRuntime] | |
$null = [Windows.Data.Xml.Dom.XmlDocument, Windows.Data.Xml.Dom.XmlDocument, ContentType = WindowsRuntime] | |
$WarningLogo = "$env:TEMP\ToastWarningLogo.png" | |
if (-not (Test-Path $WarningLogo)){ | |
$Logo = "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" | |
[byte[]]$Bytes = [convert]::FromBase64String($Logo) | |
[System.IO.File]::WriteAllBytes($WarningLogo,$Bytes) | |
} | |
$InfoLogo = "$env:TEMP\ToastInfoLogo.png" | |
if (-not (Test-Path $InfoLogo)){ | |
$Logo = "iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAG20lEQVR4Xu2ZbYwdZRXH/+fMzL1LpC3t3rnbCLE1QQH7wotGS4zKBz5oK5UPkLRBCkKsIVgsoDUmu9PZe69+IBUFGo2EtyovRRNF5S3RLw2RaCjEFsuLSqQJNHRnttotNnvvzJxjnru70q2WOy93u8nunW8395znnPN7zjnPeWYI8/yheR4/egB6GTDPCfRKYJ4nQK8J9kqgVwLznECvBOZ5AvROgdNaAlW/uVoSvpwIawQ43xI5WxkLTBaS4JiA3yLG66r4owX53eF6+eWZztAZB1DZpgvQF28m4EYAF2QJiBSvCHA/Wfa9gU/vZtFNKztzAHy1K0l8C5EMArzYOCTQw0z8FAn2CORAyXIOHgLGzH8fBBa2kmgZEa8UwecAWcdM1YnskCNCVA9fc+7BLyhJG1wauRkBUBkcP09g7bYYF006sUeZdoSwnoVPcRrHsFmdytLk8wT9FoDPtHUEL6mdbAz9vr+mWiOFUNcBVPx4vcbJI8x8poDeYNavB77zbApfTini+vFaFewk6IcBOaawNoY1+6kia07pdhVA1YuvVSQPAmwp6cNEzk3dql3TS7gU/USZNkIkVrauD2v2I0UhdA2A2XmS5Jft4AE/rNk1gLSog9P1lapePKzA0CSEK4tmQlcAmJpX0F6T9hPBO8PdDXz6au72qA7FoCkHiH48aPT9La+94gB8tZfE8Qum4Zm0D4edTWl2frmvfe9Kcich+bI5HsDWzxb907r97/dQs3MwSu726FEobQDwYvCq/am8p0NhABWvdSuB7pxoeNZFaWu+4sU/IuhN04Il2hkM21s6AwCW+LqQomgfW7Qcqt8I6qW70+idLFMIgGlM1Nc62D7nGV/I0u1drzkGcHsKfO+Ro0GtfFbaQCpefAVBfwPIKB8vLTu8g/6dVrcrp0DFi24nYAeAPUHNuSyLcXeweRTMC6fHL/8KGuX20JTuUap48XMEfFpVbw3rpR+m03tPqlAGuF70ihlvlemK0LefzGLc9eKdgN58ks5dQc3ZmmWdidNHfw3BX4KGsyqLrpHNDcBcbFR4nxlvR9k5J/WEN+nhuVu0fHRJsgOaXDvRBHlXwPY2+NTKFMRmdfqXNt9mWK6lsvKdevlAFv3cACpD0W1E+D6IHgiGbXPRmbXHHWw9BKbrAN0a1Ep3ZXEkNwDXi34O4GpSum6kbv80i9Fuy7pe6ysAPQDRx4NGyRyNqZ/cAPq9aD8Dq1T1E2G99GJqizMg2O+3PslCf0oEfz7ScC7OYiI3gOpgc1SZlzhsu4d8CrMY7bbsgK9VkfiwShKGjT43y/q5AbheswWwE7BdztK4XC/6v/eDoObk9qXdUBfH4yTSGmmUyz0AGQjkpt6tEpjKiCIZMCslUBmM9hFjddEm2A0As9IEp45BgK4PavauDFk3TbQbAGblGJwahETowdGGfcOsAtje2gWlTXluhbl7wMBQc5UQ7xfRkVHbOTvrKDwFrHAG+FqqxM23ia1KwrLiiF8295PUT24AxkJ1KDqghI8paH1Ys3+b2uoJgkUBVP34ShX9lQAvj9ac1Vl9KATgv/cB4Lmg5nw2q3EjXwyAkuvFzwNYk+ceYOwXAuD6eibFrYNmIgTTusC3n84KoQiAAS/+kkCfMBNgyS4vO+TT8az2CwEwxipDra1E9AMF/QPj1oXhHXTs/Zw41SR4sk6nuWDxt3WR40T7lelDUN0S1Es7swZfOAPaBq9Wy70gfgHAxST62EjDueb9Xop2B0A79R831gHsDV6118zaS9F2FvjjHyWhveYdHwH1kZrj5dmNtDquF30PwHcgMkbQS0YafW+k1T1ZrnAJTC1Y8eJ1JMkTYLZBaATDtpfm9Xg2x9s7/93J4GOwtT6o2c9kW2O6dNcAtDPBi68hSR6agKC7E3K+dsSn9tffoo+peS7F9zHhqsmvQpvCmv1Y0XW7CmCiHOIvkiSPmnKQRN8ki28Ja9aT+bNBacBL1qvI3e2GJzIGtjYU3fkpcF0H0D7bB8c/Ali7wbjE/FbgD2C6IzxkPYN7KUq1a76WqkjWqug2AJdO6uwlSTYUqfkZ6wH/E5Q5Hc6PbgapB3C/+V+QBCz8NBh7hHHAhvPmglEcNf8d68eiGNFyFqwA4TJNZK0Zb9sAJQmJeDh4zflx3m5/KugzkgEnGhv4pn4gOSP6KindCMbKVLs/KWTGW4bez8ed+/J89Ulja8YBnOjE0qHmioTocgguTUDnMZJzGNT+OiTQMYH1lgV9HYTnE0t/n/Vikybg01cCebyZBZ3TmgGzEF9Hkz0AHRHNcYFeBszxDe4YXi8DOiKa4wK9DJjjG9wxvF4GdEQ0xwX+A/WJ+l+1sXcwAAAAAElFTkSuQmCC" | |
[byte[]]$Bytes = [convert]::FromBase64String($Logo) | |
[System.IO.File]::WriteAllBytes($InfoLogo,$Bytes) | |
} | |
} | |
$lastResult = $true | |
# result of the last sent alerts | |
$lastConsecAlert = $true | |
$lastFailRateAlert = $true | |
$lastFlipRateAlert = $true | |
$consecCount = 0 | |
$failTimes = @() | |
$flipTimes = @() | |
$flipCountThld = [int](($FlipWindowMin * 60 / $Pause) * ($FlipRateThreshold / 100)) | |
$failCountThld = [int](($FailWindowMin * 60 / $Pause) * ($FailRateThreshold / 100)) | |
while ($true) { | |
$statusMessage = '' | |
Write-Verbose "Performing tests..." | |
# perform test | |
if ($TCPPort) { | |
$Result = $false | |
$tcpclient = [System.Net.Sockets.TcpClient]::new() | |
$tcpStartTime = [datetime]::Now | |
try { | |
$task = $tcpclient.ConnectAsync($RemoteHost, $TCPPort) | |
if ($task.Wait($TCPTimeoutMs) -and $tcpclient.Connected) { | |
$Result = $true | |
} | |
} | |
catch {} | |
finally { | |
$tcpclient.Close() | |
} | |
$tcpTripTime = ([datetime]::Now - $tcpStartTime).TotalMilliseconds | |
$statusMessage = "TCP $TCPPort Connection $(if ($Result) {'Succeeded'} else {'Failed'}) in $tcpTripTime ms" | |
} | |
elseif ($WMIProperty) { | |
if ($Pause -lt 30) { | |
Write-Host "A pause of only $($Pause) between WMI queries will strain the remote host. Adjusting to 30 seconds." | |
$Pause = 30 | |
} | |
$WMIResult = Get-WmiObject -Namespace $Namespace -Class $Class -ComputerName $RemoteHost | |
if ($WMIFilter) { | |
$WMIResult = $WMIResult.Where($WMIFilter) | |
} | |
if ($WMIResult.Count -gt "1") { | |
throw "Multiple WMI results found, check you're using a valid filter." | |
} elseif ($WMIResult.Count -eq "0") { | |
$WMIResult = "NotFound" | |
} else { | |
#If a single result found, update the result to its value | |
$WMIResult = $WMIResult | Select-Object -ExpandProperty $WMIProperty | |
} | |
$Result = $WMIValidator.InvokeReturnAsIs(@(,$WMIResult)) | |
$statusMessage = "$WMIProperty $(if ($Result) {'Success'} else {'Failure'}) with value $WMIResult" | |
} | |
else { | |
$Result = Test-Connection -Count 1 -ComputerName $RemoteHost -Quiet | |
$statusMessage = if ($Result) {'UP'} else {'DOWN'} | |
} | |
Write-Verbose "Test result: $Result." | |
# process test result | |
if ($Result -ne $lastResult) { | |
#status changed | |
$consecCount = 0 | |
if ($flipCountThld -gt 0) { $flipTimes += [datetime]::Now } | |
} | |
else { | |
# using -le instead of -lt can avoid sending alert every loop after reaching threshold without knowing $lastConsecAlert | |
if ($consecCount -le $ConsecThreshold) { | |
$consecCount += 1 | |
} | |
} | |
if ($failCountThld -gt 0 -and !$Result) { $failTimes += [datetime]::Now } | |
$lastResult = $Result | |
if ($consecCount -eq $ConsecThreshold -and $lastConsecAlert -ne $Result) { | |
Write-Verbose "Consecutive Threshold ($consecCount/$ConsecThreshold): Perform alert actions..." | |
Alert -Success $Result -Hostname $RemoteHost -StatusMsg $statusMessage | |
$lastConsecAlert = $Result | |
} | |
else { | |
Write-Verbose "Consecutive Threshold ($consecCount/$ConsecThreshold): Skip alert actions..." | |
} | |
if ($failCountThld -gt 0) { | |
$failTimes = $failTimes.Where{$_ -gt [datetime]::Now.AddMinutes(-$FailWindowMin)} | |
$failRateResult = $failTimes.Count -lt $failCountThld | |
if ($lastFailRateAlert -ne $failRateResult) { | |
Write-Verbose "Fail Threshold ($($failTimes.Count)/$failCountThld): Perform alert actions..." | |
Alert -Success $failRateResult -Hostname $RemoteHost -StatusMsg "Fail rate $(if ($failRateResult) {'under'} else {'over'}) $FailRateThreshold% in the last $FailWindowMin mins" | |
$lastFailRateAlert = $failRateResult | |
} | |
else { | |
Write-Verbose "Fail Threshold ($($failTimes.Count)/$failCountThld): Skip same alert actions..." | |
} | |
} | |
if ($flipCountThld -gt 0) { | |
$flipTimes = $flipTimes.Where{$_ -gt [datetime]::Now.AddMinutes(-$FlipWindowMin)} | |
$flipRateResult = $flipTimes.Count -lt $flipCountThld | |
if ($lastFlipRateAlert -ne $flipRateResult) { | |
Write-Verbose "Filp Threshold ($($flipTimes.Count)/$flipCountThld): Perform alert actions..." | |
Alert -Success $flipRateResult -Hostname $RemoteHost -StatusMsg "Flip rate $(if ($flipRateResult) {'under'} else {'over'}) $FlipRateThreshold% in the last $FlipWindowMin mins" | |
$lastFlipRateAlert = $flipRateResult | |
} | |
else { | |
Write-Verbose "Filp Threshold ($($flipTimes.Count)/$flipCountThld): Skip same alert actions..." | |
} | |
} | |
Write-Verbose "Waiting $Pause sec for next test..." | |
Write-Verbose "" | |
Start-Sleep -Seconds $Pause | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Added multiple metrics for alerting and lots of other improvements.