Wednesday, April 30, 2008

Powershell script to monitor MSMQ queues

Part of one of the products I support communicates with other products via Microsoft Message Queues (MSMQ). While we can monitor all of the various servers' CPU, memory, disk, network, etc for performance issues, one of the best determining factors is to see if a queue has suddenly grown. Since all queues should be either near zero or at a steady amount of messages depending on the time of day, any queue that shows an upward trend or sudden spike usually means there is an issue somewhere.

I usually run this at peak times of the day with a polling interval of ten seconds, whereas our monitoring system polls once every few minutes. This allows me to quickly see trends that may need attention. I also run it after performing maintenance just to verify the traffic patterns.

This script will not only query and display the most populated queues, but will also keep a count of the previous seven iterations to show an upward or downward trend. See below:
(Queue names were changed post-screenshot, sorry for the sloppiness)

Pre-Requisites:
  • Windows 2003 (not tested on 2000 or 2008)
  • Powershell 1.0
  • Rights to perform perfmon queries against the MSMQ server
  • Rights to enumerate the messages in all of the MSMQ queues
  • This has only been tested against "Private Queues"

What this script does:
  1. It reads in its .config files to determine the MSMQ server to contact, the important queues (if any) to flag, the top number of queues to poll, and the polling interval
  2. It then connects to the target server and queries for the largest X queues (defined in the config file)
  3. It steps through each queue and if its name matches an "important queue" it flags it with an asterisk
  4. Assigns the queue's name and count to a custom object
  5. Prints the custom object in table form
  6. On the next iteration, it sets the previous values to the custom object's "PriorN" column, keeping the previous 7 total.

Script files:

Steps:
  1. Download the code
  2. Extract it to a target folder
  3. Edit the .config file to set the proper values
  4. Execute it

For reference, here is the script code:
$Version="v8.4.28 Aaron Dodd"
$Description="Query MSMQ server for top X queues"

#------------------------------------------------------------------------------
# Settings / Variables
#------------------------------------------------------------------------------

$cfg=[xml](get-content QueryQueues.config)

$Computer = $cfg.configuration.QueueServer.name
$ImportantQueues = $cfg.configuration.ImportantQueues
$MaxQueues = $cfg.configuration.MaxQueues.value
$PollInterval = $cfg.configuration.PollInterval.value

# Hashes to store the values (queuename, messagescount)
$Current=@{}
$Previous1=@{}
$Previous2=@{}
$Previous3=@{}
$Previous4=@{}
$Previous5=@{}
$Previous6=@{}
$Previous7=@{}

# Make an array of important queues
$ImpQueues=@()
ForEach ($Queue in $ImportantQueues.Queue) {
$ImpQueues+=$Queue.name
}
#------------------------------------------------------------------------------

#------------------------------------------------------------------------------
# Continously poll the MSMQ server, report results
#------------------------------------------------------------------------------
For () {

clear

$Queues= Get-WmiObject Win32_PerfFormattedData_MSMQ_MSMQQueue -computer $computer | Sort-Object -property "MessagesinQueue" -descending | Select-Object -first $MaxQueues

# Update the historical queue hashes, clear the current hash
$Previous7=$Previous6
$Previous6=$Previous5
$Previous5=$Previous4
$Previous4=$Previous3
$Previous3=$Previous2
$Previous2=$Previous1
$Previous1=$Current
$Current=@{}

# Repeat for "important queues"

#Collection to store the object of values to report on
$ColReport = @()

ForEach ($Queue in $Queues) {
$QueueName = $Queue.Name.Split('\')[2]
# If the $QueueName is an "important" queue, we'll prepend it with "* " for readability
If ($ImpQueues -contains $QueueName) {$QueueName = "* " + $QueueName}
$Current.Add($QueueName,$Queue.MessagesInQueue)
}
ForEach ($Entry in $Current.GetEnumerator()) {
$Name = $Entry.Name
$Curr = $Entry.Value

If ($Previous1.ContainsKey($Name)) {
$Prior1 = $Previous1.Get_Item($Name)
} Else {
$Prior1 = "-"
}
If ($Previous2.ContainsKey($Name)) {
$Prior2 = $Previous2.Get_Item($Name)
} Else {
$Prior2 = "-"
}
If ($Previous3.ContainsKey($Name)) {
$Prior3 = $Previous3.Get_Item($Name)
} Else {
$Prior3 = "-"
}
If ($Previous4.ContainsKey($Name)) {
$Prior4 = $Previous4.Get_Item($Name)
} Else {
$Prior4 = "-"
}
If ($Previous5.ContainsKey($Name)) {
$Prior5 = $Previous5.Get_Item($Name)
} Else {
$Prior5 = "-"
}
If ($Previous6.ContainsKey($Name)) {
$Prior6 = $Previous6.Get_Item($Name)
} Else {
$Prior6 = "-"
}
If ($Previous7.ContainsKey($Name)) {
$Prior7 = $Previous7.Get_Item($Name)
} Else {
$Prior7 = "-"
}

$ObjReport = New-Object System.Object
$ObjReport | Add-Member -type NoteProperty -name Queue -value $Name
$ObjReport | Add-Member -type NoteProperty -name Curr -value $Curr
$ObjReport | Add-Member -type NoteProperty -name Prior1 -value $Prior1
$ObjReport | Add-Member -type NoteProperty -name Prior2 -value $Prior2
$ObjReport | Add-Member -type NoteProperty -name Prior3 -value $Prior3
$ObjReport | Add-Member -type NoteProperty -name Prior4 -value $Prior4
$ObjReport | Add-Member -type NoteProperty -name Prior5 -value $Prior5
$ObjReport | Add-Member -type NoteProperty -name Prior6 -value $Prior6
$ObjReport | Add-Member -type NoteProperty -name Prior7 -value $Prior7
$ColReport += $ObjReport

}

$ColReport | Sort-Object Curr -descending | Format-Table -auto

$timestamp = Get-Date
Write-Host Last Updated: $timestamp
Start-Sleep -seconds $PollInterval
}
#------------------------------------------------------------------------------


Future enhancements:
  • If someone can help me out with how, I'd like to have the "important queues" show up with a different background color instead of simply renaming it to prepend an asterisk
  • I'd love to incorporate the ability to press a key to force a new loop instead of having to wait the polling interval, for times when I want to quickly see queue changes

3 comments:

Anonymous said...

I cannot download the zip file... did something change?

Aaron Dodd said...

My apologies for the missing attachments. It was due to a misconfigured .htaccess file. I've fixed it and the script should be available again.

Anonymous said...

I'd like to play around with this script some, but the download no longer works. It looks like aarondodd.com isn't around any more. Any chance you could post the config file or re-host the zip?