Skip to content

Instantly share code, notes, and snippets.

@SP3269
Created May 9, 2021 23:56
Show Gist options
  • Save SP3269/d0515ff72c479dbbadbed082b63943f2 to your computer and use it in GitHub Desktop.
Save SP3269/d0515ff72c479dbbadbed082b63943f2 to your computer and use it in GitHub Desktop.
Dockerable Prometheus "anything" exporter in PowerShell
# pwsh "anything" Prometheus exporter sketch
# Using: PrometheusExporter (https://github.com/jobec/powershell-prom-client), including class defintitions
# Using: Polaris (https://github.com/PowerShell/Polaris)
# Motivation: being able to package PowerShell-based collector/exporter in a Docker container
# PrometheusExporter's 0.1.0 New-PrometheusExporter functionfails when run in a container - [console]::KeyAvailable error
# NOTE: Polaris can process one request at a time. Refactor/implement LB for real use
Using module PrometheusExporter
Import-Module Polaris
# Polaris request logging
function Log-Request([PolarisRequest]$Request) {
$logentry = '{0} {1} [{2}] "{3}"' -f $Request.clientIP,($Request.user ?? "-"),(Get-Date -Format "yyyy-MM-dd HH:mm:ss"),$Request.URL
Write-Host $logentry
}
New-PolarisRouteMiddleware -Name "Request Logger" -Scriptblock {
Log-Request -Request $Request
}
# The renderer function
function Render-Collector (
[Parameter(Mandatory = $true)][ScriptBlock]$Collector
) {
$ch = [Channel]::new() # A class from PrometheusExporter
$ch.AddMetrics($Collector.Invoke())
[String]$ch
}
# Collector metric descriptors and initial values
$global:invocations = 0
$collection_time_desc = New-MetricDescriptor -Name "pwsh_collection_time" -Type gauge -Help "Last collection time (s)"
$invocations_desc = New-MetricDescriptor -Name "pwsh_number_of_invocations" -Type counter -Help "Number of collections by this instance"
$uptime_desc = New-MetricDescriptor -Name "pwsh_collector_instance_uptime" -Type counter -Help "Exporter instance uptime (s)"
$starttime = Get-Date
# Custom metrics
# Metric 1 - IBM market cap
$vkey = "demo" # alphavantage.co API key
$IBMMarketCapDesc = New-MetricDescriptor -Name "ibm_market_cap" -Type gauge -Help "IBM market capitalisation"
$GetIBMMarketCap = {(Invoke-RestMethod "https://www.alphavantage.co/query?function=OVERVIEW&symbol=IBM&apikey=$vkey" -Verbose).MarketCapitalization}
# Metric 2 - 2D random motion
$global:x = 0
$global:y = 0
$XYDesc = New-MetricDescriptor -Name "traveler_distance" -Type gauge -Help "Distance from (0,0)"
$XYDistance = {
$global:x += (-1..1 | Get-Random)
$global:y += (-1..1 | Get-Random)
[System.Math]::Sqrt($global:x*$global:x+$global:y*$global:y)
}
# The collector function - outputs Metric[]
function collector() {
@(
$tick = Get-Date
New-Metric -MetricDesc $IBMMarketCapDesc -Value (& $GetIBMMarketCap)
New-Metric -MetricDesc $XYDesc -Value (& $XYDistance)
$tock = Get-Date
New-Metric -MetricDesc $collection_time_desc -Value ($tock - $tick).TotalSeconds
New-Metric -MetricDesc $invocations_desc -Value (++$global:invocations)
New-Metric -MetricDesc $uptime_desc -Value ([math]::Floor(((Get-Date) - $starttime).TotalSeconds) )
)
}
# The runtime
$port = $env:PORT ?? 9070
$starttime = Get-Date
$invocationcount = 0
# Not using the exporter's built-in exporter runtime
# $exp = New-PrometheusExporter -Port $port
# Register-Collector -Exporter $exp -Collector $Function:collector
# $exp.Collect()
New-PolarisGetRoute -Path "/hello" -Scriptblock {
$Response.Send('hello')
}
New-PolarisGetRoute -Path "/metrics" -Scriptblock {
$body = Render-Collector $Function:collector
$Response.ContentType = "text/plain; version=0.0.4; charset=utf-8"
$Response.Send($body)
}
$Polaris = Start-Polaris -Port $port
while ($Polaris.Listener.IsListening) {
Wait-Event callbackeventbridge.callbackcomplete
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment