Code Monkey home page Code Monkey logo

powershell-prom-client's Introduction

PowerShell Prometheus Client

This Powershell module makes it easy to build a custom prometheus exporter based on PowerShell.

It's inspired and based on the Go client and works in sort of the same way.

Installing

Install-Module -Name PrometheusExporter

Usage

All steps below are combined in the example.ps1 file in this repository.

First, import this module

Import-Module PrometheusExporter

Next, define metric descriptors. These describe your metrics, their labels, type and helptext.

$TotalConnections   = New-MetricDescriptor -Name "rras_connections_total" -Type counter -Help "Total connections since server start"
$CurrentConnections = New-MetricDescriptor -Name "rras_connections" -Type gauge -Help "Current established connections" -Labels "protocol"

The scraping is done via a collector function which returns metrics. Below is an example of scraping RRAS server statistics.

function collector () {
    $RRASConnections = Get-RemoteAccessConnectionStatistics
    $TotalCurrent = $RRASConnections.count
    $IKEv2 = @($RRASConnections | Where-Object {$_.TunnelType -eq "Ikev2"}).count
    $SSTP = @($RRASConnections | Where-Object {$_.TunnelType -eq "Sstp"}).count
    $Cumulative = (Get-RemoteAccessConnectionStatisticsSummary).TotalCumulativeConnections

    @(
        New-Metric -MetricDesc $TotalConnections -Value $Cumulative
        New-Metric -MetricDesc $CurrentConnections -Value $TotalCurrent -Labels ("all")
        New-Metric -MetricDesc $CurrentConnections -Value $IKEv2 -Labels ("ikev2")
        New-Metric -MetricDesc $CurrentConnections -Value $SSTP -Labels ("sstp")
    )
}

A final step is building a new exporter and starting it:

$exp = New-PrometheusExporter -Port 9700
Register-Collector -Exporter $exp -Collector $Function:collector
$exp.Start()

If you now open http://localhost:9700 in your browser, you will see the metrics displayed.

# HELP rras_connections_total Total connections since server start
# TYPE rras_connections_total counter
rras_connections_total 8487
# HELP rras_connections Current established connections
# TYPE rras_connections gauge
rras_connections{protocol="all"} 563
rras_connections{protocol="ikev2"} 439
rras_connections{protocol="sstp"} 124

Running as a service

Running a powershell script as a windows service is possible by using NSSM.

Use the snippet below to install it as a service:

$serviceName = 'MyExporter'
$nssm = "c:\path\to\nssm.exe"
$powershell = (Get-Command powershell).Source
$scriptPath = 'c:\program files\your_exporter\exporter.ps1'
$arguments = '-ExecutionPolicy Bypass -NoProfile -File """{0}"""' -f $scriptPath

& $nssm install $serviceName $powershell $arguments
Start-Service $serviceName

# Substitute the port below with the one you picked for your exporter
New-NetFirewallRule -DisplayName "My Exporter" -Direction Inbound -Action Allow -Protocol TCP -LocalPort 9700

powershell-prom-client's People

Contributors

jobec avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

powershell-prom-client's Issues

My proof of concept doesn't work

I tried setting up the exporter using dynamic values based on what I get using the Cmdlet Get-Function.
I don't really care about the Metrics names at the moment, so I'm using a raw conversion.

Import-Module PrometheusExporter

$PlainPerfCounters = Get-Counter

