かつて山市良と呼ばれたおじさんのブログ

セイテクエンジニアのブログ  かつて山市良と呼ばれたおじさんのブログ  vol.205 PowerShellで自在に!-パフォーマンスカウンターとの付き合い方(3)|セイテク・シス管道場(Web)

 

 

vol.205 PowerShellで自在に!-パフォーマンスカウンターとの付き合い方(3)|セイテク・シス管道場(Web)

2026年05月28日配信
2026年05月28日更新
執筆者:山内 和朗

 「セイテク・シス管道場(Web)」では、Windows Serverの要素技術やシステム管理の基本的な部分に焦点を当てたいと思います。引き続き、パフォーマンスカウンターについて深掘りします。今回はパフォーマンスモニターのデータコレクターセットとPowerShellのコラボです。

 

データコレクターセットのログはPowerShellで自由自在!

 Windowsパフォーマンスカウンターは、PowerShellのGet-Counterコマンドレットで瞬間値(-SampleIntervalや-MaxSampleを指定しない場合)を取得でき、Import-Counterコマンドレットでパフォーマンスモニターのデータコレクターセットのログファイル(バイナリ《.blg》、カンマ区切り《.csv》、タブ区切り《.tsv》)ファイルからパフォーマンスデータをインポートすることができます。バイナリファイルの場合は、最大32個のログを同時に読み込むことが可能です。

Get-Counter|PowerShell(Microsoft Learn)
Import-Counter|PowerShell(Microsoft Learn)
※Get-CounterはWindows PowerShell 5.1とPowerShell 7.xの両方で同じように利用できることを確認しましたが、Import-CounterはPowerShell 7.xでは期待通りに機能しない可能性があります。

 前回は、サーバーマネージャーの「パフォーマンス」を有効にしている場合に使用される「Server Manager Performance Monitor」データコレクターセットをベースライン把握に流用できるとお話しました。グラフ表示やレポートを目で見て分析することもできますが、Import-Counterを使用すれば数値として詳細に分析できます。

 例えば、サーバーマネージャーの「パフォーマンス」が有効なシステムで次のコードを実行すると、最新の2つのログをインポートし、過去24時間の1時間ごとのCPU使用率の平均(Avg)、最大(Max)、最小(Min)を一覧で表示させることができます。さらに、各値の24時間全体の平均の計算も簡単です(画面1)。2つのログをインポートしているのは、「Server Manager Performance Monitor」データコレクターセットが24時間ごとにログを切り替えるため、過去24時間のパフォーマンスデータが2つのログに分かれる可能性を考慮してのことです。

 
画面1 サーバーマネージャーの「パフォーマンス」で使用されているデータコレクターセットのログをインポートし、過去24時間の1時間ごとのCPU使用率の平均、最大、最小と、24時間平均を取得
画面1 サーバーマネージャーの「パフォーマンス」で使用されているデータコレクターセットのログをインポートし、過去24時間の1時間ごとのCPU使用率の平均、最大、最小と、24時間平均を取得

 

$logs = Get-ChildItem "C:\PerfLogs\Admin\ServerManager\*.blg" |
       Sort-Object LastWriteTime -Descending |
       Select-Object -First 2
$since = (Get-Date).AddHours(-24)
$counters = Import-Counter -Path $logs.FullName -ErrorAction SilentlyContinue | Where-Object { $_.TimeStamp -ge $since} | Select-Object -Expand CounterSamples
$cpuusage = $counters | Where-Object { $_.Path -like "*Processor(_Total)\% Processor Time*"}
$result = $cpuusage | Group-Object {
    $_.Timestamp.ToString("yyyy-MM-dd HH:")
} | ForEach-Object {
    $values = $_.Group.CookedValue
    $avg = ($values | Measure-Object -Average).Average
    $max = ($values | Measure-Object -Maximum).Maximum
    $min = ($values | Measure-Object -Minimum).Minimum
    [PSCustomObject]@{
        Hour = $_.Name
        Avg  = [math]::Round($avg,2)
        Max  = [math]::Round($max,2)
        Min  = [math]::Round($min,2)
    }
}
$result | Sort-Object Hour| Format-Table Hour, Avg, Max, Min -AutoSize

