Skip to content

Instantly share code, notes, and snippets.

@JohnLBevan
Last active July 18, 2024 10:36
Show Gist options
  • Save JohnLBevan/b37084af0f383b66ed342dda0017dbda to your computer and use it in GitHub Desktop.
Save JohnLBevan/b37084af0f383b66ed342dda0017dbda to your computer and use it in GitHub Desktop.
Plot a line chart in PowerShell
Add-Type -AssemblyName System.Windows.Forms
Add-Type -AssemblyName System.Windows.Forms.DataVisualization
Function Out-LineChart {
[CmdletBinding()]
Param (
[Parameter(Mandatory, ValueFromPipeline)]
[Hashtable]$Data
,
[Parameter()]
[string]$Title = 'Line Chart'
,
[Parameter()]
[int]$Width = 800
,
[Parameter()]
[int]$Height = 600
,
[Parameter()]
[System.Windows.Forms.DataVisualization.Charting.Axis]$XAxis = [System.Windows.Forms.DataVisualization.Charting.Axis]::new()
,
[Parameter()]
[System.Windows.Forms.DataVisualization.Charting.Axis]$YAxis = [System.Windows.Forms.DataVisualization.Charting.Axis]::new()
,
[Parameter()]
[System.Windows.Forms.DataVisualization.Charting.ChartValueType]$XType = [System.Windows.Forms.DataVisualization.Charting.ChartValueType]::Auto
,
[Parameter()]
[System.Windows.Forms.DataVisualization.Charting.ChartValueType]$YType = [System.Windows.Forms.DataVisualization.Charting.ChartValueType]::Auto
,
[Parameter()]
[string]$XProperty = 'X'
,
[Parameter()]
[string]$YProperty = 'Y'
)
Begin {
$chartArea = [Windows.Forms.DataVisualization.Charting.ChartArea]::new()
$chartArea.AxisX = $XAxis
$chartArea.AxisY = $YAxis
$chart = [Windows.Forms.DataVisualization.Charting.Chart]::new()
$chart.Width = $Width
$chart.Height = $Height
$chart.ChartAreas.Add($chartArea)
}
Process {
foreach ($key in $Data.Keys) {
$series = [Windows.Forms.DataVisualization.Charting.Series]::new()
$series.Name = $key
$series.ChartType = [System.Windows.Forms.DataVisualization.Charting.SeriesChartType]::Line
$series.XValueType = $XType
$series.YValueType = $YType
foreach ($point in ($Data[$key] | Sort-Object $XProperty)) {
$series.Points.AddXY($point."$XProperty", $point."$YProperty") | Out-Null
}
$chart.Series.Add($series)
}
}
End {
$form = [Windows.Forms.Form]::new()
$form.Text = $Title
$form.Width = $Width
$form.Height = $Height
$form.Controls.Add($chart)
# Show the form
$form.ShowDialog() | Out-Null
}
}
Function Append-MissingIntervals {
[CmdletBinding()]
Param (
[Parameter(Mandatory)]
[PSCustomObject[]]$Series
,
[Parameter()]
[ValidateScript({($_.TotalMilliseconds -ne 0) -or (throw 'Interval cannot be 0')})]
[TimeSpan]$Interval = [TimeSpan]::Parse('01:00:00')
,
[Parameter()]
[Nullable[DateTime]]$Start = $null
,
[Parameter()]
[Nullable[DateTime]]$End = $null
,
[Parameter()]
[string]$XProperty = 'X'
,
[Parameter()]
[string]$YProperty = 'Y'
)
if (($null -eq $Start) -or ($null -eq $End)) {
$maxmin = $Series."$XProperty" | Measure-Object -Maximum -Minimum
if ($null -eq $Start) {$Start = $maxmin.Minimum}
if ($null -eq $End) {$End = $maxmin.Maximum}
}
if ($Start -gt $End) {
$swap = $Start
$Start = $End
$End = $swap
}
if ($Interval.TotalMilliseconds -lt 0) {
$Interval = -$Interval
}
# convert our series to a hashtable for quick lookups
$hash = @{}
foreach ($item in $Series) {
$hash[$item.$XProperty] = $item.$YProperty
}
$current = $Start
while ($current -le $End) {
if (!$hash.ContainsKey($current)) {
$hash[$current] = 0
}
$current += $Interval
}
foreach ($item in $hash.GetEnumerator()) {
([PSCustomObject]@{
$XProperty = $item.Name
$YProperty = $item.Value
})
}
}
# todo: improve how we initialise the axis values... want them to be parameterised to the function, but without the caller needing to know / interact with the .net classes directly
$x = [System.Windows.Forms.DataVisualization.Charting.Axis]::new()
$x.IntervalType = [System.Windows.Forms.DataVisualization.Charting.DateTimeIntervalType]::Days
$x.LabelStyle.Format = "yyyy-MM-dd HH"
$x.LabelStyle.Interval = 1
# We may want to set XType to [System.Windows.Forms.DataVisualization.Charting.ChartValueType]::DateTime.. will see if auto cuts it.
$x.Title = "When"
$y = [System.Windows.Forms.DataVisualization.Charting.Axis]::new()
$y.Title = '# of Timeouts'
# Read logs
$data = Get-Item './path/to/logs/log_2024*.csv' | % FullName | Import-Csv -Delimiter ';' | Where-Object{$_.type -like 'ERROR*' -and $_.Message -like 'The operation has timed out.*'} | %{[DateTime]::Parse("$($_.Date.Trim()) $($_.Time.Substring(0,2)):00:00")} | Group-Object | Select-Object @{N='Name';E={$_.Name -as [DateTime]}}, Count
Out-LineChart -Data @{
Series1 = (Append-MissingIntervals -Series $data -XProperty 'Name' -YProperty 'Count' -End (Get-Date -Minute 0 -Second 0 -Millisecond 0))
Series2 = $data
} -XAxis $x -YAxis $y -XProperty 'Name' -YProperty 'Count'
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment