【windows用】typeperfコマンドで定期的に負荷状況を取得するPowerShell

# 用途
:::note info
windows用。
:::
な・ぜ・か、リソース取得やリソース負荷レポートを発行する文化のない環境なので、負荷試験をしてもリソース使用状況が見えない。
VM基盤なのでvShpere上からも確認は可能だが、内部の負荷状況を細かく確認する術もない為、ChatGPTに作成させたツール。
CPU使用率・メモリ使用率・使用帯域(bps)・ディスクIOを取得する。
PowerShellにして期間中実行するようにして取得する。
もっと他に取得方法はあるはずだがこれ以上windowsに肩入れする気はないので気になる方はご自身で調査してください。
ホントはPrometheus入れたかったけど時間にもリソースの空きもなかった。

# スクリプト
5秒に1度、typeperfコマンドを実行する。
時間は5秒に一回にしていますが、1秒単位のほうが他の装置の出力に合わせやすいと思う。
標準出力を「Ctrl + C」で停止するまでCSVに出力し続ける。
CSVはTドライブ直下出力されますので、必要に応じて変更してください。

詳細は下記の通り。
:::note alert
スクリプト内に自分が書き込んだ日本語はバグの元なので必ず削除してください。
:::

```powershell:Get-Resource_ver1.0.ps1
# Get the current host name
$hostName = $env:COMPUTERNAME

# Output the script start time at the beginning
$startTime = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
$timetag = Get-Date -Format "yyyyMMdd_HHmmss"

# Generate the log file name with the host name
$filename = "T:\STATUS_${hostName}_$timetag.log.csv" #ここを変更する。なんで指定したしホント。

# Write the script start time and host name at the beginning of the log file
"Script Start Time: $startTime" | Out-File -FilePath $filename -Encoding UTF8
"Host Name: $hostName" | Out-File -FilePath $filename -Append -Encoding UTF8

# Display the script start time and a message to the console
Write-Output "Script Start Time: $startTime"
Write-Output "Host Name: $hostName"
Write-Output "Collecting resource information. Please do not touch."

# Get the total physical memory of the system (in megabytes)
$totalMemory = (Get-CimInstance Win32_ComputerSystem).TotalPhysicalMemory / 1MB

# Define the performance counters to collect
$counters = @(
    '\Processor(_Total)\% Processor Time',
    '\Memory\Available MBytes',
    '\Network Interface(*)\Bytes Total/sec',
    '\PhysicalDisk(_Total)\Disk Write Bytes/sec',
    '\PhysicalDisk(_Total)\Disk Read Bytes/sec'
)

# Write the header line to the log file
"Timestamp, ProcessorTime(%), MemoryUsage(%), NetworkBitsTotalPerSec(bps), DiskWriteBytesPerSec(B/s), DiskReadBytesPerSec(B/s)" | Out-File -FilePath $filename -Append -Encoding UTF8

# Infinite loop to collect data
while ($true) {
    try {
        # Get the current timestamp
        $timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"

        # Execute typeperf and get the counter values
        $results = & typeperf $counters -sc 1 | Select-Object -Skip 2

        # Remove the last 2 lines of messages
        $values = $results[0] -split ',' | ForEach-Object { $_.Trim('"') } | Select-Object -Skip 1

        # Extract each value into variables
        $processorTime = [double]$values[0]
        $availableMemory = [double]$values[1]
        $networkBytesTotalPerSec = [double]$values[2]
        $diskWriteBytesPerSec = [double]$values[3]
        $diskReadBytesPerSec = [double]$values[4]

        # Calculate the memory usage percentage
        $memoryUsagePercent = 100 * ($totalMemory - $availableMemory) / $totalMemory

        # Convert network bytes to bits (B/s -> bps)
        $networkBitsTotalPerSec = $networkBytesTotalPerSec * 8

        # Form the log data line
        $logLine = "$timestamp, $processorTime, $memoryUsagePercent, $networkBitsTotalPerSec, $diskWriteBytesPerSec, $diskReadBytesPerSec"

        # Append the data line to the log file
        $logLine | Out-File -FilePath $filename -Append -Encoding UTF8

        # Wait for 5 seconds
        Start-Sleep -Seconds 5 #取得間隔はここを変更する。
    }
    catch {
        Write-Error "Error occurred: $_"
    }
}
```

# 生成物
下記のようなファイルが生成されます。
取得間隔は好きにいじってください。
ただしあくまでコマンド実行タイミングでの取得値となる為、平均値などは出ませんのでご注意を。
```csvs:STATUS_{HOST}_{YYYYMMDD}_{hh:mm:ss}.log.csv
Script Start Time: 2023-01-01 12:00:00
Host Name: MYCOMPUTER
Timestamp,ProcessorTime(%),MemoryUsage(%),NetworkBitsTotalPerSec(bps),DiskWriteBytesPerSec(B/s),DiskReadBytesPerSec(B/s)
2025-01-29 05:45:00, 20.791859, 23.7674797695841, 207107.239216, 4061.392871, 0
2025-01-29 06:33:10, -13.12766, 23.0167361607576, 421391.835384, 294985.391147, 0
2025-01-29 12:15:17, 18.086339, -22.9495964884235, 396430.972744, 0, 0
2025-01-29 15:45:25, 18.004937, 21.4771014020056, 206881.031816, -191272.005808, 0
2025-01-29 18:33:32, 24.907089, 21.6754686157199, 353744.958384, 1019291.818321, 0
2025-01-29 22:05:39, 28.413857,-20.9521912364846, 856663.587432, 828054.224741, 12237.254553
2025-01-30 03:45:46, 35.490145, 21.2146463192451, 1005378.77088, 593235.428625, 0
```

# 集計方法
1.エクセルにコンマベースで貼り付け。
2.エクセルでテキストからCSVを読み込み。
3.「Timestamp」列を選択し、「変換」タブの「データ型」を「日時」に設定。
4.「追加の列」タブで「カスタム列」をクリックし、新しいカスタム列を追加。
  名前を「RoundedTimestamp」に設定し、以下の数式を入力。
```:これは6時間で切り上げて平均を出すのに使用します。
let
    ts = [Timestamp],
    Hour = Number.RoundDown(Time.Hour(ts) / 6) * 6,
    NewTime = #datetime(Date.Year(ts), Date.Month(ts), Date.Day(ts), Hour, 0, 0)
in
    NewTime
```
:::note info
Time.Hour(ts) でタイムスタンプから時間部分を取得します。
Number.RoundDown(Time.Hour(ts) / 6) * 6 で6の倍数に丸めます。
#datetime関数を使用して、分と秒を0に設定し新しい日時を作成します。
次に進める手順は同様になります。
:::

5.「ホーム」タブで「条件付き列」をクリック。表示されるウィンドウで、以下の設定を行う。
　新しい列名: Processed ProcessorTime(%)
　条件1: ProcessorTime(%) は次の値より小さい 0
　出力列の値: 0
　それ以外の場合の値: ProcessorTime(%)
　他の列についても同様に条件付き列を作成。
 
6.最後に、「閉じて読み込む」ボタンをクリックして、ワークシートに結果をロード。

# 最後に
こんなスクリプトを入れなきゃいけないくらいなら、Prometheus+Grafana環境を構築したいけど、社内LANですらIIJのリポジトリに接続できないように制限されてるので辛い。