PowerShell Performance Counter
Performancecounter gibt es in Windows schon seit Windows NT 3.x und sie sind das Gegenstück zu SNMP auf Unix.
Performancecounter lesen
Seit PowerShell 2 gibt es nun auch ein "Get-Counter"-Commandlet, welches einen direkten lesenden Zugriff auf die Performance Counter erlaubt:
PS> (Get-Counter "\prozessor(_total)\prozessorzeit (%)").countersamples[0] Path InstanceName CookedValue ---- ------------ ----------- \\nawnbfc\prozessor(_to... _total 4,24059311090853
Natürlich kann man auch über das .NET-Framework auf Performancecounter zugreifen, was anscheinend auch schneller aber vor allem einfacher ist, als der WMI-Zugriff. Hier ein Muster auf einem englischen Server:
[PS] $perf = new-object System.Diagnostics.PerformanceCounter [PS] $perf.countername = "% Processor Time" [PS] $perf.Categoryname = "Processor" [PS] $perf.Instancename = "_Total" oder [PS] $perf = new-object System.Diagnostics.PerformanceCounter "Processor","% Processor Time", "_Total" [PS] $perf CategoryName : Processor CounterHelp : % Processor Time is the percentage of elapsed time that the processor spends to execute a non-Idle thread. It is calcula ted by measuring the duration of the idle thread is active i n the sample interval, and subtracting that time from interv al duration. (Each processor has an idle thread that consum es cycles when no other threads are ready to run). This coun ter is the primäry indicator of processor activity, and disp lays the average percentage of busy time observed during the sample interval. It is calculated by monitoring the time th at the service is inactive, and subtracting that value from 100%. CounterName : % Processor Time CounterType : Timer100NsInverse InstanceLifetime : Global InstanceName : _Total ReadOnly : True MachineName : . RawValue : 36131046250000 Site : Container :
Das ist ja noch einfach, so lange es genau ein Counter ist und die Namen als auch Instanzen gleich sind. Nun gibt es aber Performancecounter, die mehrere Instanzen haben, z.B.: die Exchange 2007 CCR Replikation. Da muss man sich dann selbst durch die Instanzen arbeiten.
[PS] $perfcat = new-object System.Diagnostics.PerformanceCounterCategory("MSExchange Replication") [PS] $perfcat.GetInstanceNames() | %{$perfcat.GetCounters($_)} | ft Cat*,countername,Instancename,rawvalue CategoryName CounterName InstanceName RawValue ------------ ----------- ------------ -------- MSExchange Repli... CopyNotification... _total 83317 MSExchange Repli... CopyGenerationNu... _total 83317 MSExchange Repli... InspectorGenerat... _total 83317 MSExchange Repli... ReplayNotificati... _total 83317 MSExchange Repli... ReplayGeneration... _total 83317 MSExchange Repli... ReplayQueueLength _total 0 MSExchange Repli... Suspended _total 0 MSExchange Repli... TrUNCatedGenerat... _total 0 MSExchange Repli... CopyNotification... s1 sg1 83317 MSExchange Repli... CopyGenerationNu... s1 sg1 83317 MSExchange Repli... InspectorGenerat... s1 sg1 83317 MSExchange Repli... ReplayNotificati... s1 sg1 83317 MSExchange Repli... ReplayGeneration... s1 sg1 83317 MSExchange Repli... ReplayQueueLength s1 sg1 0 MSExchange Repli... ReplayBatchSize s1 sg1 0 MSExchange Repli... CopyQueueLength s1 sg1 0
Aber auch das geht mit Get-Counter, man muss nur etwas geschickter die Abfrage stellen. Hier z.B. für die Anzahl der Handles aller Prozesse
(get-counter "\Process(*)\handle count" ).countersamples Path InstanceName CookedValue ---- ------------ ----------- \\w2k8r2e2010\process(idle)\handle c... idle 0 \\w2k8r2e2010\process(system)\handle... system 717 \\w2k8r2e2010\process(smss)\handle c... smss 32 \\w2k8r2e2010\process(csrss#2)\handl... csrss 72 \\w2k8r2e2010\process(csrss#1)\handl... csrss 293
Eine Filterung ist dann mit "Where" möglich oder sie suchen genau einen bestimmten Wert. Vielleicht hilft es auch einfach einmal alle Counter auf einem PC aufzulisten:
#Alle Counter listen
Get-Counter "\*\*"
# Alle Counter einer Gruppe mit allen Instanzen lauflisten
Get-Counter "\MSExchange Replication(*)\*"
# Alle Counter einer bekannten Instanz auslesen
Get-Counter "\MSExchange Replication(_total)\*
# einen einzelnen Counter
Get-Counter "\MSExchange Replication(_total)\log copy kb/sec"
Achtung: Der String des Counters ist "Case sensibel"
Performance Counter und Serversprache
Die Lokalisierung von Strings findet sich bei Windows leider auch bei den Performance Counter wieder. Daher muss die Abfrage des String sich immer auch an der Landessprache orientieren. Aber es gibt natürlich eine Möglichkeit, von dem englischen Namen auf den lokalen Namen zu schließen.
.. If the name of the objects and
counters is known only as an English string in the
application, there are some additional steps that you must
perform on a system that uses a localized language, such as
French or German.
Quelle: Using PDH APIs correctly in a localized language
https://support.microsoft.com/en-us/help/287159/using-pdh-apis-correctly-in-a-localized-language
Die Liste der Performance Counter in englischer Sprache findet sich in einem Registrierungsschlüssel:
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Perflib\009\Counter
Diese MultiLine String-Feld enthält alle Instanzen mit einer ID und dem String
Wenn ich also den englischen Namen kenne, kann ich mir so die "Nummer" des Counters holen und dann mit der Nummer und der "Current Language" mit den lokalen Text dazu ermitteln um dann letztlich den Counter zu lesen. Ich finde etwas schade, dass man nicht direkt einfach immer die englischen Beschreibungen nutzen kann. Aber so muss man eben einen kleinen Umweg machen.
function Get-LocalPerfCounterName { param ( [Parameter(Mandatory=$true)] $Name ) $key009 = 'Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Perflib\009' $counters009 = (Get-ItemProperty -Path $key009 -Name Counter).Counter.tolower() $Index = $counters009.IndexOf($name.tolower()) if ($index -eq -1) { $null # not found } else { $keylocal = 'Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Perflib\CurrentLanguage' $counterslocal = (Get-ItemProperty -Path $keylocal -Name Counter).Counter $counterslocal[$index] } }
Über die "ToLower"-Methode mache ich die Suche unabhängig von Groß/Kleinschreibung (Case). Bedenken sie aber, dass ein Performance Counter ja immer aus einem Counternamem und der Unterklasse besteht. Sie müssen die Funktion daher doppelt aufrufen. hier am Beispiel einer CPU
# Get CPU Load with local Perfmon names # Normal localized Version (get-counter "\prozessor(_total)\prozessorzeit (%)").countersamples.cookedValue # Get CPU Load using english names as source $counter="Processor" $Subcounter = "% Processor Time" $instance = "_Total" (get-counter "\$(get-LocalPerfCounterName $counter)($($instance))\$(get-LocalPerfCounterName $subcounter)").countersamples.cookedValue
Die eigentliche "Instanz" ist aber nicht lokalisiert. Wer also einen Exchange Information Store ausliest, der findet als Instanzname dann den Namen der Datenbank. Bei generischen Countern ist ein "_total" in der Regel passend oder die Nummer der Instanz
- Retrieving Counter Data
https://msdn.microsoft.com/en-us/library/aa373178(v=vs.85).aspx - Using the PDH Functions to Consume
Counter Data
https://msdn.microsoft.com/en-us/library/windows/desktop/aa373214(v=vs.85).aspx - Using PDH APIs correctly in a localized
language
https://support.microsoft.com/en-us/help/287159/using-pdh-apis-correctly-in-a-localized-language - Using the Registry Functions to Consume
Counter Data
https://msdn.microsoft.com/en-us/library/windows/desktop/aa373219(v=vs.85).aspx - Querying performance counters from
PowerShell
https://www.powershellmagazine.com/2013/07/19/querying-performance-counters-from-powershell/ - Access Windows Performance Counters in a
locale independent ways
https://stackoverflow.com/questions/11538299/access-windows-performance-counters-in-a-locale-independent-way - How-to force perfmon counters
description to be in english
https://www.zabbix.com/forum/showthread.php?t=7455
Nicht ganz sauberer Workaround, indem man die Regkeys kopiert
Performancecounter anlegen
Was viele Personen vermutlich seltener verwenden ist die Funktion, eigene Counter anzulegen.
Damit das PowerShell-Skript Performancecounter anlegen kann, muss es als Administrator gestartet werden
# Create performance Counter # # Achtung: als ADMIN starten, wenn man den Counter anlegen muss $categoryName = "MSXFAQCounters" if ([System.Diagnostics.PerformanceCounterCategory]::Exists($categoryName)) { write-host "Counter exists" } else { write-host "Create Counters" $counterData = new-object System.Diagnostics.CounterCreationDataCollection $counter = new-object System.Diagnostics.CounterCreationData $counter.CounterType = [System.Diagnostics.PerformanceCounterType]::AverageCount64 $counter.CounterName = "64bit Counter Name" $counterData.Add($counter) # Counter real anlegen [System.Diagnostics.PerformanceCounterCategory]::Create( ` $categoryName, $categoryName, ` [System.Diagnostics.PerformanceCounterCategoryType]::SingleInstance, $counterData) }
Natürlich gibt es noch viele andere Countertypen. In Perfmon sind diese Counter dann einfach einzusehen
Offen bleibt dann nur noch die Frage, wie man aus PowerShell auch Werte schreiben kann
Performancecounter schreiben
Auch das ist per PowerShell relativ einfach möglich.
$categoryName = "MSXFAQCounters" # Counter holen $Percounter64bit = New-Object System.Diagnostics.PerformanceCounter($categoryName,"64bit Counter Name",$false) # Aktueller Wert $Percounter64bit.rawValue # Wert ändern $Percounter64bit.rawValue = [int]8 #Neuer Wert $Percounter64bit.rawValue
Offen bleibt dann nur noch die Frage, wie man aus PowerShell auch Werte schreiben kann
Performancecounter löschen
Wenn Sie den Counter nicht mehr benötigen, dann sollten Sie diesen natürlich auch wieder korrekt entfernen
# Remove performance Counter # # Achtung: als ADMIN starten, wenn man den Counter loeschen will $categoryName = "MSXFAQCounters" if ([System.Diagnostics.PerformanceCounterCategory]::Exists($categoryName)) { [System.Diagnostics.PerformanceCounterCategory]::Delete($categoryName) }
Hinweis:
Mit diesen wenigen Zeilen können jeden Counter
ohne weitere Rückfrage entfernen !
Weitere Links
Auch hierzu finden sich im Internet umfangreiche Beschreibungen
- How to Read Performance Counters [Ryan Byington]
http://blogs.msdn.com/bclteam/archive/2006/06/02/618156.aspx - Beginning System.Diagnostics
http://www.codeproject.com/KB/dotnet/SystemDiagnostics_basics.aspx - Performance Counter Interface
http://www.informit.com/guides/content.aspx?g=dotnet&seqNum=247 - PerfmonCounter mit WPF und PowerShell
http://huddledmasses.org/wpf-from-PowerShell-updating-windows/ - V2 Quick Tip: Monitoring Performance Counters with PowerShell
http://blogs.msdn.com/b/PowerShell/archive/2009/04/21/v2-quick-tip-monitoring-performance-counters-with-PowerShell.aspx - Use Windows PowerShell to Monitor System Performance
http://technet.microsoft.com/en-us/magazine/ee872428.aspx - Windows Performance Counter Types
https://manski.net/2013/11/windows-performance-counter-types/ - Get-Counter
https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.diagnostics/get-counter?view=powershell-5.1