Skip to content

Commit 7ad8610

Browse files
committed
Add NativeHA metrics to prometheus export
1 parent 3d422d9 commit 7ad8610

File tree

6 files changed

+113
-33
lines changed

6 files changed

+113
-33
lines changed

internal/metrics/exporter.go

Lines changed: 31 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
© Copyright IBM Corporation 2018, 2019
2+
© Copyright IBM Corporation 2018, 2025
33
44
Licensed under the Apache License, Version 2.0 (the "License");
55
you may not use this file except in compliance with the License.
@@ -18,16 +18,20 @@ limitations under the License.
1818
package metrics
1919

2020
import (
21+
"strings"
22+
2123
"github.com/ibm-messaging/mq-container/pkg/logger"
2224
"github.com/prometheus/client_golang/prometheus"
2325
)
2426

2527
const (
26-
namespace = "ibmmq"
27-
qmgrPrefix = "qmgr"
28-
qmgrLabel = "qmgr"
29-
objectPrefix = "object"
30-
objectLabel = "object"
28+
namespace = "ibmmq"
29+
qmgrPrefix = "qmgr"
30+
qmgrLabel = "qmgr"
31+
objectPrefix = "object"
32+
objectLabel = "object"
33+
nhaInstancePrefix = "nha"
34+
nhaInstanceLabel = "instance"
3135
)
3236

