diff --git a/config.example.json b/config.example.json index 2a4fec3..405d936 100644 --- a/config.example.json +++ b/config.example.json @@ -15,11 +15,13 @@ "skip_vhost": "^$", "include_vhost": ".*", "rabbit_capabilities": "no_sort,bert", + "aliveness_vhost": "/", "enabled_exporters": [ "exchange", "node", "overview", - "queue" + "queue", + "aliveness" ], "timeout": 30, "max_queues": 0 diff --git a/config.go b/config.go index 03ac6be..7115adc 100644 --- a/config.go +++ b/config.go @@ -30,6 +30,7 @@ var ( SkipVHost: regexp.MustCompile("^$"), IncludeVHost: regexp.MustCompile(".*"), RabbitCapabilities: parseCapabilities("no_sort,bert"), + AlivenessVhost: "/", EnabledExporters: []string{"exchange", "node", "overview", "queue"}, Timeout: 30, MaxQueues: 0, @@ -58,6 +59,7 @@ type rabbitExporterConfig struct { IncludeVHostString string `json:"include_vhost"` RabbitCapabilitiesString string `json:"rabbit_capabilities"` RabbitCapabilities rabbitCapabilitySet `json:"-"` + AlivenessVhost string `json:"aliveness_vhost"` EnabledExporters []string `json:"enabled_exporters"` Timeout int `json:"timeout"` MaxQueues int `json:"max_queues"` @@ -195,6 +197,10 @@ func initConfig() { config.EnabledExporters = strings.Split(enabledExporters, ",") } + if alivenessVhost := os.Getenv("ALIVENESS_VHOST"); alivenessVhost != "" { + config.AlivenessVhost = alivenessVhost + } + if timeout := os.Getenv("RABBIT_TIMEOUT"); timeout != "" { t, err := strconv.Atoi(timeout) if err != nil { diff --git a/exporter_aliveness.go b/exporter_aliveness.go new file mode 100644 index 0000000..3300e02 --- /dev/null +++ b/exporter_aliveness.go @@ -0,0 +1,104 @@ +package main + +import ( + "context" + + "github.com/prometheus/client_golang/prometheus" + log "github.com/sirupsen/logrus" +) + +func init() { + RegisterExporter("aliveness", newExporterAliveness) +} + +var ( + alivenessLabels = []string{"vhost"} + + alivenessGaugeVec = map[string]*prometheus.GaugeVec{ + "vhost.aliveness": newGaugeVec("aliveness_test", "vhost aliveness test", alivenessLabels), + } + + rabbitmqAlivenessMetric = prometheus.NewGaugeVec( + prometheus.GaugeOpts{ + Name: "rabbitmq_aliveness_info", + Help: "A metric with value 1 status:ok else 0 labeled by aliveness test status, error, reason", + }, + []string{"status", "error", "reason"}, + ) +) + +type exporterAliveness struct { + alivenessMetrics map[string]*prometheus.GaugeVec + alivenessInfo AlivenessInfo +} + +type AlivenessInfo struct { + Status string + Error string + Reason string +} + +func newExporterAliveness() Exporter { + alivenessGaugeVecActual := alivenessGaugeVec + + if len(config.ExcludeMetrics) > 0 { + for _, metric := range config.ExcludeMetrics { + if alivenessGaugeVecActual[metric] != nil { + delete(alivenessGaugeVecActual, metric) + } + } + } + + return &exporterAliveness{ + alivenessMetrics: alivenessGaugeVecActual, + alivenessInfo: AlivenessInfo{}, + } +} + +func (e *exporterAliveness) Collect(ctx context.Context, ch chan<- prometheus.Metric) error { + body, contentType, err := apiRequest(config, "aliveness-test") + if err != nil { + return err + } + + reply, err := MakeReply(contentType, body) + if err != nil { + return err + } + + rabbitMqAlivenessData := reply.MakeMap() + + e.alivenessInfo.Status, _ = reply.GetString("status") + e.alivenessInfo.Error, _ = reply.GetString("error") + e.alivenessInfo.Reason, _ = reply.GetString("reason") + + rabbitmqAlivenessMetric.Reset() + var flag float64 = 0 + if e.alivenessInfo.Status == "ok" { + flag = 1 + } + rabbitmqAlivenessMetric.WithLabelValues(e.alivenessInfo.Status, e.alivenessInfo.Error, e.alivenessInfo.Reason).Set(flag) + + log.WithField("alivenesswData", rabbitMqAlivenessData).Debug("Aliveness data") + for key, gauge := range e.alivenessMetrics { + if value, ok := rabbitMqAlivenessData[key]; ok { + log.WithFields(log.Fields{"key": key, "value": value}).Debug("Set aliveness metric for key") + gauge.WithLabelValues(e.alivenessInfo.Status).Set(value) + } + } + + if ch != nil { + rabbitmqAlivenessMetric.Collect(ch) + for _, gauge := range e.alivenessMetrics { + gauge.Collect(ch) + } + } + return nil +} + +func (e exporterAliveness) Describe(ch chan<- *prometheus.Desc) { + rabbitmqVersionMetric.Describe(ch) + for _, gauge := range e.alivenessMetrics { + gauge.Describe(ch) + } +} diff --git a/rabbitClient.go b/rabbitClient.go index 5796c23..7014df4 100644 --- a/rabbitClient.go +++ b/rabbitClient.go @@ -6,6 +6,7 @@ import ( "errors" "io/ioutil" "net/http" + "net/url" "os" "time" @@ -67,6 +68,11 @@ func apiRequest(config rabbitExporterConfig, endpoint string) ([]byte, string, e args = "?sort=" } + if endpoint == "aliveness-test" { + escapeAlivenessVhost := url.QueryEscape(config.AlivenessVhost) + args = "/" + escapeAlivenessVhost + } + req, err := http.NewRequest("GET", config.RabbitURL+"/api/"+endpoint+args, nil) if err != nil { log.WithFields(log.Fields{"error": err, "host": config.RabbitURL}).Error("Error while constructing rabbitHost request")