diff --git a/internal/collector/api.go b/internal/collector/api.go index 1308733..db1a282 100644 --- a/internal/collector/api.go +++ b/internal/collector/api.go @@ -64,7 +64,7 @@ func (collector *Icinga2APICollector) Describe(ch chan<- *prometheus.Desc) { } func (collector *Icinga2APICollector) Collect(ch chan<- prometheus.Metric) { - result, err := collector.icingaClient.GetAPIMetrics() + result, err := collector.icingaClient.GetAPIListenerMetrics() if err != nil { collector.logger.Error("Could not retrieve ApiListener metrics", "error", err.Error()) diff --git a/internal/collector/api_test.go b/internal/collector/api_test.go index 29755d2..b5de633 100644 --- a/internal/collector/api_test.go +++ b/internal/collector/api_test.go @@ -21,7 +21,7 @@ func TestIcinga2APICollector_Collect(t *testing.T) { data, _ := os.ReadFile("testdata/api.json") json.Unmarshal(data, &result) - client.SetAPIMetrics(result) + client.SetAPIListenerMetrics(result) logger := slog.New(slog.NewTextHandler(io.Discard, nil)) collector := NewIcinga2APICollector(client, logger) diff --git a/internal/collector/cib.go b/internal/collector/cib.go index c157d93..b934f6e 100644 --- a/internal/collector/cib.go +++ b/internal/collector/cib.go @@ -202,187 +202,66 @@ func (collector *Icinga2CIBCollector) Collect(ch chan<- prometheus.Metric) { r := result.Results[0] - if v, ok := r.Status["uptime"]; ok { - ch <- prometheus.MustNewConstMetric(collector.uptime, prometheus.CounterValue, v) - } - - if v, ok := r.Status["avg_execution_time"]; ok { - ch <- prometheus.MustNewConstMetric(collector.avg_execution_time, prometheus.GaugeValue, v) - } - - if v, ok := r.Status["avg_latency"]; ok { - ch <- prometheus.MustNewConstMetric(collector.avg_latency, prometheus.GaugeValue, v) - } - - if v, ok := r.Status["max_execution_time"]; ok { - ch <- prometheus.MustNewConstMetric(collector.max_execution_time, prometheus.GaugeValue, v) - } - - if v, ok := r.Status["max_latency"]; ok { - ch <- prometheus.MustNewConstMetric(collector.max_latency, prometheus.GaugeValue, v) - } - - if v, ok := r.Status["min_execution_time"]; ok { - ch <- prometheus.MustNewConstMetric(collector.min_execution_time, prometheus.GaugeValue, v) - } - - if v, ok := r.Status["min_latency"]; ok { - ch <- prometheus.MustNewConstMetric(collector.min_latency, prometheus.GaugeValue, v) - } - - if v, ok := r.Status["current_concurrent_checks"]; ok { - ch <- prometheus.MustNewConstMetric(collector.current_concurrent_checks, prometheus.GaugeValue, v) - } - - if v, ok := r.Status["current_pending_callbacks"]; ok { - ch <- prometheus.MustNewConstMetric(collector.current_pending_callbacks, prometheus.GaugeValue, v) - } + // Icinga Statistics + emitMetric(ch, collector.uptime, prometheus.CounterValue, r.Status, "uptime") + emitMetric(ch, collector.avg_execution_time, prometheus.GaugeValue, r.Status, "avg_execution_time") + emitMetric(ch, collector.avg_latency, prometheus.GaugeValue, r.Status, "avg_latency") + emitMetric(ch, collector.max_execution_time, prometheus.GaugeValue, r.Status, "max_execution_time") + emitMetric(ch, collector.max_latency, prometheus.GaugeValue, r.Status, "max_latency") + emitMetric(ch, collector.min_execution_time, prometheus.GaugeValue, r.Status, "min_execution_time") + emitMetric(ch, collector.min_latency, prometheus.GaugeValue, r.Status, "min_latency") + emitMetric(ch, collector.current_concurrent_checks, prometheus.GaugeValue, r.Status, "current_concurrent_checks") + emitMetric(ch, collector.current_pending_callbacks, prometheus.GaugeValue, r.Status, "current_pending_callbacks") + emitMetric(ch, collector.remote_check_queue, prometheus.GaugeValue, r.Status, "remote_check_queue") - if v, ok := r.Status["remote_check_queue"]; ok { - ch <- prometheus.MustNewConstMetric(collector.remote_check_queue, prometheus.GaugeValue, v) - } // Active Checks - if v, ok := r.Status["active_host_checks"]; ok { - ch <- prometheus.MustNewConstMetric(collector.active_host_checks, prometheus.GaugeValue, v) - } - - if v, ok := r.Status["active_host_checks_15min"]; ok { - ch <- prometheus.MustNewConstMetric(collector.active_host_checks_15min, prometheus.GaugeValue, v) - } - - if v, ok := r.Status["active_host_checks_1min"]; ok { - ch <- prometheus.MustNewConstMetric(collector.active_host_checks_1min, prometheus.GaugeValue, v) - } - - if v, ok := r.Status["active_host_checks_5min"]; ok { - ch <- prometheus.MustNewConstMetric(collector.active_host_checks_5min, prometheus.GaugeValue, v) - } - - if v, ok := r.Status["active_service_checks"]; ok { - ch <- prometheus.MustNewConstMetric(collector.active_service_checks, prometheus.GaugeValue, v) - } - - if v, ok := r.Status["active_service_checks_15min"]; ok { - ch <- prometheus.MustNewConstMetric(collector.active_service_checks_15min, prometheus.GaugeValue, v) - } - - if v, ok := r.Status["active_service_checks_1min"]; ok { - ch <- prometheus.MustNewConstMetric(collector.active_service_checks_1min, prometheus.GaugeValue, v) - } + emitMetric(ch, collector.active_host_checks, prometheus.GaugeValue, r.Status, "active_host_checks") + emitMetric(ch, collector.active_host_checks_15min, prometheus.GaugeValue, r.Status, "active_host_checks_15min") + emitMetric(ch, collector.active_host_checks_1min, prometheus.GaugeValue, r.Status, "active_host_checks_1min") + emitMetric(ch, collector.active_host_checks_5min, prometheus.GaugeValue, r.Status, "active_host_checks_5min") + emitMetric(ch, collector.active_service_checks, prometheus.GaugeValue, r.Status, "active_service_checks") + emitMetric(ch, collector.active_service_checks_15min, prometheus.GaugeValue, r.Status, "active_service_checks_15min") + emitMetric(ch, collector.active_service_checks_1min, prometheus.GaugeValue, r.Status, "active_service_checks_1min") + emitMetric(ch, collector.active_service_checks_5min, prometheus.GaugeValue, r.Status, "active_service_checks_5min") - if v, ok := r.Status["active_service_checks_5min"]; ok { - ch <- prometheus.MustNewConstMetric(collector.active_service_checks_5min, prometheus.GaugeValue, v) - } // Passive Checks - if v, ok := r.Status["passive_host_checks"]; ok { - ch <- prometheus.MustNewConstMetric(collector.passive_host_checks, prometheus.GaugeValue, v) - } - - if v, ok := r.Status["passive_host_checks_15min"]; ok { - ch <- prometheus.MustNewConstMetric(collector.passive_host_checks_15min, prometheus.GaugeValue, v) - } - - if v, ok := r.Status["passive_host_checks_1min"]; ok { - ch <- prometheus.MustNewConstMetric(collector.passive_host_checks_1min, prometheus.GaugeValue, v) - } - - if v, ok := r.Status["passive_host_checks_5min"]; ok { - ch <- prometheus.MustNewConstMetric(collector.passive_host_checks_5min, prometheus.GaugeValue, v) - } - - if v, ok := r.Status["passive_service_checks"]; ok { - ch <- prometheus.MustNewConstMetric(collector.passive_service_checks, prometheus.GaugeValue, v) - } + emitMetric(ch, collector.passive_host_checks, prometheus.GaugeValue, r.Status, "passive_host_checks") + emitMetric(ch, collector.passive_host_checks_15min, prometheus.GaugeValue, r.Status, "passive_host_checks_15min") + emitMetric(ch, collector.passive_host_checks_1min, prometheus.GaugeValue, r.Status, "passive_host_checks_1min") + emitMetric(ch, collector.passive_host_checks_5min, prometheus.GaugeValue, r.Status, "passive_host_checks_5min") + emitMetric(ch, collector.passive_service_checks, prometheus.GaugeValue, r.Status, "passive_service_checks") + emitMetric(ch, collector.passive_service_checks_15min, prometheus.GaugeValue, r.Status, "passive_service_checks_15min") + emitMetric(ch, collector.passive_service_checks_1min, prometheus.GaugeValue, r.Status, "passive_service_checks_1min") + emitMetric(ch, collector.passive_service_checks_5min, prometheus.GaugeValue, r.Status, "passive_service_checks_5min") - if v, ok := r.Status["passive_service_checks_15min"]; ok { - ch <- prometheus.MustNewConstMetric(collector.passive_service_checks_15min, prometheus.GaugeValue, v) - } - - if v, ok := r.Status["passive_service_checks_1min"]; ok { - ch <- prometheus.MustNewConstMetric(collector.passive_service_checks_1min, prometheus.GaugeValue, v) - } - - if v, ok := r.Status["passive_service_checks_5min"]; ok { - ch <- prometheus.MustNewConstMetric(collector.passive_service_checks_5min, prometheus.GaugeValue, v) - } // Hosts - if v, ok := r.Status["num_hosts_up"]; ok { - ch <- prometheus.MustNewConstMetric(collector.num_hosts_up, prometheus.GaugeValue, v) - } + emitMetric(ch, collector.num_hosts_up, prometheus.GaugeValue, r.Status, "num_hosts_up") + emitMetric(ch, collector.num_hosts_down, prometheus.GaugeValue, r.Status, "num_hosts_down") + emitMetric(ch, collector.num_hosts_acknowledged, prometheus.GaugeValue, r.Status, "num_hosts_acknowledged") + emitMetric(ch, collector.num_hosts_flapping, prometheus.GaugeValue, r.Status, "num_hosts_flapping") + emitMetric(ch, collector.num_hosts_handled, prometheus.GaugeValue, r.Status, "num_hosts_handled") + emitMetric(ch, collector.num_hosts_in_downtime, prometheus.GaugeValue, r.Status, "num_hosts_in_downtime") + emitMetric(ch, collector.num_hosts_pending, prometheus.GaugeValue, r.Status, "num_hosts_pending") + emitMetric(ch, collector.num_hosts_problem, prometheus.GaugeValue, r.Status, "num_hosts_problem") + emitMetric(ch, collector.num_hosts_unreachable, prometheus.GaugeValue, r.Status, "num_hosts_unreachable") - if v, ok := r.Status["num_hosts_down"]; ok { - ch <- prometheus.MustNewConstMetric(collector.num_hosts_down, prometheus.GaugeValue, v) - } - - if v, ok := r.Status["num_hosts_acknowledged"]; ok { - ch <- prometheus.MustNewConstMetric(collector.num_hosts_acknowledged, prometheus.GaugeValue, v) - } - - if v, ok := r.Status["num_hosts_flapping"]; ok { - ch <- prometheus.MustNewConstMetric(collector.num_hosts_flapping, prometheus.GaugeValue, v) - } - - if v, ok := r.Status["num_hosts_handled"]; ok { - ch <- prometheus.MustNewConstMetric(collector.num_hosts_handled, prometheus.GaugeValue, v) - } - - if v, ok := r.Status["num_hosts_in_downtime"]; ok { - ch <- prometheus.MustNewConstMetric(collector.num_hosts_in_downtime, prometheus.GaugeValue, v) - } - - if v, ok := r.Status["num_hosts_pending"]; ok { - ch <- prometheus.MustNewConstMetric(collector.num_hosts_pending, prometheus.GaugeValue, v) - } - - if v, ok := r.Status["num_hosts_problem"]; ok { - ch <- prometheus.MustNewConstMetric(collector.num_hosts_problem, prometheus.GaugeValue, v) - } - - if v, ok := r.Status["num_hosts_unreachable"]; ok { - ch <- prometheus.MustNewConstMetric(collector.num_hosts_unreachable, prometheus.GaugeValue, v) - } // Services - if v, ok := r.Status["num_services_ok"]; ok { - ch <- prometheus.MustNewConstMetric(collector.num_services_ok, prometheus.GaugeValue, v) - } - - if v, ok := r.Status["num_services_critical"]; ok { - ch <- prometheus.MustNewConstMetric(collector.num_services_critical, prometheus.GaugeValue, v) - } - - if v, ok := r.Status["num_services_acknowledged"]; ok { - ch <- prometheus.MustNewConstMetric(collector.num_services_acknowledged, prometheus.GaugeValue, v) - } - - if v, ok := r.Status["num_services_flapping"]; ok { - ch <- prometheus.MustNewConstMetric(collector.num_services_flapping, prometheus.GaugeValue, v) - } - - if v, ok := r.Status["num_services_handled"]; ok { - ch <- prometheus.MustNewConstMetric(collector.num_services_handled, prometheus.GaugeValue, v) - } - - if v, ok := r.Status["num_services_in_downtime"]; ok { - ch <- prometheus.MustNewConstMetric(collector.num_services_in_downtime, prometheus.GaugeValue, v) - } - - if v, ok := r.Status["num_services_pending"]; ok { - ch <- prometheus.MustNewConstMetric(collector.num_services_pending, prometheus.GaugeValue, v) - } - - if v, ok := r.Status["num_services_problem"]; ok { - ch <- prometheus.MustNewConstMetric(collector.num_services_problem, prometheus.GaugeValue, v) - } - - if v, ok := r.Status["num_services_unknown"]; ok { - ch <- prometheus.MustNewConstMetric(collector.num_services_unknown, prometheus.GaugeValue, v) - } - - if v, ok := r.Status["num_services_unreachable"]; ok { - ch <- prometheus.MustNewConstMetric(collector.num_services_unreachable, prometheus.GaugeValue, v) - } + emitMetric(ch, collector.num_services_ok, prometheus.GaugeValue, r.Status, "num_services_ok") + emitMetric(ch, collector.num_services_critical, prometheus.GaugeValue, r.Status, "num_services_critical") + emitMetric(ch, collector.num_services_acknowledged, prometheus.GaugeValue, r.Status, "num_services_acknowledged") + emitMetric(ch, collector.num_services_flapping, prometheus.GaugeValue, r.Status, "num_services_flapping") + emitMetric(ch, collector.num_services_handled, prometheus.GaugeValue, r.Status, "num_services_handled") + emitMetric(ch, collector.num_services_in_downtime, prometheus.GaugeValue, r.Status, "num_services_in_downtime") + emitMetric(ch, collector.num_services_pending, prometheus.GaugeValue, r.Status, "num_services_pending") + emitMetric(ch, collector.num_services_problem, prometheus.GaugeValue, r.Status, "num_services_problem") + emitMetric(ch, collector.num_services_unknown, prometheus.GaugeValue, r.Status, "num_services_unknown") + emitMetric(ch, collector.num_services_unreachable, prometheus.GaugeValue, r.Status, "num_services_unreachable") + emitMetric(ch, collector.num_services_warning, prometheus.GaugeValue, r.Status, "num_services_warning") +} - if v, ok := r.Status["num_services_warning"]; ok { - ch <- prometheus.MustNewConstMetric(collector.num_services_warning, prometheus.GaugeValue, v) +// emitMetric checks if the given key exists in the status map and emits the metric if so. +func emitMetric(ch chan<- prometheus.Metric, desc *prometheus.Desc, metricType prometheus.ValueType, status map[string]float64, key string) { + if v, ok := status[key]; ok { + ch <- prometheus.MustNewConstMetric(desc, metricType, v) } } diff --git a/internal/collector/testing.go b/internal/collector/testing.go index 30c97e2..cebf9c2 100644 --- a/internal/collector/testing.go +++ b/internal/collector/testing.go @@ -29,11 +29,11 @@ func (m *MockIcingaClient) GetCIBMetrics() (icinga.CIBResult, error) { return m.cib, nil } -func (m *MockIcingaClient) SetAPIMetrics(api icinga.APIResult) { +func (m *MockIcingaClient) SetAPIListenerMetrics(api icinga.APIResult) { m.api = api } -func (m *MockIcingaClient) GetAPIMetrics() (icinga.APIResult, error) { +func (m *MockIcingaClient) GetAPIListenerMetrics() (icinga.APIResult, error) { return m.api, nil } diff --git a/internal/icinga/client.go b/internal/icinga/client.go index 1269929..ad50069 100644 --- a/internal/icinga/client.go +++ b/internal/icinga/client.go @@ -11,6 +11,15 @@ import ( "time" ) +const ( + defaultDialTimeout = 10 * time.Second + defaultKeepAlive = 10 * time.Second + defaultTLSHandshake = 10 * time.Second + defaultIdleConnTimeout = 90 * time.Second + defaultMaxIdleConns = 100 + defaultMaxIdleConnsPerHost = 10 +) + const ( EndpointApiListener = "/status/ApiListener" EndpointApplication = "/status/IcingaApplication" @@ -54,11 +63,12 @@ type Client struct { } // IcingaClient is an interface that we use to simplify testing +// Note, the methods use a context internally. type IcingaClient interface { GetPerfdataMetrics(endpoint string) (PerfdataResult, error) GetCIBMetrics() (CIBResult, error) GetApplicationMetrics() (ApplicationResult, error) - GetAPIMetrics() (APIResult, error) + GetAPIListenerMetrics() (APIResult, error) GetCheckerComponentMetrics() (CheckerComponentResult, error) } @@ -79,14 +89,14 @@ func NewClient(c Config) (*Client, error) { var rt http.RoundTripper = &http.Transport{ Proxy: http.ProxyFromEnvironment, DialContext: (&net.Dialer{ - Timeout: 10 * time.Second, - KeepAlive: 10 * time.Second, + Timeout: defaultDialTimeout, + KeepAlive: defaultKeepAlive, }).DialContext, - TLSHandshakeTimeout: 10 * time.Second, + TLSHandshakeTimeout: defaultTLSHandshake, TLSClientConfig: tlsConfig, - IdleConnTimeout: 90 * time.Second, - MaxIdleConns: 100, - MaxIdleConnsPerHost: 10, + IdleConnTimeout: defaultIdleConnTimeout, + MaxIdleConns: defaultMaxIdleConns, + MaxIdleConnsPerHost: defaultMaxIdleConnsPerHost, } // Using a BasicAuth for authentication @@ -109,6 +119,7 @@ func NewClient(c Config) (*Client, error) { } // GetPerfdataMetrics returns the perfdata from a given status API endpoint +// There is some duplication here, but that is fine for now func (c *Client) GetPerfdataMetrics(endpoint string) (PerfdataResult, error) { var result PerfdataResult @@ -131,6 +142,7 @@ func (c *Client) GetPerfdataMetrics(endpoint string) (PerfdataResult, error) { return result, nil } +// GetCIBMetrics returns the Common Information Base metrics func (c *Client) GetCIBMetrics() (CIBResult, error) { var result CIBResult @@ -149,6 +161,7 @@ func (c *Client) GetCIBMetrics() (CIBResult, error) { return result, nil } +// GetApplicationMetrics returns the base application metrics func (c *Client) GetApplicationMetrics() (ApplicationResult, error) { var result ApplicationResult @@ -167,7 +180,8 @@ func (c *Client) GetApplicationMetrics() (ApplicationResult, error) { return result, nil } -func (c *Client) GetAPIMetrics() (APIResult, error) { +// GetAPIListenerMetrics returns the APIListener metrics +func (c *Client) GetAPIListenerMetrics() (APIResult, error) { var result APIResult body, errBody := c.fetchJSON(EndpointApiListener) @@ -185,6 +199,7 @@ func (c *Client) GetAPIMetrics() (APIResult, error) { return result, nil } +// GetCheckerComponentMetrics returns the CheckerComponent metrics func (c *Client) GetCheckerComponentMetrics() (CheckerComponentResult, error) { var result CheckerComponentResult @@ -203,6 +218,7 @@ func (c *Client) GetCheckerComponentMetrics() (CheckerComponentResult, error) { return result, nil } +// fetchJSON calls the given endpoint and returns the JSON result func (c *Client) fetchJSON(endpoint string) ([]byte, error) { // Lookup data in the cache we go out and bother the Icinga API if elem, ok := c.cache.Get(endpoint); ok {