[array]$Global:MetricDescriptors = $null
foreach ($PsCounter in $PlainPerfCounters.CounterSamples) {
    $MetricDescriptorName = $PsCounter.Path -replace '^\\\\.*?\\','' -replace '!|^_|\?|\*|\\|\/|\.$|\.{2}|\[|\]|\(|\)|\%|\s|\-','_'
    
    $CounterType = @{
        "RateOfCountsPerSecond64" = "gauge"
        "NumberOfItems32" = "gauge"
        "Timer100NsInverse" = "gauge"
        "RawFraction" = "gauge"
        "RateOfCountsPerSecond32" = "gauge"
        "542573824" = "gauge"
    }

    $HelpName = $PsCounter.InstanceName -replace '!|^_|\?|\*|\\|\/|\.$|\.{2}|\[|\]|\(|\)|\%|\s|\-','_'
    if (!$HelpName) {
        $HelpName = '_'
    }

    $MetricDescriptor = New-MetricDescriptor `
        -Name $MetricDescriptorName `
        -Type $CounterType["$($PsCounter.CounterType)"] `
        -Help $HelpName

    $Global:MetricDescriptors += $MetricDescriptor

}

function collector {
    $PlainPerfCounters = Get-Counter

    [array]$Metrics = $null
    for ($i=0; $i -lt $PlainPerfCounters.CounterSamples.Count; $i++) {
        $Metric = New-Metric `
            -MetricDesc $MetricDescriptors[$i] `
            -Value $PlainPerfCounters.CounterSamples[$i].CookedValue

        $Metrics += $Metric
    }

    $Metrics

}

$PerformanceExporter = New-PrometheusExporter -Port 9700
Register-Collector -Exporter $PerformanceExporter  -Collector $Function:collector
$PerformanceExporter.Start()

But when visiting the address as described, the exporter displays the error

Exception calling "Invoke" with "0" argument(s): "Cannot process argument transformation on parameter 'Value'. Connot
convert value "[string]" to type "System.Single". Error: "Input string was not in a correct format.""

Multiple Labels for one metric

When I try to apply multiple labels to a single metric, the page errors

For example, I have a descriptor like this:
$metric_Diskspace_counter_description = New-MetricDescriptor -Name "Diskspace" -Type gauge -Help "performance counter value" -Labels "drive_letter","servername"

Then a metric like this:
New-Metric -MetricDesc $metric_diskspace_counter_description -Value (Get-Counter -Counter "\LogicalDisk(C:)% Free Space" -ComputerName server01)[0].countersamples.cookedvalue -Labels ("C","server01")

Unexpected end of input steam

When trying to pull data presented with this module with Telegraf, we receive the following error:
[inputs.prometheus] Error in plugin: error reading metrics for http://localhost:9191/metrics: reading text format failed: text format parsing error in line 20: unexpected end of input stream
It seems like it might be related to Telegraf issue 10608. Would you mind taking a look at this to see if our suspicions are correct?

Transmission of (Database) Table Data to Prometheus

Hi there,

I'm using PowerShell (5.1.14409.1018), ORACLE Database (12.2.0.1.0), Prometheus (2.19.2), Grafana (7.1.1)

PowerShell allows to perform database queries i.e. from ORACLE DB.

Following the instructions at Use Oracle ODP.NET and PowerShell to Simplify Data Access and Oracle Data Provider for .NET / ODP.NET connection strings leads to the following (working) PowerShell Script:

# Load Oracle.ManagedDataAccess.dll module in PowerShell
Add-Type -Path "C:\Program Files\WindowsPowerShell\Modules\Oracle\lib\netstandard2.0\Oracle.ManagedDataAccess.dll"

$connectionString = "Data Source=<NAME_OF_TNSNAMES_ENTRY>;User Id=<DB_USERNAME>;Password=<DB_PASSWORD>"
$connection = New-Object Oracle.ManagedDataAccess.Client.OracleConnection($connectionString)

$connection.open()
$command = $connection.CreateCommand()

$command.CommandText = "
SELECT 'JOB_A' JOB_NAME, 1 STATUS FROM dual
UNION SELECT 'JOB_B' JOB_NAME, 2 STATUS FROM dual
UNION SELECT 'JOB_C' JOB_NAME, 3 STATUS FROM dual
UNION SELECT 'JOB_D' JOB_NAME, 4 STATUS FROM dual
UNION SELECT 'JOB_E' JOB_NAME, 5 STATUS FROM dual
UNION SELECT 'JOB_F' JOB_NAME, 6 STATUS FROM dual
UNION SELECT 'JOB_G' JOB_NAME, 7 STATUS FROM dual
UNION SELECT 'JOB_H' JOB_NAME, 8 STATUS FROM dual
UNION SELECT 'JOB_I' JOB_NAME, 9 STATUS FROM dual
UNION SELECT 'JOB_J' JOB_NAME, 10 STATUS FROM dual
"

$queryResult = $command.ExecuteReader()

while ($queryResult.Read()) {
$queryResult.GetOracleString(0).Value
$queryResult.GetOracleDecimal(1).Value
}

$connection.close()

When I execute this PowerShell script, I get the following output (as expected):
PS C:\Windows\system32> C:\Program Files\powershell_prom_client\test_oracle_query.ps1
JOB_A
1
JOB_B
2
JOB_C
3
JOB_D
4
JOB_E
5
JOB_F
6
JOB_G
7
JOB_H
8
JOB_I
9
JOB_J
10

Due to the fact that my query (in real world) contains several thousand rows of output, I would like to pursue a generic approach to avoid setting a separate PowerShell variable for each table cell data...

But currently I don't know what data structure is necessary or how to parse my results (JOB_NAMES (column 1) and STATUS (column 2)) in order to trasmit them subsequently to Prometheus, so that Prometheus "understands" that there is not only one single value arriving simultaneously but mutltiple values (table data) simultaneously instead...

A) Does powershell-prom-client support the transmission of table data to Prometheus?
B) And if so, what do I have to do to solve my problem?

PS: When I would be able to receive the database query data as table data in Prometheus, I would like to use Grafana's Bar Gauge visualization. Grafana's Bar Gauge in the context of MySQL exporter supports the possibility to generically repeat every column value of type string of a specific datatable column as display name (like in my case every JOB_NAME) and it's corresponding value (like in my case STATUS). See for more information Bar Gauge : How to show Series name from a query’s row value (MySQL)?

Thanks for your help in advance!

The property 'labels' exists on multiple object types

When trouble shooting the collector I can't display the Labels for the Metrics definition and the Labels property for the actual metric object at the same time.

When trying to list all values in action during a trouble shooting session

$ExampleDescriptor = New-MetricDescriptor `
  -Name "ExampleDescriptor" `
  -Type gauge `
  -Help "Some help" `
  -Labels "Instance"

$Metrics = @(
  New-Metric `
    -MetricDesc $ExampleDescriptor `
    -Value 10 `
    -Labels ("example")
)