3337
type exporter struct {
@@ -58,15 +62,15 @@ func (e *exporter) Describe(ch chan<- *prometheus.Desc) {
5862

5963
if metric.isDelta {
6064
// For delta type metrics - allocate a Prometheus Counter
61-
counterVec := createCounterVec(metric.name, metric.description, metric.objectType)
65+
counterVec := createCounterVec(metric.name, metric.description, metric.objectType, metric.nhaType)
6266
e.counterMap[key] = counterVec
6367

6468
// Describe metric
6569
counterVec.Describe(ch)
6670

6771
} else {
6872
// For non-delta type metrics - allocate a Prometheus Gauge
69-
gaugeVec := createGaugeVec(metric.name, metric.description, metric.objectType)
73+
gaugeVec := createGaugeVec(metric.name, metric.description, metric.objectType, metric.nhaType)
7074
e.gaugeMap[key] = gaugeVec
7175

7276
// Describe metric
@@ -96,6 +100,9 @@ func (e *exporter) Collect(ch chan<- prometheus.Metric) {
96100

97101
if label == qmgrLabelValue {
98102
counter, err = counterVec.GetMetricWithLabelValues(e.qmName)
103+
} else if strings.HasPrefix(label, nhaLabelValue) {
104+
nhaInstance := strings.ReplaceAll(label, nhaLabelValue, "")
105+
counter, err = counterVec.GetMetricWithLabelValues(nhaInstance, e.qmName)
99106
} else {
100107
counter, err = counterVec.GetMetricWithLabelValues(label, e.qmName)
101108
}
@@ -124,6 +131,9 @@ func (e *exporter) Collect(ch chan<- prometheus.Metric) {
124131

125132
if label == qmgrLabelValue {
126133
gauge, err = gaugeVec.GetMetricWithLabelValues(e.qmName)
134+
} else if strings.HasPrefix(label, nhaLabelValue) {
135+
nhaInstance := strings.ReplaceAll(label, nhaLabelValue, "")
136+
gauge, err = gaugeVec.GetMetricWithLabelValues(nhaInstance, e.qmName)
127137
} else {
128138
gauge, err = gaugeVec.GetMetricWithLabelValues(label, e.qmName)
129139
}
@@ -146,9 +156,9 @@ func (e *exporter) Collect(ch chan<- prometheus.Metric) {
146156
}
147157

148158
// createCounterVec returns a Prometheus CounterVec populated with metric details
149-
func createCounterVec(name, description string, objectType bool) *prometheus.CounterVec {
159+
func createCounterVec(name, description string, objectType bool, nhaType bool) *prometheus.CounterVec {
150160

151-
prefix, labels := getVecDetails(objectType)
161+
prefix, labels := getVecDetails(objectType, nhaType)
152162

153163
counterVec := prometheus.NewCounterVec(
154164
prometheus.CounterOpts{
@@ -162,9 +172,9 @@ func createCounterVec(name, description string, objectType bool) *prometheus.Cou
162172
}
163173

164174
// createGaugeVec returns a Prometheus GaugeVec populated with metric details
165-
func createGaugeVec(name, description string, objectType bool) *prometheus.GaugeVec {
175+
func createGaugeVec(name, description string, objectType bool, nhaType bool) *prometheus.GaugeVec {
166176

167-
prefix, labels := getVecDetails(objectType)
177+
prefix, labels := getVecDetails(objectType, nhaType)
168178

169179
gaugeVec := prometheus.NewGaugeVec(
170180
prometheus.GaugeOpts{
@@ -178,14 +188,14 @@ func createGaugeVec(name, description string, objectType bool) *prometheus.Gauge
178188
}
179189

180190
// getVecDetails returns the required prefix and labels for a metric
181-
func getVecDetails(objectType bool) (prefix string, labels []string) {
182-
183-
prefix = qmgrPrefix
184-
labels = []string{qmgrLabel}
185-
186-
if objectType {
187-
prefix = objectPrefix
188-
labels = []string{objectLabel, qmgrLabel}
191+
func getVecDetails(objectType bool, nhaType bool) (prefix string, labels []string) {
192+
switch true {
193+
case objectType:
194+
return objectPrefix, []string{objectLabel, qmgrLabel}
195+
case nhaType:
196+
return nhaInstancePrefix, []string{nhaInstanceLabel, qmgrLabel}
197+
default:
198+
return qmgrPrefix, []string{qmgrLabel}
189199
}
190-
return prefix, labels
200+
191201
}

internal/metrics/exporter_test.go

Lines changed: 38 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -84,9 +84,9 @@ func testCollect(t *testing.T, isDelta bool) {
8484

8585
exporter := newExporter("qmName", log)
8686
if isDelta {
87-
exporter.counterMap[testKey1] = createCounterVec(testElement1Name, testElement1Description, false)
87+
exporter.counterMap[testKey1] = createCounterVec(testElement1Name, testElement1Description, false, false)
8888
} else {
89-
exporter.gaugeMap[testKey1] = createGaugeVec(testElement1Name, testElement1Description, false)
89+
exporter.gaugeMap[testKey1] = createGaugeVec(testElement1Name, testElement1Description, false, false)
9090
}
9191

9292
for i := 1; i <= 3; i++ {
@@ -142,7 +142,7 @@ func testCollect(t *testing.T, isDelta bool) {
142142
func TestCreateCounterVec(t *testing.T) {
143143

144144
ch := make(chan *prometheus.Desc)
145-
counterVec := createCounterVec("MetricName", "MetricDescription", false)
145+
counterVec := createCounterVec("MetricName", "MetricDescription", false, false)
146146
go func() {
147147
counterVec.Describe(ch)
148148
}()
@@ -158,7 +158,7 @@ func TestCreateCounterVec(t *testing.T) {
158158
func TestCreateCounterVec_ObjectLabel(t *testing.T) {
159159

160160
ch := make(chan *prometheus.Desc)
161-
counterVec := createCounterVec("MetricName", "MetricDescription", true)
161+
counterVec := createCounterVec("MetricName", "MetricDescription", true, false)
162162
go func() {
163163
counterVec.Describe(ch)
164164
}()
@@ -171,10 +171,26 @@ func TestCreateCounterVec_ObjectLabel(t *testing.T) {
171171
}
172172
}
173173

174+
func TestCreateCounterVec_NHALabel(t *testing.T) {
175+
176+
ch := make(chan *prometheus.Desc)
177+
counterVec := createCounterVec("MetricName", "MetricDescription", false, true)
178+
go func() {
179+
counterVec.Describe(ch)
180+
}()
181+
description := <-ch
182+
183+
expected := "Desc{fqName: \"ibmmq_nha_MetricName\", help: \"MetricDescription\", constLabels: {}, variableLabels: [instance qmgr]}"
184+
actual := description.String()
185+
if actual != expected {
186+
t.Errorf("Expected value=%s; actual %s", expected, actual)
187+
}
188+
}
189+
174190
func TestCreateGaugeVec(t *testing.T) {
175191

176192
ch := make(chan *prometheus.Desc)
177-
gaugeVec := createGaugeVec("MetricName", "MetricDescription", false)
193+
gaugeVec := createGaugeVec("MetricName", "MetricDescription", false, false)
178194
go func() {
179195
gaugeVec.Describe(ch)
180196
}()
@@ -190,7 +206,7 @@ func TestCreateGaugeVec(t *testing.T) {
190206
func TestCreateGaugeVec_ObjectLabel(t *testing.T) {
191207

192208
ch := make(chan *prometheus.Desc)
193-
gaugeVec := createGaugeVec("MetricName", "MetricDescription", true)
209+
gaugeVec := createGaugeVec("MetricName", "MetricDescription", true, false)
194210
go func() {
195211
gaugeVec.Describe(ch)
196212
}()
@@ -202,3 +218,19 @@ func TestCreateGaugeVec_ObjectLabel(t *testing.T) {
202218
t.Errorf("Expected value=%s; actual %s", expected, actual)
203219
}
204220
}
221+
222+
func TestCreateGaugeVec_NHALabel(t *testing.T) {
223+
224+
ch := make(chan *prometheus.Desc)
225+
gaugeVec := createGaugeVec("MetricName", "MetricDescription", false, true)
226+
go func() {
227+
gaugeVec.Describe(ch)
228+
}()
229+
description := <-ch
230+
231+
expected := "Desc{fqName: \"ibmmq_nha_MetricName\", help: \"MetricDescription\", constLabels: {}, variableLabels: [instance qmgr]}"
232+
actual := description.String()
233+
if actual != expected {
234+
t.Errorf("Expected value=%s; actual %s", expected, actual)
235+
}
236+
}

internal/metrics/mapping.go

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,39 @@ func generateMetricNamesMap() map[string]metricLookup {
124124
"STATMQI/GET/Failed MQCB count": metricLookup{"failed_mqcb_total", true},
125125
"STATMQI/SYNCPOINT/Commit count": metricLookup{"commit_total", true},
126126
"STATMQI/SYNCPOINT/Rollback count": metricLookup{"rollback_total", true},
127+
"NHAREPLICA/REPLICATION/Catch-up log bytes sent": metricLookup{"replication_catchup_log_sent_bytes", true},
128+
"NHAREPLICA/REPLICATION/MQ FDC file count": metricLookup{"replication_mq_fdc_file_count", true},
129+
"NHAREPLICA/REPLICATION/Catch-up log bytes decompressed": metricLookup{"replication_catch_up_log_decompressed_bytes", true},
130+
"NHAREPLICA/REPLICATION/Acknowledged log sequence number": metricLookup{"replication_acknowledged_log_sequence_number_total", true},
131+
"NHAREPLICA/REPLICATION/Log file system - bytes in use": metricLookup{"replication_log_file_system_in_use_bytes", true},
132+
"NHAREPLICA/REPLICATION/Log file system - free space": metricLookup{"replication_log_file_system_free_space_percent", true},
133+
"NHAREPLICA/REPLICATION/Synchronous log bytes sent": metricLookup{"replication_synchronous_log_sent_bytes", true},
134+
"NHAREPLICA/REPLICATION/Log write average acknowledgement size": metricLookup{"replication_log_write_average_acknowledgement_size_bytes", true},
135+
"NHAREPLICA/REPLICATION/Backlog average bytes": metricLookup{"replication_backlog_average_bytes", true},
136+
"NHAREPLICA/REPLICATION/Synchronous compressed log bytes sent": metricLookup{"replication_synchronous_compressed_log_sent_bytes", true},
137+
"NHAREPLICA/REPLICATION/Synchronous uncompressed log bytes sent": metricLookup{"replication_synchronous_uncompressed_log_sent_bytes", true},
138+
"NHAREPLICA/REPLICATION/Synchronous log data average compression time": metricLookup{"replication_synchronous_log_data_average_compression_time_seconds", true},
139+
"NHAREPLICA/REPLICATION/Catch-up log data average compression time": metricLookup{"replication_catch_up_log_data_average_compression_time_seconds", true},
140+
"NHAREPLICA/REPLICATION/Average network round trip time": metricLookup{"replication_average_network_round_trip_time_seconds", true},
141+
"NHAREPLICA/REPLICATION/Synchronous log bytes decompressed": metricLookup{"replication_synchronous_log_decompressed_bytes", true},
142+
"NHAREPLICA/REPLICATION/Synchronous log data average decompression time": metricLookup{"replication_synchronous_log_data_average_decompression_time_seconds", true},
143+
"NHAREPLICA/REPLICATION/Queue Manager file system - free space": metricLookup{"replication_queue_manager_file_system_free_space_percent", true},
144+
"NHAREPLICA/REPLICATION/Catch-up log data average decompression time": metricLookup{"replication_catch_up_log_data_average_decompression_time_seconds", true},
145+
"NHAREPLICA/REPLICATION/Log write average acknowledgement latency": metricLookup{"replication_log_write_average_acknowledgement_latency_seconds", true},
146+
"NHAREPLICA/REPLICATION/Backlog bytes": metricLookup{"replication_backlog_bytes", true},
147+
"NHAREPLICA/REPLICATION/Catch-up compressed log bytes sent": metricLookup{"replication_catch_up_compressed_log_sent_bytes", true},
148+
"NHAREPLICA/REPLICATION/Catch-up uncompressed log bytes sent": metricLookup{"replication_catch_up_uncompressed_log_sent_bytes", true},
149+
"NHAREPLICA/REPLICATION/Queue Manager file system - bytes in use": metricLookup{"replication_queue_manager_file_system_in_use_bytes", true},
150+
"NHAREPLICA/RECOVERY/Compressed log bytes sent": metricLookup{"recovery_compressed_log_sent_bytes", true},
151+
"NHAREPLICA/RECOVERY/Backlog average bytes": metricLookup{"recovery_backlog_average_bytes", true},
152+
"NHAREPLICA/RECOVERY/Rebase count": metricLookup{"recovery_rebase_count", true},
153+
"NHAREPLICA/RECOVERY/Average network round trip time": metricLookup{"recovery_average_network_round_trip_time_seconds", true},
154+
"NHAREPLICA/RECOVERY/Log data average compression time": metricLookup{"recovery_log_data_average_compression_time_seconds", true},
155+
"NHAREPLICA/RECOVERY/Log bytes decompressed": metricLookup{"recovery_log_decompressed_bytes", true},
156+
"NHAREPLICA/RECOVERY/Log data average decompression time": metricLookup{"recovery_log_data_average_decompression_time_seconds", true},
157+
"NHAREPLICA/RECOVERY/Log bytes sent": metricLookup{"recovery_log_sent_bytes", true},
158+
"NHAREPLICA/RECOVERY/Backlog bytes": metricLookup{"recovery_backlog_bytes", true},
159+
"NHAREPLICA/RECOVERY/Recovery log sequence number": metricLookup{"recovery_recovery_log_sequence_number", true},
127160
}
128161
return metricNamesMap
129162
}

internal/metrics/mapping_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,8 @@ func TestGenerateMetricNamesMap(t *testing.T) {
2121

2222
metricNamesMap := generateMetricNamesMap()
2323

24-
if len(metricNamesMap) != 98 {
25-
t.Errorf("Expected mapping-size=%d; actual %d", 98, len(metricNamesMap))
24+
if len(metricNamesMap) != 131 {
25+
t.Errorf("Expected mapping-size=%d; actual %d", 131, len(metricNamesMap))
2626
}
2727

2828
actual, ok := metricNamesMap[testKey1]

internal/metrics/update.go

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import (
2929

3030
const (
3131
qmgrLabelValue = mqmetric.QMgrMapKey
32+
nhaLabelValue = mqmetric.NativeHAKeyPrefix
3233
requestTimeout = 10
3334
)
3435

@@ -43,6 +44,7 @@ type metricData struct {
4344
name string
4445
description string
4546
objectType bool
47+
nhaType bool
4648
values map[string]float64
4749
isDelta bool
4850
}
@@ -139,9 +141,9 @@ func initialiseMetrics(log *logger.Logger) (map[string]*metricData, error) {
139141

140142
for _, metricClass := range mqmetric.Metrics.Classes {
141143
for _, metricType := range metricClass.Types {
142-
if !strings.Contains(metricType.ObjectTopic, "%s") {
144+
isNHA := metricClass.Name == "NHAREPLICA"
145+
if isNHA || !strings.Contains(metricType.ObjectTopic, "%s") {
143146
for _, metricElement := range metricType.Elements {
144-
145147
// Get unique metric key
146148
key := makeKey(metricElement)
147149

@@ -162,6 +164,7 @@ func initialiseMetrics(log *logger.Logger) (map[string]*metricData, error) {
162164
name: metricLookup.name,
163165
description: metricElement.Description,
164166
isDelta: isDelta,
167+
nhaType: isNHA,
165168
}
166169

167170
// Add metric
@@ -193,8 +196,9 @@ func initialiseMetrics(log *logger.Logger) (map[string]*metricData, error) {
193196
func updateMetrics(metrics map[string]*metricData) {
194197

195198
for _, metricClass := range mqmetric.Metrics.Classes {
199+
isNHA := metricClass.Name == "NHAREPLICA"
196200
for _, metricType := range metricClass.Types {
197-
if !strings.Contains(metricType.ObjectTopic, "%s") {
201+
if isNHA || !strings.Contains(metricType.ObjectTopic, "%s") {
198202
for _, metricElement := range metricType.Elements {
199203

200204
// Unexpected metric elements (with no defined mapping) are handled in 'initialiseMetrics'

test/container/mqmetric_test_util.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -185,11 +185,12 @@ func metricsContainerConfig() *ce.ContainerConfig {
185185

186186
func metricNames() []string {
187187

188-
// NB: There are currently a total of 98 metrics, but the following 4 do not generate values (based on the queue manager configuration)
188+
// NB: There are currently a total of 131 metrics, but the following 37 do not generate values (based on the queue manager configuration)
189189
// - log_occupied_by_reusable_extents_bytes
190190
// - log_occupied_by_extents_waiting_to_be_archived_bytes
191191
// - log_required_for_media_recovery_bytes
192192
// - log_sequence_number_quorum_total (Only produced by NativeHA Queue Managers)
193+
// - 33 "recovery_*" and "replication_*" metrics (Only produced by NativeHA Queue Managers)
193194

194195
names := []string{
195196
"cpu_load_one_minute_average_percentage",

0 commit comments

Comments
 (0)