Get-PerfGraphs

Perfmon is a powerfull tool, for sysadmins… But to provide nice (and especially readable for them!) graphs for your managers, here’s a PowerShell script based on WMI and using the great JavaScript Charting Framework Highcharts.

The ‘Get-PerfGraphs.ps1’ script

<#
.SYNOPSIS
	Get-PerfGraphs - Performance Graphing tool
.DESCRIPTION
    Get-PerfGraphs - Performance Graphing tool
    Generate performance graphs in a HTML file
    Default parameters are : pickup values each 5 seconds, duration of 10 minutes, on localhost,
    for CurrentConnections counter on the "Win32_PerfFormattedData_W3SVC_WebService\_Total" Class
.NOTES
    File Name  : Get-PerfGraphs.ps1 
    Author     : Fabrice ZERROUKI - fabricezerrouki@hotmail.com
#>
function isNumeric ($x) {
    try {
        0 + $x | Out-Null
        return $true
    } catch {
        return $false
    }
}

$GraphDate=(Get-Date).ToString('ddMMyyyy')
# Configuration file checking
Get-Content "Get-PerfGraphs.ini" | ForEach-Object -begin {$settings=@{}} -process { $store=[regex]::split($_,'='); if(($store[0].CompareTo("") -ne 0) -and ($store[0].StartsWith("[") -ne $True) -and ($store[0].StartsWith("#") -ne $True)) { $settings.Add($store[0], $store[1]) } }
Write-Host "Checking 'Get-PerfGraphs.ini' parameters... `n" -ForegroundColor DarkGreen;
$ComputerName=$settings.Get_Item("ComputerName")
$Website=$settings.Get_Item("Website")
[int]$Duration=$settings.Get_Item("Duration")
[int]$Interval=$settings.Get_Item("Interval")
$ASPNet=$settings.Get_Item("ASPNet")