※注 Import-Counterは、Windows PowerShell 5.1(powershell.exe)とPowerShell 7.x(pwsh.exe)では挙動が異なるようです。今回紹介するコードは、すべてWindows PowerShell 5.1を前提としています。PowerShell 7.xでは期待した結果を得られません。 

 

 次のPowerShellのコードは、上記のコードにいくつかのエラー処理を加え、24時間以外の期間を指定できるようにしたものです。上記のコードでは過去24時間のデータが存在する可能性がある2つのログをインポートしていますが、次のコードではログのタイムスタンプ(LastWriteTime)に基づいて読み込むログを決定しています。なお、期間を長くすればするほど処理対象のパフォーマンスデータが膨大になるため、実行に時間がかかることに留意してください。

 

[プレーンテキストで表示、ダウンロード]

$logpath = Get-ChildItem "C:\PerfLogs\Admin\ServerManager\*.blg"
$intervalhr = 24
$since = (Get-Date).AddHours(-1*$intervalhr)
$logs = Get-ChildItem $logpath |
    Where-Object { $_.LastWriteTime -ge $since } |
    Sort-Object LastWriteTime -Descending
$counters = @()
foreach ($log in $logs) {
  $ret = $null
  if((get-item($log)).Length -gt 131072) {
    $ret = Import-Counter -Path $log.FullName -ErrorAction SilentlyContinue | Where-Object { $_.TimeStamp -ge $since } | Select-Object -Expand CounterSamples
    if ($ret -and $ret.Count -gt 0) {
      $counters += $ret
    }
  }
}
if ($counters) {
  $cpuusage = $counters | Where-Object { $_.Path -like "*Processor(_Total)\% Processor Time*"}
$result = $cpuusage | Group-Object {
    $_.Timestamp.ToString("yyyy-MM-dd HH:")
  } | ForEach-Object {

    $values = $_.Group.CookedValue
    $avg = ($values | Measure-Object -Average).Average
    $max = ($values | Measure-Object -Maximum).Maximum
    $min = ($values | Measure-Object -Minimum).Minimum
    [PSCustomObject]@{
        Hour = $_.Name
        Avg  = [math]::Round($avg,2)
        Max  = [math]::Round($max,2)
        Min  = [math]::Round($min,2)
    }
  }
  $result | Sort-Object Hour| Format-Table Hour, Avg, Max, Min -AutoSize
  [PSCustomObject]@{
    Avg_Avg = [math]::Round(($result | Measure-Object Avg -Average).Average,2)
    Max_Avg = [math]::Round(($result | Measure-Object Max -Average).Average,2)
    Min_Avg = [math]::Round(($result | Measure-Object Min -Average).Average,2)
  }
} else {
    Write-Host "No performance data in logs."
}

 

簡単な例でImport-Counterの使い方を学ぼう

 

 短いサンプリング間隔(この例では15秒)で「¥Processor Information(_Total)¥% Processor Time」と「¥Memory¥Available MBytes」を含むいくつかのパフォーマンスカウンターを対象とした「CounterForBaseline」という名前のデータコレクターセットを作成しました(画面2)。このログは24時間ごとに新しいログに切り替わるように、データコレクターセットのプロパティの「停止条件」タブで設定してあります(✓ 制限に達したらデータコレクターセットを再開する 期間1日)。ログファイルは「C:¥PerfLogs¥Admin¥CounterForBaseline」の下にバイナリ形式(ファイル名-NNNNNN.blg)で格納するように設定してあります。

 

画面2 このデータコレクターセットの実行中のログをImport-Counterを使用してインポートする

画面2 このデータコレクターセットの実行中のログをImport-Counterを使用してインポートする

 

 次のコードをPowerShell(管理者権限は不要)で実行すると、最新のログファイルをインポートし、過去1時間のCPU使用率(¥Processor Information(_Total)¥% Processor Time )と利用可能メモリ(¥Memory¥Available MBytes)のパフォーマンスデータをそれぞれ集計して、平均、最大、最小値を表示します(画面3)。

 

$logpath = "C:\PerfLogs\Admin\CounterForBaseline\*.blg"
$log = Get-ChildItem $logpath | 
    Sort-Object LastWriteTime -Descending |
    Select-Object -First 1
$since = (Get-Date).AddHours(-1)
$counters = Import-Counter -path $log.FullName -ErrorAction SilentlyContinue | Where-Object { $_.TimeStamp -ge $since } | Select-Object -Expand CounterSamples

$perfcounter = "*Processor Information(_Total)\% Processor Time*"
$result = $counters | Where-Object Path -like $perfcounter |Measure-Object -Property CookedValue -Average -Maximum -Minimum
"CPU AVG: {0:N2} % MAX: {1:N2} % MIN: {2:N2} %" -f $result.Average,$result.Maximum,$result.Minimum

