Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,16 +33,20 @@ See the `-help` output for more options.
```
-collector.apilistener
Include APIListener data
-collector.cib
Include CIB data
-collector.checker
Include CheckerComponent data
-collector.cib
Include CIB data
-collector.gelf
Include GelfWriter data
-collector.graphite
Include GraphiteWriter data
-collector.influx
Include InfluxDBWriter data
-collector.influx2
Include InfluxDB2Writer data
-collector.opentsdb
Include OpenTSDBWriter data
-collector.otlpmetrics
Include OTLPMetricsWriter data
-debug
Expand Down
12 changes: 12 additions & 0 deletions icinga2_exporter.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@ func main() {
cliCollectorInflux bool
cliCollectorInflux2 bool
cliCollectorGraphite bool
cliCollectorOpenTSDB bool
cliCollectorGELF bool
cliCollectorOTLP bool
)

Expand All @@ -91,6 +93,8 @@ func main() {
flag.BoolVar(&cliCollectorInflux, "collector.influx", false, "Include InfluxDBWriter data")
flag.BoolVar(&cliCollectorInflux2, "collector.influx2", false, "Include InfluxDB2Writer data")
flag.BoolVar(&cliCollectorGraphite, "collector.graphite", false, "Include GraphiteWriter data")
flag.BoolVar(&cliCollectorOpenTSDB, "collector.opentsdb", false, "Include OpenTSDBWriter data")
flag.BoolVar(&cliCollectorGELF, "collector.gelf", false, "Include GelfWriter data")
flag.BoolVar(&cliCollectorOTLP, "collector.otlpmetrics", false, "Include OTLPMetricsWriter data")

flag.BoolVar(&cliVersion, "version", false, "Print version")
Expand Down Expand Up @@ -174,6 +178,14 @@ func main() {
prometheus.MustRegister(collector.NewIcinga2GraphiteCollector(c, logger))
}

if cliCollectorGELF {
prometheus.MustRegister(collector.NewIcinga2GELFCollector(c, logger))
}

if cliCollectorOpenTSDB {
prometheus.MustRegister(collector.NewIcinga2OpenTSDBCollector(c, logger))
}

if cliCollectorOTLP {
prometheus.MustRegister(collector.NewIcinga2OTLPMetricsCollector(c, logger))
}
Expand Down
60 changes: 60 additions & 0 deletions internal/collector/gelf.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package collector

import (
"fmt"
"log/slog"

"github.com/NETWAYS/icinga2-exporter/internal/icinga"

"github.com/prometheus/client_golang/prometheus"
)

type Icinga2GELFCollector struct {
icingaClient icinga.IcingaClient
logger *slog.Logger
}

func NewIcinga2GELFCollector(client icinga.IcingaClient, logger *slog.Logger) *Icinga2GELFCollector {
return &Icinga2GELFCollector{
icingaClient: client,
logger: logger,
}
}

func (collector *Icinga2GELFCollector) Describe(ch chan<- *prometheus.Desc) {
}

func (collector *Icinga2GELFCollector) Collect(ch chan<- prometheus.Metric) {
perfdata, err := collector.icingaClient.GetPerfdataMetrics(icinga.EndpointGELFWriter)

if err != nil {
collector.logger.Error("Could not retrieve GELFWriter metrics", "error", err.Error())
return
}

for _, result := range perfdata.Results {
for component, writers := range result.Status {
for writer, metrics := range writers {
for metricName, value := range metrics {
if !isPerfdataMetric(metricName) {
continue
}

safeMetricName := ensureValidMetricName(metricName)

name := fmt.Sprintf("icinga2_%s_%s", component, safeMetricName)

description := prometheus.NewDesc(name, "GELFWriter "+name, []string{"writer"}, nil)
metric, err := prometheus.NewConstMetric(description, prometheus.GaugeValue, value, writer)

if err != nil {
collector.logger.Error("Error creating metric "+metricName, "error", err.Error())
continue
}

ch <- metric
}
}
}
}
}
48 changes: 48 additions & 0 deletions internal/collector/gelf_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package collector

import (
"encoding/json"
"io"
"log/slog"
"os"
"strings"
"testing"

"github.com/NETWAYS/icinga2-exporter/internal/icinga"

"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/testutil"
)

func TestIcinga2GELFWriterCollector_Collect(t *testing.T) {
client := &MockIcingaClient{}

var result icinga.PerfdataResult
data, _ := os.ReadFile("testdata/gelf.json")
json.Unmarshal(data, &result)

client.SetPerfdataMetrics(result)

logger := slog.New(slog.NewTextHandler(io.Discard, nil))
collector := NewIcinga2GELFCollector(client, logger)

reg := prometheus.NewRegistry()
reg.MustRegister(collector)

metricCount := testutil.CollectAndCount(collector)

expectedCount := 2
if metricCount != expectedCount {
t.Errorf("expected %d metrics, got %d", expectedCount, metricCount)
}

expectedMetrics := `
# HELP icinga2_gelfwriter_work_queue_items GELFWriter icinga2_gelfwriter_work_queue_items
# TYPE icinga2_gelfwriter_work_queue_items gauge
icinga2_gelfwriter_work_queue_items{writer="gelf"} 4
`

if err := testutil.CollectAndCompare(collector, strings.NewReader(expectedMetrics), "icinga2_gelfwriter_work_queue_items"); err != nil {
t.Errorf("unexpected metric difference:\n%s", err)
}
}
60 changes: 60 additions & 0 deletions internal/collector/opentsdb.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package collector

import (
"fmt"
"log/slog"

"github.com/NETWAYS/icinga2-exporter/internal/icinga"

"github.com/prometheus/client_golang/prometheus"
)

type Icinga2OpenTSDBCollector struct {
icingaClient icinga.IcingaClient
logger *slog.Logger
}

func NewIcinga2OpenTSDBCollector(client icinga.IcingaClient, logger *slog.Logger) *Icinga2OpenTSDBCollector {
return &Icinga2OpenTSDBCollector{
icingaClient: client,
logger: logger,
}
}

func (collector *Icinga2OpenTSDBCollector) Describe(ch chan<- *prometheus.Desc) {
}

func (collector *Icinga2OpenTSDBCollector) Collect(ch chan<- prometheus.Metric) {
perfdata, err := collector.icingaClient.GetPerfdataMetrics(icinga.EndpointOpenTsdbWriter)

if err != nil {
collector.logger.Error("Could not retrieve OpenTSDBWriter metrics", "error", err.Error())
return
}

for _, result := range perfdata.Results {
for component, writers := range result.Status {
for writer, metrics := range writers {
for metricName, value := range metrics {
if !isPerfdataMetric(metricName) {
continue
}

safeMetricName := ensureValidMetricName(metricName)

name := fmt.Sprintf("icinga2_%s_%s", component, safeMetricName)

description := prometheus.NewDesc(name, "OpenTSDBWriter "+name, []string{"writer"}, nil)
metric, err := prometheus.NewConstMetric(description, prometheus.GaugeValue, value, writer)

if err != nil {
collector.logger.Error("Error creating metric "+metricName, "error", err.Error())
continue
}

ch <- metric
}
}
}
}
}
48 changes: 48 additions & 0 deletions internal/collector/opentsdb_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package collector

import (
"encoding/json"
"io"
"log/slog"
"os"
"strings"
"testing"

"github.com/NETWAYS/icinga2-exporter/internal/icinga"

"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/testutil"
)

func TestIcinga2OpenTSDBWriterCollector_Collect(t *testing.T) {
client := &MockIcingaClient{}

var result icinga.PerfdataResult
data, _ := os.ReadFile("testdata/opentsdb.json")
json.Unmarshal(data, &result)

client.SetPerfdataMetrics(result)

logger := slog.New(slog.NewTextHandler(io.Discard, nil))
collector := NewIcinga2OpenTSDBCollector(client, logger)

reg := prometheus.NewRegistry()
reg.MustRegister(collector)

metricCount := testutil.CollectAndCount(collector)

expectedCount := 2
if metricCount != expectedCount {
t.Errorf("expected %d metrics, got %d", expectedCount, metricCount)
}

expectedMetrics := `
# HELP icinga2_opentsdbwriter_work_queue_items OpenTSDBWriter icinga2_opentsdbwriter_work_queue_items
# TYPE icinga2_opentsdbwriter_work_queue_items gauge
icinga2_opentsdbwriter_work_queue_items{writer="opentsdb"} 4
`

if err := testutil.CollectAndCompare(collector, strings.NewReader(expectedMetrics), "icinga2_opentsdbwriter_work_queue_items"); err != nil {
t.Errorf("unexpected metric difference:\n%s", err)
}
}
41 changes: 41 additions & 0 deletions internal/collector/testdata/gelf.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
{
"results": [
{
"name": "GelfWriter",
"perfdata": [
{
"counter": false,
"crit": null,
"label": "gelfwriter_gelf_work_queue_items",
"max": null,
"min": null,
"type": "PerfdataValue",
"unit": "",
"value": 4,
"warn": null
},
{
"counter": false,
"crit": null,
"label": "gelfwriter_gelf_work_queue_item_rate",
"max": null,
"min": null,
"type": "PerfdataValue",
"unit": "",
"value": 0,
"warn": null
}
],
"status": {
"gelfwriter": {
"gelf": {
"connected": false,
"source": "icinga2",
"work_queue_item_rate": 0,
"work_queue_items": 4
}
}
}
}
]
}
40 changes: 40 additions & 0 deletions internal/collector/testdata/opentsdb.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
{
"results": [
{
"name": "OpenTsdbWriter",
"perfdata": [
{
"counter": false,
"crit": null,
"label": "opentsdbwriter_opentsdb_work_queue_items",
"max": null,
"min": null,
"type": "PerfdataValue",
"unit": "",
"value": 4,
"warn": null
},
{
"counter": false,
"crit": null,
"label": "opentsdbwriter_opentsdb_work_queue_item_rate",
"max": null,
"min": null,
"type": "PerfdataValue",
"unit": "",
"value": 0,
"warn": null
}
],
"status": {
"opentsdbwriter": {
"opentsdb": {
"connected": false,
"work_queue_item_rate": 0,
"work_queue_items": 4
}
}
}
}
]
}
2 changes: 1 addition & 1 deletion internal/icinga/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ const (
EndpointElasticsearchWriter = "/status/ElasticsearchWriter"
EndpointExternalCommandListener = "/status/ExternalCommandListener"
EndpointFileLogger = "/status/FileLogger"
EndpointGelfWriter = "/status/GelfWriter"
EndpointGELFWriter = "/status/GelfWriter"
EndpointGraphiteWriter = "/status/GraphiteWriter"
EndpointIdoMysqlConnection = "/status/IdoMysqlConnection"
EndpointIdoPgsqlConnection = "/status/IdoPgsqlConnection"
Expand Down