Search code examples
powershellperformancemonitoringprotobuf-net

Parsing Prometheus promo format using PowerShell


I've looked around a while for how to go about to parse a Prometheus promo answer using PowerShell to convert published Windows system performance metrics back to PowerShell objects.

# HELP CPU_Total Total CPU load in percent of all processors combined.
# TYPE CPU_Total gauge
CPU_Total{Instance="_total"} 1.29915
# HELP Disk_Avg_Bytes_Read Average number of bytes transfered from the disk during read operations.
# TYPE Disk_Avg_Bytes_Read gauge
Disk_Avg_Bytes_Read{Instance="harddiskvolume2"} 0
# HELP Disk_Avg_Bytes_Read Average number of bytes transfered from the disk during read operations.
# TYPE Disk_Avg_Bytes_Read gauge
Disk_Avg_Bytes_Read{Instance="c"} 0
# HELP Disk_Avg_Bytes_Read Average number of bytes transfered from the disk during read operations.
# TYPE Disk_Avg_Bytes_Read gauge
Disk_Avg_Bytes_Read{Instance="_total"} 0
# HELP Disk_Avg_Bytes_Write Average number of bytes transfered to the disk during write operations.
# TYPE Disk_Avg_Bytes_Write gauge
Disk_Avg_Bytes_Write{Instance="harddiskvolume2"} 0
# HELP Disk_Avg_Bytes_Write Average number of bytes transfered to the disk during write operations.
# TYPE Disk_Avg_Bytes_Write gauge
Disk_Avg_Bytes_Write{Instance="c"} 8192
# HELP Disk_Avg_Bytes_Write Average number of bytes transfered to the disk during write operations.
# TYPE Disk_Avg_Bytes_Write gauge
Disk_Avg_Bytes_Write{Instance="_total"} 8192
# HELP Disk_sec_per_Read Average time, in seconds, of a read of data from the disk.
# TYPE Disk_sec_per_Read gauge
Disk_sec_per_Read{Instance="harddiskvolume2"} 0

There a plenty of examples in Go (How to parse Prometheus data) and C#, etc. and they all seem to refer back to the Google ProtoBuf library

Even if PowerShell can interpret C#, I haven't a clue about how to write it :)
I assume that the download for Windows is a .Net-library, but exposing the calls is a daunting task when the foundation of knowledge is PowerShell.
And I need to use a DLL-explorer to find the names of the public calls available.

So I tried to start download the .Net version of ProtBuf, with accompanying dependencies, and then install using 'Install-Package' only to end up in the error

Install-Package: Dependency loop detected for package 'protobuf-net'

But using

choco install protoc

works like a charm.

Unfortunatley, Promethues no longer supports the JSON-format, so the proto file that can be found at
https://github.com/prometheus/client_model/blob/master/io/prometheus/client/metrics.proto
is no longer maintained (although it might be in the future again).

So,

  • is there a simple way to construct a parser based on the prom file in PowerShell?

Edit: Using Chocolatey to install the library instead...
Edit II: The ProtoBuf library is actually an protoc.exe file with command switches
Edit III: Found the Prometheus ebnf specification
Edit IV: Added a promo example


Solution

  • I did find prom2json, but not as a binary for Windows or easaly transferable to PowerShell.
    And that would have resulted in a cmd tool anyway.

    So I resorted to plain text parsing for now...

    $RestCall = Invoke-RestMethod -Uri http://localhost:9700/metrics
    
    $RestObjects = $RestCall -split "`n# HELP "
    
    [array]$Metrics = $null
    foreach ($Object in $RestObjects) {
      $Part = $Object -split "`n"
    
      $Help = $Part[0]
      $Type = $Part[1] # for now we only have gauge, ignore
      [array]$ValueStruct = ($Part[2] -split '[{}]').Trim(' ')
    
      if ($ValueStruct.Count -ne 1) {# label
        $ValueName = $ValueStruct[0]
        $Label = $ValueStruct[1] -split "="
        $LabelName = $Label[0]
        $LabelValue = $Label[1] -replace '"',''
        $Value = $ValueStruct[2]
      } else {# no label
        $ValueStruct = $Part[2] -split ' '
    
        $ValueName = $ValueStruct[0]
        $LabelName = $null
        $LabelValue = $null
        $Value = $ValueStruct[1]
      }
        
    
      $Metric = [PSCustomObject]@{
        Name    = $ValueName
        Instance = $LabelValue
        Value   = $Value
      }
    
      $Metrics += $Metric
    
    }
    
    $Metrics