examination of the object is possible separately using

C:\> $Metrics

Descriptor Value Labels
---------- ----- ------
MetricDesc    10 {example}

C:\> $Metrics | select -ExpandProperty Descriptor

Name              Help       Type Labels
----              ----       ---- ------
ExampleDescriptor Some help gauge {Instance}

but not using a "combined view"

C:\> $Metrics | select Value, Labels -ExpandProperty Descriptor | ft
Select-Object: The property cannot be processed because the property "Labels" already exists.

Value Name              Help       Type Labels
----- ----              ----       ---- ------
   10 ExampleDescriptor Some help gauge {Instance}

I could of course use a calculated property during output

$Metrics | select Value, @{l='LabelValue'; e={$_.Labels}} -ExpandProperty Descriptor | ft

Value LabelValue Name              Help       Type Labels
----- ---------- ----              ----       ---- ------
   10 example    ExampleDescriptor Some help gauge {Instance}

The property Labels is clearly for different use depending on the object related, and so should have different names to better separate them.

How can I add multiple labels in the metric

Hi

Thanks a lot for this amazing tool.
How do I add multiple labels in the metric?
For ex, if I have to add an instance name to it:
rras_connections{protocol="all",instance="host1"} 563

Thanks a lot in advance.

Gajanan

Setting up an exporter using a Job will fail

When using a job in PowerShell Windows 5,1 for running the Exporter, the job will fail with

Exception setting "TreatControlCAsInput":  "The handle is invalid.
"
At C:\Program Files\WindowsPowerShell\Modules\PromethuesExporter\0.1.0\PrometheusExporter.psm1:117 char:9
+        [Console]::TreatControlCAsInput = $True
+        ~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo           :  NotSpecified: (:) [], SetValueInvocationException
    + FullyQualifiedErrorId  :  ExceptionsWhenSetting

as a job has no input handler.

A switch, -Interactive, might be an appropriate change to use when Ctrl-C is a viable input.

Or better yet, don't look for Ctrl-C at all, the script will still abort when breaking...

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.