$perfcounter = "*Memory\Available MBytes*"
$result = $counters | Where-Object Path -like $perfcounter |Measure-Object -Property CookedValue -Average -Maximum -Minimum
"MEM AVG: {0:N2} mb MAX: {1:N2} mb MIN: {2:N2} mb" -f $result.Average,$result.Maximum,$result.Minimum

 

vol205_scr03

画面3 ユーザー定義のデータコレクターセットの最新のログをインポートし、CPU使用率とメモリ使用率の過去1時間の平均、最大、最小値を計算する

 

 なお、データコレクターセットの開始直後で有効なサンプルデータがまだ存在しない場合や、ログファイルが排他的に開かれている場合*1、「Import-Counter : 有効なカウンター パスがファイル内に見つかりません。」というエラーが表示される場合があります。前者の場合は、1時間以上待ってから再実行してください。後者の場合は、PowerShellのコードを実行する前後に次のコマンドラインを実行してください。
*1 データコレクターセットの設定に依存するようです。パフォーマンスカウンターのGUIでパフォーマンスカウンターのデータコレクターセットを手動で作成(手動で作成する > データログを作成する: パフォーマンスカウンター...)した場合はデータコレクターセット実行中でもImport-Counterでインポートすることができましたが、logman createコマンドで作成した場合はデータコレクターセットを停止しない限り、このエラーが表示されました。この記事の最後におまけとして紹介したdcs_HostLog.xmlでインポートしたデータコレクターセットは停止しなくてもログを読み取ることができます。

logman stop CounterForBaseline
(PowerShellコードの実行)
logman start CounterForBaseline

 

 上記のPowerShellのコードを少し解説します。Import-Counterは、TimestampとCounterSamplesを返し、CounterSamplesには複数のカウンターパス(Path)、値(CookedValue)、サンプル取得時間(Timestamp)が入っています(画面4)。

 

画面4 Import-Counterによってログファイルからインポートされたパフォーマンスデータは、TimestampとCounterSamplesプロパティからなる
画面4 Import-Counterによってログファイルからインポートされたパフォーマンスデータは、TimestampとCounterSamplesプロパティからなる

 インポート時の| Where-Object { $_.TimeStamp -ge $since } 条件により、過去1時間のデータがすべて読み込まれるはずです。さらに| Select-Object -ExpandProperty CounterSamples*2 することでCounterSamplesの中身を取り出し、$counters変数に格納します。
*2 PowerShell 7.xでは-Expand CounterSamplesが期待通りに機能しませんでした。

 この時点で$counters変数には、サンプル取得時間(TimeStamp)ごとのすべてのパフォーマンスカウンター(Path 、¥¥コンピューター名¥カウンターオブジェクト(インスタンス)¥カウンター)とインスタンス(InstanceName、ある場合)、カウンターの値(CoockedValue)が格納された状態になります。ここから目的のパフォーマンスカウンター、つまりPathに"*Processor Information(_Total)¥% Processor Time*"や"*Memory¥Available MBytes*"(大文字小文字は区別しません)を含むものだけを抽出し、最後に値(CookedValue)の平均、最大、最小値を計算します(画面5)。この例では過去1時間の有効なサンプル数218(サンプル間隔が15秒だと最大240)から結果を得ることができました。

 

画面5 $counters変数を目的のパフォーマンスカウンターだけに絞り込み、値(CookedValue)の平均、最大、最小値を算出
画面5 $counters変数を目的のパフォーマンスカウンターだけに絞り込み、値(CookedValue)の平均、最大、最小値を算出

 次のPowerShellのコードは、上記のコードにいくつかのエラー処理を加え、$logpathと$perfcounter、$intervalhrを変更することで、汎用的に利用できるようにしたものです。$logpathのと$perfcounter の初期値はServer Manager Performance Monitorデータコレクターセットのログファイルの「¥Processor(_Total)¥% Processor Time」になっています。

 

 [プレーンテキストで表示、ダウンロード] 

$logpath = "C:\PerfLogs\Admin\ServerManager\*.blg"
$perfcounter = "*Processor(_Total)\% Processor Time*"
#$perfcounter = "*Memory\Available KBytes*"
$intervalhr = 1