if (Test-Connection -ComputerName $ComputerName -Quiet -Count 1)
{ "Computer name seems to be fine." ; Write-Host "Target computer: $ComputerName" -ForegroundColor DarkCyan; }
else { Write-Host "Something gone wrong. Please check the config file is rightly filled for the [ComputerName] parameter." -ForegroundColor DarkRed ; Write-Host "$ComputerName didn't responds to ping" -ForegroundColor DarkRed ; exit }
if ($Website -ne $null) {
$wql=Get-WmiObject -Class Win32_PerfFormattedData_W3SVC_WebService -ComputerName $ComputerName | Select Name | Format-Wide | Out-String
if ($wql | Select-String -Pattern "$Website" –CaseSensitive)
{ "The specified website seems to be fine." ; Write-Host "`"$Website`" exists." -ForegroundColor DarkCyan; }
else { Write-Host "Something gone wrong. Please check the config file is rightly filled for the [Website] parameter." -ForegroundColor DarkRed ; Write-Host "There no $Website hosted on $ComputerName" -ForegroundColor DarkRed ; exit }
}
if ((isNumeric($Duration)) -and ($Duration -le 43200))
{ "Duration seems to be fine." ; Write-Host "Duration: $Duration seconds" -ForegroundColor DarkCyan; }
else { Write-Host "Something gone wrong. Please check the config file is rightly filled for the [Duration] parameter. Numeric values only. Maximum value: 43200" -ForegroundColor DarkRed ; exit }
if ((isNumeric($Interval)) -and ($Interval -le 60))
{ "Interval seems to be fine." ; Write-Host "Interval: $Interval seconds" -ForegroundColor DarkCyan; }
else { Write-Host "Something gone wrong. Please check the config file is rightly filled for the [Interval] parameter. Numeric values only. Maximum value: 60" -ForegroundColor DarkRed ; exit }
if (($ASPNet -eq 'YES') -or ($ASPNet -eq 'NO'))
{ "ASPNet parameter seems to be fine." ; Write-Host "ASPNet: $ASPNet" -ForegroundColor DarkCyan; }
else { Write-Host "Something gone wrong. Please check the config file is rightly filled for the [ASPNet] parameter. 'YES' or 'NO' only." -ForegroundColor DarkRed ; exit }
Write-Host "`nAll parameters from 'Get-PerfGraphs.ini' file are OK." -ForegroundColor DarkGreen;
Write-Host "Now, we can do the job for you asshole... `n" -ForegroundColor DarkGreen;

if (($Duration -gt 240) -or ($Interval -lt 5)) {
$labelformat=@"
formatter: function(){
if (this.isFirst || this.isLast)
return this.value;
else
return '';
},
rotation: -45,
align: 'right'
"@
$plot=@"
lineWidth: 1,
    marker: {
        enabled: false,
        symbol: 'circle',
        radius: 2,
            states: {
                hover: {
                    enabled: true
                    }
                }
            }
"@
}
else {
$labelformat=@"
rotation: -45,
align: 'right'
"@
$plot=@"
lineWidth: 1
"@
}
$i=0

$Cat="categories: ["
$SerieCurCon="data: ["
$SerieCurAnon="data: ["
$SerieCurNonAnon="data: ["
$SeriePercentProc="data: ["
$SerieAvailableMB="data: ["
$SerieBytesSent="data: ["
$SerieBytesReceived="data: ["
$SerieCopyRequestsPerSec="data: ["
$SerieDeleteRequestsPerSec="data: ["
$SerieGetRequestsPerSec="data: ["
$SerieHeadRequestsPerSec="data: ["
$SerieLockRequestsPerSec="data: ["
$SerieMkcolRequestsPerSec="data: ["
$SerieMoveRequestsPerSec="data: ["
$SerieOptionsRequestsPerSec="data: ["
$SerieOtherRequestMethodsPerSec="data: ["
$SeriePostRequestsPerSec="data: ["
$SeriePropfindRequestsPerSec="data: ["
$SerieProppatchRequestsPerSec="data: ["
$SeriePutRequestsPerSec="data: ["
$SerieSearchRequestsPerSec="data: ["
$SerieTraceRequestsPerSec="data: ["
$SerieUnlockRequestsPerSec="data: ["
$SerieContextSw="data: ["
$SerieProcesses="data: ["
$SerieThreads="data: ["
$SerieAvgDiskQL="data: ["
$SerieDiskReadsPerSec="data: ["
$SerieDiskWritesPerSec="data: ["
if ($ASPNet -eq 'YES') {
$SerieApplicationRestarts="data: ["
$SerieApplicationsRunning="data: ["
$SerieRequestExecutionTime="data: ["
$SerieRequestsCurrent="data: ["
$SerieRequestsQueued="data: ["
$SerieWorkerProcessRestarts="data: ["
$SerieASPNetExpThrown="data: ["
$SerieASPNetCLRLoadAssem="data: ["
$SerieASPNetCLRLoadClass="data: ["
}

$StartDate=Get-Date -format G
Start-Sleep 3
Clear-Host

Do{
$WebserviceRequest=Get-WmiObject -Class Win32_PerfFormattedData_W3SVC_WebService -ComputerName $ComputerName `
 | Where {$_.Name -eq "$Website"} -ErrorAction SilentlyContinue
$WebsiteName=$WebserviceRequest | % {$_.Name}
$CurCon=$WebserviceRequest | % {$_.CurrentConnections}
$CurAnon=$WebserviceRequest | % {$_.CurrentAnonymousUsers}
$CurNonAnon=$WebserviceRequest | % {$_.CurrentNonAnonymousUsers}
$BytesSent=$WebserviceRequest | % {$_.BytesSentPerSec}
$KBSent=[math]::Round($BytesSent/1024, 2)
$BytesReceived=$WebserviceRequest | % {$_.BytesReceivedPerSec}
$KBReceived=[math]::Round($BytesReceived/1024, 2)
$CopyRequestsPerSec=$WebserviceRequest | % {$_.CopyRequestsPerSec}
$DeleteRequestsPerSec=$WebserviceRequest | % {$_.DeleteRequestsPerSec}
$GetRequestsPerSec=$WebserviceRequest | % {$_.GetRequestsPerSec}
$HeadRequestsPerSec=$WebserviceRequest | % {$_.HeadRequestsPerSec}
$LockRequestsPerSec=$WebserviceRequest | % {$_.LockRequestsPerSec}
$MkcolRequestsPerSec=$WebserviceRequest | % {$_.MkcolRequestsPerSec}
$MoveRequestsPerSec=$WebserviceRequest | % {$_.MoveRequestsPerSec}
$OptionsRequestsPerSec=$WebserviceRequest | % {$_.OptionsRequestsPerSec}
$OtherRequestMethodsPerSec=$WebserviceRequest | % {$_.OtherRequestMethodsPerSec}
$PostRequestsPerSec=$WebserviceRequest | % {$_.PostRequestsPerSec}
$PropfindRequestsPerSec=$WebserviceRequest | % {$_.PropfindRequestsPerSec}
$ProppatchRequestsPerSec=$WebserviceRequest | % {$_.ProppatchRequestsPerSec}
$PutRequestsPerSec=$WebserviceRequest | % {$_.PutRequestsPerSec}
$SearchRequestsPerSec=$WebserviceRequest | % {$_.SearchRequestsPerSec}
$TraceRequestsPerSec=$WebserviceRequest | % {$_.TraceRequestsPerSec}
$UnlockRequestsPerSec=$WebserviceRequest | % {$_.UnlockRequestsPerSec}
$PercentProc=Get-WmiObject -Class Win32_PerfFormattedData_PerfOS_Processor -ComputerName $ComputerName | Where {$_.Name -eq "_Total"} | % {$_.PercentProcessorTime} -ErrorAction SilentlyContinue
$AvailableMB=Get-WmiObject -Class Win32_PerfFormattedData_PerfOS_Memory -ComputerName $ComputerName | % {$_.AvailableMBytes} -ErrorAction SilentlyContinue
$SystemStats=Get-WmiObject -Class Win32_PerfFormattedData_PerfOS_System -ComputerName $ComputerName -ErrorAction SilentlyContinue
$ContextSw=$SystemStats | % {$_.ContextSwitchesPerSec}
$Processes=$SystemStats | % {$_.Processes}
$Threads=$SystemStats | % {$_.Threads}
$DiskStats=Get-WmiObject -Class Win32_PerfFormattedData_PerfDisk_PhysicalDisk -ComputerName $ComputerName | Where {$_.Name -eq "_Total"} -ErrorAction SilentlyContinue
$AvgDiskQL=$DiskStats | % {$_.AvgDiskQueueLength}
$DiskReadsPerSec=$DiskStats | % {$_.DiskReadsPerSec}
$DiskWritesPerSec=$DiskStats | % {$_.DiskWritesPerSec}
if ($ASPNet -eq 'YES') {
$ASPNETStats=Get-WmiObject -Class Win32_PerfFormattedData_ASPNET_ASPNET -ComputerName $ComputerName -ErrorAction SilentlyContinue
$ApplicationRestarts=$ASPNETStats | % {$_.ApplicationRestarts}
$ApplicationsRunning=$ASPNETStats | % {$_.ApplicationsRunning}
$RequestExecutionTime=$ASPNETStats | % {$_.RequestExecutionTime}
$RequestsCurrent=$ASPNETStats | % {$_.RequestsCurrent}
$RequestsQueued=$ASPNETStats | % {$_.RequestsQueued}
$WorkerProcessRestarts=$ASPNETStats | % {$_.WorkerProcessRestarts}
$ASPNetExp=Get-WmiObject -Class Win32_PerfFormattedData_NETFramework_NETCLRExceptions -ComputerName $ComputerName | Where {$_.Name -eq "_Global_"} -ErrorAction SilentlyContinue
$ASPNetExpThrown=$ASPNetExp | % {$_.NumberofExcepsThrown}
$ASPNetCLRLoad=Get-WmiObject -Class Win32_PerfFormattedData_NETFramework_NETCLRLoading -ComputerName $ComputerName | Where {$_.Name -eq "_Global_"} -ErrorAction SilentlyContinue
$ASPNetCLRLoadAssem=$ASPNetCLRLoad | % {$_.CurrentAssemblies}
$ASPNetCLRLoadClass=$ASPNetCLRLoad | % {$_.CurrentClassesLoaded}
}

$PerfDate=Get-Date -uformat %T

$Cat += "'$PerfDate', "
$SerieCurCon += "$CurCon, "
$SerieCurAnon += "$CurAnon, "
$SerieCurNonAnon += "$CurNonAnon, "
$SeriePercentProc += "$PercentProc, "
$SerieAvailableMB += "$AvailableMB, "
$SerieBytesSent += "$KBSent, "
$SerieBytesReceived += "$KBReceived, "
$SerieCopyRequestsPerSec += "$CopyRequestsPerSec, "
$SerieDeleteRequestsPerSec += "$DeleteRequestsPerSec, "
$SerieGetRequestsPerSec += "$GetRequestsPerSec, "
$SerieHeadRequestsPerSec += "$HeadRequestsPerSec, "
$SerieLockRequestsPerSec += "$LockRequestsPerSec, "
$SerieMkcolRequestsPerSec += "$MkcolRequestsPerSec, "
$SerieMoveRequestsPerSec += "$MoveRequestsPerSec, "
$SerieOptionsRequestsPerSec += "$OptionsRequestsPerSec, "
$SerieOtherRequestMethodsPerSec += "$OtherRequestMethodsPerSec, "
$SeriePostRequestsPerSec += "$PostRequestsPerSec, "
$SeriePropfindRequestsPerSec += "$PropfindRequestsPerSec, "
$SerieProppatchRequestsPerSec += "$ProppatchRequestsPerSec, "
$SeriePutRequestsPerSec += "$PutRequestsPerSec, "
$SerieSearchRequestsPerSec += "$SearchRequestsPerSec, "
$SerieTraceRequestsPerSec += "$TraceRequestsPerSec, "
$SerieUnlockRequestsPerSec += "$UnlockRequestsPerSec, "
$SerieContextSw += "$ContextSw, "
$SerieProcesses += "$Processes, "
$SerieThreads += "$Threads, "
$SerieAvgDiskQL += "$AvgDiskQL, "
$SerieDiskReadsPerSec += "$DiskReadsPerSec, "
$SerieDiskWritesPerSec += "$DiskWritesPerSec, "
if ($ASPNet -eq 'YES') {
$SerieApplicationRestarts += "$ApplicationRestarts, "
$SerieApplicationsRunning += "$ApplicationsRunning, "
$SerieRequestExecutionTime += "$RequestExecutionTime, "
$SerieRequestsCurrent += "$RequestsCurrent, "
$SerieRequestsQueued += "$RequestsQueued, "
$SerieWorkerProcessRestarts += "$WorkerProcessRestarts, "
$SerieASPNetExpThrown += "$ASPNetExpThrown, "
$SerieASPNetCLRLoadAssem += "$ASPNetCLRLoadAssem, "
$SerieASPNetCLRLoadClass += "$ASPNetCLRLoadClass, "
}

Start-Sleep $Interval
$i += $Interval
$SecondsRemaining=$Duration - $i
Write-Progress -Activity "Collecting performance data for $ComputerName. Please wait..." -Status "Progress:" -percentcomplete (($i*100)/$Duration) -SecondsRemaining $SecondsRemaining
}
While ($i -lt $Duration)

$Cat += "],"
$SerieCurCon += "]"
$SerieCurAnon += "]"
$SerieCurNonAnon += "]"
$SeriePercentProc += "]"
$SerieAvailableMB += "]"
$SerieBytesSent += "]"
$SerieBytesReceived += "]"
$SerieCopyRequestsPerSec += "]"
$SerieDeleteRequestsPerSec += "]"
$SerieGetRequestsPerSec += "]"
$SerieHeadRequestsPerSec += "]"
$SerieLockRequestsPerSec += "]"
$SerieMkcolRequestsPerSec += "]"
$SerieMoveRequestsPerSec += "]"
$SerieOptionsRequestsPerSec += "]"
$SerieOtherRequestMethodsPerSec += "]"
$SeriePostRequestsPerSec += "]"
$SeriePropfindRequestsPerSec += "]"
$SerieProppatchRequestsPerSec += "]"
$SeriePutRequestsPerSec += "]"
$SerieSearchRequestsPerSec += "]"
$SerieTraceRequestsPerSec += "]"
$SerieUnlockRequestsPerSec += "]"
$SerieContextSw += "]"
$SerieProcesses += "]"
$SerieThreads += "]"
$SerieAvgDiskQL += "]"
$SerieDiskReadsPerSec += "]"
$SerieDiskWritesPerSec += "]"
if ($ASPNet -eq 'YES') {
$SerieApplicationRestarts += "]"
$SerieApplicationsRunning += "]"
$SerieRequestExecutionTime += "]"
$SerieRequestsCurrent += "]"
$SerieRequestsQueued += "]"
$SerieWorkerProcessRestarts += "]"
$SerieASPNetExpThrown += "]"
$SerieASPNetCLRLoadAssem += "]"
$SerieASPNetCLRLoadClass += "]"

$ASPNetExpCSS="#chart-container-3 {position:relative; top:50px;}"
$ASPNetStatsCSS="#chart-container-4 {position:relative; top:50px;}"
$ASPNetCLRLoadCSS="#chart-container-5 {position:relative; top:50px;}"
$ASPNetStatsDIV=@"
<div id="chart-container-3" style="width: 98%"></div>
<div class="heading" id="heading"><strong>ASP.NET Statistics</strong></div>
<div class="subtitle" id="subtitle"><strong>&#8226; Application Restarts:</strong> The number of times that an application has been restarted during the Web server's lifetime. Application restarts are incremented each time an Application_OnEnd event is raised. An application restart can occur because of changes to the Web.config file, changes to assemblies stored in the application's Bin directory, or when an application must be recompiled due to numerous changes in ASP.NET Web pages. Unexpected increases in this counter can mean that problems are causing your Web application to recycle. In such circumstances you should investigate as soon as possible.<br />
<strong>&#8226; Applications Running:</strong> The number of applications running concurrently on the server computer.<br />
<strong>&#8226; Request Execution Time:</strong> The number of milliseconds taken to execute the last request. In version 1.0 of the Framework, the execution time begins when the worker process receives the request, and stops when the ASP.NET ISAPI sends HSE_REQ_DONE_WITH_SESSION to IIS. For IIS version 5, this includes the time taken to write the response to the client, but for IIS version 6, the response buffers are sent asynchronously, and so the time taken to write the response to the client is not included. Thus on IIS version 5, a client with a slow network connection will increase the value of this counter considerably.<br />
<strong>&#8226; Requests Current:</strong> The number of requests currently handled by the ASP.NET ISAPI. This includes those that are queued, executing, or waiting to be written to the client. This performance counter was added to v1.0 of ASP.NET in the pre-SP3 hotfix described in Knowledge Base Article 329959.<br />
<strong>&#8226; Requests Queued:</strong> The number of requests waiting for service from the queue. When this number starts to increment linearly with increased client load, the Web server computer has reached the limit of concurrent requests that it can process. The default maximum for this counter is 5,000. You can change this setting in the Machine.config file.<br />
<strong>&#8226; Worker Process Restarts:</strong> The number of times a worker process has been restarted on the server computer. A worker process can be restarted if it fails unexpectedly or when it is intentionally recycled. If this counter increases unexpectedly, you should investigate as soon as possible.</div>
<br />
"@
$ASPNetExpDIV=@"
<div id="chart-container-4" style="width: 98%"></div>
<div class="heading" id="heading"><strong>ASP.NET Exceptions</strong></div>
<div class="subtitle" id="subtitle"><strong>&#8226; Number of Exceps Thrown:</strong> This counter displays the total number of exceptions thrown since the start of the application. These include both .NET exceptions and unmanaged exceptions that get converted into .NET exceptions e.g. null pointer reference exception in unmanaged code would get re-thrown in managed code as a .NET System.NullReferenceException; this counter includes both handled and unhandled exceptions. Exceptions that are re-thrown would get counted again. Exceptions should only occur in rare situations and not in the normal control flow of the program.</div>
<br />
"@
$ASPNetCLRLoadDIV=@"
<div id="chart-container-5" style="width: 98%"></div>
<div class="heading" id="heading"><strong>ASP.NET CLR Loading</strong></div>
<div class="subtitle" id="subtitle"><strong>&#8226; Current Assemblies:</strong> This counter displays the current number of Assemblies loaded across all AppDomains in this application. If the Assembly is loaded as domain-neutral from multiple AppDomains then this counter is incremented once only. Assemblies can be loaded as domain-neutral when their code can be shared by all AppDomains or they can be loaded as domain-specific when their code is private to the AppDomain.<br />
<strong>&#8226; Current Classes Loaded:</strong> This counter displays the current number of classes loaded in all Assemblies.</div>
<br />
"@
$ASPNetStatsJS=@"
<script>
var chart;
`$(document).ready(function() {
	chart=new Highcharts.Chart({
		chart: {
			renderTo: 'chart-container-3',
            height: 390,
			type: 'line',
			marginRight: 30,
			marginBottom: 125
		},
		title: {
			text: 'ASP.NET Statistics',
			x: -20 //center
		},
		subtitle: {
			text: 'ASP.NET global performance counters',
			x: -20
		},
		xAxis: {
			$cat
            labels: {
			$labelformat
			}
		},
        plotOptions: {
                series: {
                    $plot
            }
        },
		yAxis: {
			min: 0,
            title: {
				text: '# of Events'
			},
			plotLines: [{
				value: 0,
				width: 1,
				color: '#808080'
			}]
		},
		tooltip: {
            formatter: function() {
                    return '<b>'+ this.series.name +'</b><br/>'+
                    this.x +': '+ this.y;
            }
        },
        legend: {
			layout: 'horizontal',
			align: 'center',
			verticalAlign: 'bottom',
			x: -5,
			y: 0,
			borderColor: '#CCC',
		    borderWidth: 1,
		    shadow: false
		},
		credits: {
            enabled: false
        },
		series: [{
			name: 'Application Restarts',
			$SerieApplicationRestarts
		}, {
			name: 'Applications Running',
			$SerieApplicationsRunning
		}, {
			name: 'Request Execution Time',
			$SerieRequestExecutionTime
		}, {
			name: 'Requests Current',
			$SerieRequestsCurrent
		}, {
			name: 'Requests Queued',
			$SerieRequestsQueued
		}, {
			name: 'Worker Process Restarts',
			$SerieWorkerProcessRestarts
		}]
	});
});
</script>
"@
$ASPNetExpJS=@"
<script>
var chart;
`$(document).ready(function() {
	chart=new Highcharts.Chart({
		chart: {
			renderTo: 'chart-container-4',
            height: 390,
			type: 'line',
			marginRight: 30,
			marginBottom: 125
		},
		title: {
			text: 'ASP.NET Exceptions',
			x: -20 //center
		},
		subtitle: {
			text: 'Runtime statistics on CLR exception handling',
			x: -20
		},
		xAxis: {
			$cat
            labels: {
			$labelformat
			}
		},
        plotOptions: {
                series: {
                    $plot
            }
        },
		yAxis: {
			min: 0,
            title: {
				text: '# of Exceptions'
			},
			plotLines: [{
				value: 0,
				width: 1,
				color: '#808080'
			}]
		},
		tooltip: {
            formatter: function() {
                    return '<b>'+ this.series.name +'</b><br/>'+
                    this.x +': '+ this.y +' exception(s)';
            }
        },
        legend: {
			layout: 'horizontal',
			align: 'center',
			verticalAlign: 'bottom',
			x: -5,
			y: 0,
			borderColor: '#CCC',
		    borderWidth: 1,
		    shadow: false
		},
		credits: {
            enabled: false
        },
		series: [{
			name: 'Number of Exceps Thrown',
			$SerieASPNetExpThrown
		}]
	});
});
</script>
"@
$ASPNetCLRLoadJS=@"
<script>
var chart;
`$(document).ready(function() {
	chart=new Highcharts.Chart({
		chart: {
			renderTo: 'chart-container-5',
            height: 390,
			type: 'line',
			marginRight: 30,
			marginBottom: 125
		},
		title: {
			text: 'ASP.NET CLR Loading',
			x: -20 //center
		},
		subtitle: {
			text: 'Statistics for CLR Class Loader',
			x: -20
		},
        xAxis: {
			$cat
            labels: {
			$labelformat
			}
		},
        plotOptions: {
                series: {
                    $plot
            }
        },
		yAxis: {
			min: 0,
            title: {
				text: '# of Classes or Assemblies Loaded'
			},
			plotLines: [{
				value: 0,
				width: 1,
				color: '#808080'
			}]
		},
		tooltip: {
            formatter: function() {
                    return '<b>'+ this.series.name +'</b><br/>'+
                    this.x +': '+ this.y;
            }
        },
        legend: {
			layout: 'horizontal',
			align: 'center',
			verticalAlign: 'bottom',
			x: -5,
			y: 0,
			borderColor: '#CCC',
		    borderWidth: 1,
		    shadow: false
		},
		credits: {
            enabled: false
        },
		series: [{
			name: 'Current Assemblies',
			$SerieASPNetCLRLoadAssem
		}, {
			name: 'Current Classes Loaded',
			$SerieASPNetCLRLoadClass
		}]
	});
});
</script>
"@
}

$EndDate=Get-Date -format G

$HtmlReport=@"
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "xhtml11.dtd">
<html>
<head>
<title>$ComputerName - Performance Report</title>
<meta name="description" content="$ComputerName - Performance report" />
<meta name="keywords" content="$ComputerName,performance,report" />
<meta name="author" content="Fabrice ZERROUKI - fabricezerrouki@hotmail.com" />
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<style type="text/css">
body {font: 11px "Trebuchet MS", Verdana, Arial, Helvetica, sans-serif;}
#titre {position:absolute; top:10px; left:12px;}
#content {position:relative;}
#chart-container-1 {position:relative; top:50px;}
#chart-container-2 {position:relative; top:50px;}
$ASPNetExpCSS
$ASPNetStatsCSS
$ASPNetCLRLoadCSS
#chart-container-6 {position:relative; top:50px;}
#chart-container-7 {position:relative; top:50px;}
#chart-container-8 {position:relative; top:50px;}
#chart-container-9 {position:relative; top:50px;}
#chart-container-10 {position:relative; top:50px;}
#heading {position:relative; top:50px;}
#subtitle {position:relative; top:40px;}
.heading {
margin: 1px;
padding: 3px 10px;
cursor: pointer;
position: relative;
}
.subtitle {
padding: 5px 10px;
}
</style>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script>
<script src="//code.highcharts.com/2.1.4/highcharts.js"></script>
<script type="text/javascript">
jQuery(document).ready(function() {
  jQuery(".subtitle").hide();
  jQuery(".heading").click(function()
  {
    jQuery(this).next(".subtitle").slideToggle(500);
  });
});
</script>
</head>
<body>
<div id="titre"><font size='2'><strong>$ComputerName - Performance Report</strong></font><br />
<font size='1'>from $StartDate to $EndDate</font>
</div>
<div id="content">
<div id="chart-container-1" style="width: 98%"></div>
<div class="heading" id="heading"><strong>WebService Connections</strong></div>
<div class="subtitle" id="subtitle"><strong>&#8226; Current Connections:</strong> Current number of connections established with the web service.<br />
<strong>&#8226; Current Anonymous Users:</strong> Number of users who currently have an anonymous connection using the web service.<br />
<strong>&#8226; Current Non Anonymous Users:</strong> Number of users who currently have a non-anonymous connection using the web service.</div>
<br />
<div id="chart-container-2" style="width: 98%"></div>
<div class="heading" id="heading"><strong>WebService Requests Methods</strong></div>
<div class="subtitle" id="subtitle"><strong>&#8226; Copy Requests per sec:</strong> Rate at which HTTP requests using the COPY method are made. COPY requests are used for copying files and directories.<br />
<strong>&#8226; Delete Requests per sec:</strong> Rate at which HTTP requests using the DELETE method are made. DELETE requests are generally used for file removal.<br />
<strong>&#8226; Get Requests per sec:</strong> Rate at which HTTP requests using the GET method are made. GET requests are generally used for basic file retrievals or image maps, though they can be used with forms.<br />
<strong>&#8226; Head Requests per sec:</strong> Rate at which HTTP requests using the HEAD method are made. HEAD requests generally indicate that clients are querying the state of documents they already have to see if they must be refreshed.<br />
<strong>&#8226; Lock Requests per sec:</strong> Rate at which HTTP requests using the LOCK method are made. LOCK requests are used to lock a file for one user so that only that user can modify the file.<br />
<strong>&#8226; Mkcol Requests per sec:</strong> Rate at which HTTP requests using the MKCOL method are made. MKCOL requests are used to create directories on the server.<br />
<strong>&#8226; Move Requests per sec:</strong> Rate HTTP requests using the MOVE method are made. MOVE requests are used for moving files and directories.<br />
<strong>&#8226; Options Requests per sec:</strong> Rate at which HTTP requests using the OPTIONS method are made.<br />
<strong>&#8226; Other Request Methods per sec:</strong> Rate at which HTTP requests are made that do not use the OPTIONS, GET, HEAD, POST, PUT, DELETE, TRACE, MOVE, COPY, MKCOL, PROPFIND, PROPPATCH, MS-SEARCH, LOCK or UNLOCK methods. These may include LINK or other methods supported by gateway applications.<br />
<strong>&#8226; Post Requests per sec:</strong> Rate at which HTTP requests using the POST method are made. POST requests are generally used for forms or gateway requests.<br />
<strong>&#8226; Propfind Requests per sec:</strong> Rate at which HTTP requests using the PROPFIND method are made. PROPFIND requests retrieve property values on files and directories.<br />
<strong>&#8226; Proppatch Requests per sec:</strong> Rate at which HTTP requests using the PROPPATCH method are made. PROPPATCH requests set property values on files and directories.<br />
<strong>&#8226; Put Requests per sec:</strong> Rate at which HTTP requests using the PUT method are made.<br />
<strong>&#8226; Search Requests per sec:</strong> Rate at which HTTP requests using the MS-SEARCH method are made. MS-SEARCH requests query the server to find resources that match a set of client-provided conditions.<br />
<strong>&#8226; Trace Requests per sec:</strong> Rate at which HTTP requests using the TRACE method are made. TRACE requests allow the client to see what is being received at the end of the request chain and use the information for diagnostic purposes.<br />
<strong>&#8226; Unlock Requests per sec:</strong> Rate at which HTTP requests using the UNLOCK method are made. UNLOCK requests are used to remove locks from files.</div>
<br />
$ASPNetStatsDIV
$ASPNetExpDIV
$ASPNetCLRLoadDIV
<div id="chart-container-6" style="width: 98%"></div>
<div class="heading" id="heading"><strong>WebService Bandwidth</strong></div>
<div class="subtitle" id="subtitle"><strong>&#8226; Kilobytes Sent per sec:</strong> Rate at which kilobytes are sent by the web service.<br />
<strong>&#8226; Kilobytes Received per sec:</strong> Rate at which kilobytes are received by the web service.</div>
<br />
<div id="chart-container-7" style="width: 98%"></div>
<div class="heading" id="heading"><strong>System Statistics</strong></div>
<div class="subtitle" id="subtitle"><strong>&#8226; Context Switches per sec:</strong> Rate of switches from one thread to another. Thread switches can occur either inside of a single process or across processes. A thread switch can be caused either by one thread asking another for information, or by a thread being preempted by another, higher priority thread becoming ready to run. The operating system uses process boundaries for subsystem protection in addition to the traditional protection of user and privileged modes. These subsystem processes provide additional protection. Therefore, some work done by the operating system on behalf of an application appears in other subsystem processes in addition to the privileged time in the application. Switching to the subsystem process causes one context switch in the application thread. Switching back causes another context switch in the subsystem thread.<br />
<strong>&#8226; Processes:</strong> Number of processes in the computer at the time of data collection. This is an instantaneous count, not an average over the time interval. Each process represents a running program.<br />
<strong>&#8226; Threads:</strong> Number of threads in the computer at the time of data collection. This is an instantaneous count, not an average over the time interval. A thread is the basic executable entity that can execute instructions in a processor.</div>
<br />
<div id="chart-container-8" style="width: 98%"></div>
<div class="heading" id="heading"><strong>Processor Usage</strong></div>
<div class="subtitle" id="subtitle"><strong>&#8226; % Processor Time:</strong> Percentage of time that the processor is executing a non-idle thread. This property was designed as a primary indicator of processor activity. It is calculated by measuring the time that the processor spends executing the thread of the idle process in each sample interval and subtracting that value from 100%. Each processor has an idle thread which consumes cycles when no other threads are ready to run. It can be viewed as the percentage of the sample interval spent doing useful work. This property displays the average percentage of busy time observed during the sample interval. It is calculated by monitoring the time the service was inactive, and then subtracing that value from 100%.</div>
<br />
<div id="chart-container-9" style="width: 98%"></div>
<div class="heading" id="heading"><strong>Available Memory</strong></div>
<div class="subtitle" id="subtitle"><strong>&#8226; Available Mb:</strong> Amount of physical memory available to processes running on the computer, in megabytes. It is calculated by summing space on the Zeroed, Free, and Standby memory lists. Free memory is ready for use; Zeroed memory contains memory pages filled with zeros to prevent later processes from seeing data used by a previous process. Standby memory is memory removed from a process' working set (its physical memory), but is still available to be recalled. This property displays the last observed value only; it is not an average.</div>
<br />
<div id="chart-container-10" style="width: 98%"></div>
<div class="heading" id="heading"><strong>Disks Statistics</strong></div>
<div class="subtitle" id="subtitle"><strong>&#8226; Average Disk Queue Length:</strong> Average number of both read and write requests that were queued for the selected disk during the sample interval.<br />
<strong>&#8226; Disk Reads per sec:</strong> Rate of read operations on the disk.<br />
<strong>&#8226; Disk Writes per sec:</strong> Rate of write operations on the disk.</div>
<br />
<p><br />&nbsp;<br /></p>
</div>
<script>
var chart;
`$(document).ready(function() {
	chart=new Highcharts.Chart({
		chart: {
			renderTo: 'chart-container-1',
            height: 340,
			type: 'line',
			marginRight: 30,
			marginBottom: 100
		},
		title: {
			text: 'WebService Connections',
			x: -20 //center
		},
		subtitle: {
			text: 'Website: "$WebsiteName"',
			x: -20
		},
		xAxis: {
			$cat
            labels: {
			$labelformat
			}
		},
        plotOptions: {
                series: {
                    $plot
            }
        },
		yAxis: {
			min: 0,
            title: {
				text: '# of Connections'
			},
			plotLines: [{
				value: 0,
				width: 1,
				color: '#808080'
			}]
		},
		tooltip: {
            formatter: function() {
                    return '<b>'+ this.series.name +'</b><br/>'+
                    this.x +': '+ this.y +' connection(s)';
            }
        },
        legend: {
			layout: 'horizontal',
			align: 'center',
			verticalAlign: 'bottom',
			x: -5,
			y: 0,
			borderColor: '#CCC',
		    borderWidth: 1,
		    shadow: false
		},
        credits: {
            enabled: false
        },
		series: [{
			name: 'Current Connections',
			$SerieCurCon
		}, {
			name: 'Current Anonymous Users',
			$SerieCurAnon
		}, {
			name: 'Current Non Anonymous Users',
			$SerieCurNonAnon
		}]
	});
});
</script>
<script>
var chart;
`$(document).ready(function() {
	chart=new Highcharts.Chart({
		chart: {
			renderTo: 'chart-container-2',
            height: 390,
			type: 'line',
			marginRight: 30,
			marginBottom: 175
		},
		title: {
			text: 'WebService Requests Methods',
			x: -20 //center
		},
		subtitle: {
			text: 'Website: "$WebsiteName"',
			x: -20
		},
		xAxis: {
			$cat
            labels: {
			$labelformat
			}
		},
        plotOptions: {
                series: {
                    $plot
            }
        },
		yAxis: {
			min: 0,
            title: {
				text: '# of Requests/s'
			},
			plotLines: [{
				value: 0,
				width: 1,
				color: '#808080'
			}]
		},
		tooltip: {
            formatter: function() {
                    return '<b>'+ this.series.name +'</b><br/>'+
                    this.x +': '+ this.y +' request(s)';
            }
        },
        legend: {
			layout: 'horizontal',
			align: 'center',
			verticalAlign: 'bottom',
			x: -5,
			y: 0,
			borderColor: '#CCC',
		    borderWidth: 1,
		    shadow: false
		},
		credits: {
            enabled: false
        },
		series: [{
			name: 'Copy Requests per sec',
			$SerieCopyRequestsPerSec
		}, {
			name: 'Delete Requests per sec',
			$SerieDeleteRequestsPerSec
		}, {
			name: 'Get Requests per sec',
			$SerieGetRequestsPerSec
		}, {
			name: 'Head Requests per sec',
			$SerieHeadRequestsPerSec
		}, {
			name: 'Lock Requests per sec',
			$SerieLockRequestsPerSec
		}, {
			name: 'Mkcol Requests per sec',
			$SerieMkcolRequestsPerSec
		}, {
			name: 'Move Requests per sec',
			$SerieMoveRequestsPerSec
		}, {
			name: 'Options Requests per sec',
			$SerieOptionsRequestsPerSec
		}, {
			name: 'Other Request Methods per sec',
			$SerieOtherRequestMethodsPerSec
		}, {
			name: 'Post Requests per sec',
			$SeriePostRequestsPerSec
		}, {
			name: 'Propfind Requests per sec',
			$SeriePropfindRequestsPerSec
		}, {
			name: 'Proppatch Requests per sec',
			$SerieProppatchRequestsPerSec
		}, {
			name: 'Put Requests per sec',
			$SeriePutRequestsPerSec
		}, {
			name: 'Search Requests per sec',
			$SerieSearchRequestsPerSec
		}, {
			name: 'Trace Requests per sec',
			$SerieTraceRequestsPerSec
		}, {
			name: 'Unlock Requests per sec',
			$SerieUnlockRequestsPerSec
		}]
	});
});
</script>
$ASPNetStatsJS
$ASPNetExpJS
$ASPNetCLRLoadJS
<script>
var chart;
`$(document).ready(function() {
	chart=new Highcharts.Chart({
		chart: {
			renderTo: 'chart-container-6',
            height: 340,
			type: 'area',
			marginRight: 30,
			marginBottom: 100
		},
		title: {
			text: 'WebService Bandwidth',
			x: -20 //center
		},
		subtitle: {
			text: 'Website: "$WebsiteName"',
			x: -20
		},
		xAxis: {
			$cat
            labels: {
			$labelformat
			}
		},
		yAxis: {
			min: 0,
            title: {
				text: 'KiloBytes/s'
			},
			plotLines: [{
				value: 0,
				width: 1
			}]
		},
		tooltip: {
            formatter: function() {
                    return '<b>'+ this.series.name +'</b><br/>'+
                    this.x +': '+ this.y +' kbytes';
            }
        },
        plotOptions: {
            area: {
	           stacking: 'normal',
               lineWidth: 1,
	           marker: {
	               enabled: false,
                    symbol: 'circle',
                    radius: 2,
                    states: {
                        hover: {
                            enabled: true
                        }
                    }
                }
            }
        },
        legend: {
			layout: 'horizontal',
			align: 'center',
			verticalAlign: 'bottom',
			x: -5,
			y: 0,
			borderColor: '#CCC',
		    borderWidth: 1,
		    shadow: false
		},
        credits: {
            enabled: false
        },
		series: [{
			name: 'KiloBytes Sent per sec',
			$SerieBytesSent,
            color: '#0000FF',
            lineWidth: 1
		}, {
			name: 'KiloBytes Received per sec',
			$SerieBytesReceived,
            color: '#00FF00',
            lineWidth: 1
		}]
	});
});
</script>
<script>
var chart;
`$(document).ready(function() {
	chart=new Highcharts.Chart({
		chart: {
			renderTo: 'chart-container-7',
            height: 340,
			type: 'line',
			marginRight: 30,
			marginBottom: 100
		},
		title: {
			text: 'System Statistics',
			x: -20 //center
		},
		xAxis: {
			$cat
            labels: {
			$labelformat
			}
		},
        plotOptions: {
                series: {
                    $plot
            }
        },
		yAxis: {
			min: 0,
            title: {
				text: '# of Events'
			},
			plotLines: [{
				value: 0,
				width: 1,
				color: '#808080'
			}]
		},
		tooltip: {
            formatter: function() {
                    return '<b>'+ this.series.name +'</b><br/>'+
                    this.x +': '+ this.y;
            }
        },
        legend: {
			layout: 'horizontal',
			align: 'center',
			verticalAlign: 'bottom',
			x: -5,
			y: 0,
			borderColor: '#CCC',
		    borderWidth: 1,
		    shadow: false
		},
        credits: {
            enabled: false
        },
		series: [{
			name: 'Context Switches per sec',
			$SerieContextSw
		}, {
			name: 'Processes',
			$SerieProcesses
		}, {
			name: 'Threads',
			$SerieThreads
		}]
	});
});
</script>
<script>
var chart;
`$(document).ready(function() {
	chart=new Highcharts.Chart({
		chart: {
			renderTo: 'chart-container-8',
            height: 340,
			type: 'area',
			marginRight: 30,
			marginBottom: 100
		},
		title: {
			text: 'Processor Usage',
			x: -20 //center
		},
		subtitle: {
			text: '% Processor Time',
			x: -20
		},
		xAxis: {
			$cat
            labels: {
			$labelformat
			}
		},
		yAxis: {
			min: 0,
            title: {
				text: '% Processor Time'
			}
		},
		tooltip: {
            formatter: function() {
                    return '<b>'+ this.series.name +'</b><br/>'+
                    this.x +': '+ this.y +' %';
            }
        },
        plotOptions: {
            area: {
            lineWidth: 1,
                marker: {
                    enabled: false,
                    symbol: 'circle',
                    radius: 2,
                        states: {
                            hover: {
                                enabled: true
                        }
                    }
                }
            }
        },
        legend: {
			layout: 'horizontal',
			align: 'center',
			verticalAlign: 'bottom',
			x: -5,
			y: 0,
			borderColor: '#CCC',
		    borderWidth: 1,
		    shadow: false
		},
        credits: {
            enabled: false
        },
		series: [{
			name: '% Processor Time',
			$SeriePercentProc,
            color: '#006600',
            lineWidth: 1
		}]
	});
});
</script>
<script>
var chart;
`$(document).ready(function() {
	chart=new Highcharts.Chart({
		chart: {
			renderTo: 'chart-container-9',
            height: 340,
			type: 'area',
			marginRight: 30,
			marginBottom: 100
		},
		title: {
			text: 'Available Memory',
			x: -20 //center
		},
		subtitle: {
			text: 'Available Mb',
			x: -20
		},
		xAxis: {
			$cat
            labels: {
			$labelformat
			}
		},
		yAxis: {
			min: 0,
            title: {
				text: 'Available Mb'
			}
		},
		tooltip: {
            formatter: function() {
                    return '<b>'+ this.series.name +'</b><br/>'+
                    this.x +': '+ this.y +' Mb';
            }
        },
        plotOptions: {
            area: {
	           lineWidth: 1,
	           marker: {
                    enabled: false,
                    symbol: 'circle',
                    radius: 2,
                        states: {
                            hover: {
                                enabled: true
                        }
                    }
                }
            }
        },
        legend: {
			layout: 'horizontal',
			align: 'center',
			verticalAlign: 'bottom',
			x: -5,
			y: 0,
			borderColor: '#CCC',
		    borderWidth: 1,
		    shadow: false
		},
        credits: {
            enabled: false
        },
		series: [{
			name: 'Available Mb',
			$SerieAvailableMB,
            color: '#FF6600',
            lineWidth: 1
		}]
	});
});
</script>
<script>
var chart;
`$(document).ready(function() {
	chart=new Highcharts.Chart({
		chart: {
			renderTo: 'chart-container-10',
            height: 340,
			type: 'line',
			marginRight: 30,
			marginBottom: 100
		},
		title: {
			text: 'Disks Statistics',
			x: -20 //center
		},
		subtitle: {
			text: 'Disk: "_Total"',
			x: -20
		},
		xAxis: {
			$cat
            labels: {
			$labelformat
			}
		},
		yAxis: {
			min: 0,
            title: {
				text: '# of operations'
			},
			plotLines: [{
				value: 0,
				width: 1,
				color: '#808080'
			}]
		},
		tooltip: {
            formatter: function() {
                    return '<b>'+ this.series.name +'</b><br/>'+
                    this.x +': '+ this.y +' operation(s)';
            }
        },
        plotOptions: {
            line: {
	           lineWidth: 1,
	           marker: {
                    enabled: false,
                    symbol: 'circle',
                    radius: 2,
                        states: {
                            hover: {
                                enabled: true
                        }
                    }
                }
            }
        },
        legend: {
			layout: 'horizontal',
			align: 'center',
			verticalAlign: 'bottom',
			x: -5,
			y: 0,
			borderColor: '#CCC',
		    borderWidth: 1,
		    shadow: false
		},
        credits: {
            enabled: false
        },
		series: [{
			name: 'Average Disk Queue Length',
			$SerieAvgDiskQL
		}, {
			name: 'Disk Reads per sec',
			$SerieDiskReadsPerSec
		}, {
			name: 'Disk Writes per sec',
			$SerieDiskWritesPerSec
		}]
	});
});
</script>
</body>
</html>
"@

$HTMLReport_name=$ComputerName + "-PerfGraphs-" + $GraphDate + ".html"
$HTMLReport | Out-File $HTMLReport_name -Encoding UTF8

and the ‘Get-PerfGraphs.ini’ file containing the script parameters

#################################################################################
#										#
#	File	: Get-PerfGraphs.ini						#
#	Purpose	: provide settings for the Get-PerfGraphs.ps1 script		#
#										#
#	Author	: Fabrice ZERROUKI - fabricezerrouki@hotmail.com		#
#										#
#################################################################################
[ComputerName]
#Expected value: hostname or IP address of the target computer
#ComputerName=localhost
ComputerName=localhost

[Website]
#Expected value: website name (as listed in the 'Win32_PerfFormattedData_W3SVC_WebService' class)
#Website=_Total
Website=_Total

[Duration]
#Total collecting performance counters data duration
#Expected value: time in seconds. Maximum value is 43200 (12h)
#Duration=120
#Examples for slackers:
#5mn > 300, 30mn > 1800, 1h > 3600, 2h > 7200
Duration=300

[Interval]
#Interval to get performance counters data
#Expected value: minimum time in seconds between each counter value picking. Maximum value is 60 (1 minute)
#Interval=5
Interval=5

[ASPNet]
#Enable or disable ASP.NET specific performance counters
#Expected value: YES or NO
#ASPNet=NO
ASPNet=YES

 
Allright, and now what? What does the report looks like? here you are:

Get-PerfGraphsGet-PerfGraphs

Waouh, we’ll need a really big screen to have all in one shot…

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Scroll to Top