$since = (Get-Date).AddHours(-1*$intervalhr)
$logs = Get-ChildItem $logpath |
    Where-Object { $_.LastWriteTime -ge $since } |
    Sort-Object LastWriteTime -Descending

$counters = @()
foreach ($log in $logs) {
  $ret = $null
  if((get-item($log)).Length -gt 131072) {
    $ret = Import-Counter -Path $log.FullName -ErrorAction SilentlyContinue | Where-Object { $_.TimeStamp -ge $since } | Select-Object -Expand CounterSamples
    if ($ret -and $ret.Count -gt 0) {
      $counters += $ret
    }
  }
}
if ($counters) {
  $usage = $counters | Where-Object Path -like $perfcounter
  if($usage) {
    $result = $usage |Measure-Object -Property CookedValue -Average -Maximum -Minimum
    "AVG: {0:N2} MAX: {1:N2} MIN: {2:N2}" -f $result.Average,$result.Maximum,$result.Minimum
    #return $result.Average
  } else {
    Write-Host "No performance data in logs."
  }
} else {
  Write-Host "No performance data in logs."
}

 

応用編: データコレクターセットを使えば、長い監視間隔でも精度の高い監視が可能に

 

 ネットワーク経由で監視する監視ツールは、監視間隔を短くすると精度の高い監視が可能になりますが、一方でネットワーク負荷や監視ツールのシステム負荷が問題になることがあります。データコレクターセットのログを利用した今回の例を組み合わせれば、監視ツールで長い監視間隔(例えば1時間ごと)で取得できない間のアクティビティを、データコレクターセットの短いサンプリング間隔で補完することができるでしょう(画面6)。通常の監視ツールであれば、PowerShellなどの外部プログラムを実行する機能を備えているはずです。

 

画面6 弊社製品BOM for Windowsでの実装例。標準の監視項目「プロセッサ監視」を使用して1時間ごとに監視しても瞬間値しかわからないが、データコレクターセットのログと組み合わせれば、同じ1時間ごとでも精度の高い(30秒、5分など)サンプリングデータの平均値で効果的に監視できる
画面6 弊社製品BOM for Windowsでの実装例。標準の監視項目「プロセッサ監視」を使用して1時間ごとに監視しても瞬間値しかわからないが、データコレクターセットのログと組み合わせれば、同じ1時間ごとでも精度の高い(30秒、5分など)サンプリングデータの平均値で効果的に監視できる

 

おまけ 

 vol.203で紹介した一般的なパフォーマンスカウンター(最新版)を取得するデータコレクターセット「HostLog」のテンプレートファイル(dcstmp_HostLog.xml)と集計用PowerShellスクリプト(sample03.ps1)を用意しました。次のように、Logmanコマンドを使用してインポートし、ログの記録を開始できます。 ログの記録は直ちに始まりますが、バッファーがディスクに書き込まれるまでしばらく時間がかかります。最後のスケジュール設定は、システム起動時にデータコレクターセットを自動開始するための設定です。

logman import -name "HostLog" -xml .\dcs_HostLog.xml
logman start "HostLog" 
Set-ScheduledTask -TaskPath "\Microsoft\Windows\PLA\" -TaskName "HostLog" `
  -Trigger (New-ScheduledTaskTrigger -AtStartup)

 このデータコレクターセット「HostLog」は、各パフォーマンスカウンターを15秒間隔でログファイル「C:¥perflogs¥Admin¥HostLog¥DataCollector01NNNNNN.blg」(NNNNNNは連番)に記録し、24時間(86395秒)ごとに停止して新しいログで再開し、各ログは30日後に削除するように、データマネージャーで設定してあります。 集計用PowerShellスクリプト(sample03.ps1)は、最大過去24時間(ログがある場合)、パフォーマンスカウンターごとに、1時間ごとインスタンスごと(Hour)の平均(Avg)、最大(Max)、最小値(Min)を一覧表示します(画面7)。

(一般的なパフォーマンスカウンターを15秒間隔でログに記録するデータコレクターセット「HostLog」と、集計用PowerShellスクリプト) 

(一般的なパフォーマンスカウンターを15秒間隔でログに記録するデータコレクターセット「HostLog」と、集計用PowerShellスクリプト) 

 セイテク・シス管道場(Web) (1) (2)(3)(4)(5)(6)(7)(8)(9)(10)(11)(12)|(13)  

blog_yamanxworld_subscribe

blog_yamanxworld_comment

blog_yamanxworld_WP_ws2025

最新記事