✨ Update Go module dependencies and enhance NATS consumer metrics. Add github.com/kylelemons/godebug as an indirect dependency. Upgrade github.com/prometheus/common to version 0.67.3. Change NATS consumer mode to "jetstream" in the development configuration. Improve observability by ensuring metrics for pending messages are initialized and recorded correctly in JetStream mode. Update documentation to reflect changes in metrics handling and consumer behavior.
This commit is contained in:
@@ -255,7 +255,21 @@ func RecordJSAPICall(operation string) {
|
||||
// JetStream consumer as a gauge, enabling backlog / lag alerts.
|
||||
func RecordNATSConsumerPending(stream, consumer string, pending uint64) {
|
||||
ensureCollectors()
|
||||
natsConsumerPending.WithLabelValues(labelValue(stream), labelValue(consumer)).Set(float64(pending))
|
||||
|
||||
// Validate inputs to ensure metric is recorded correctly
|
||||
streamLabel := labelValue(stream)
|
||||
consumerLabel := labelValue(consumer)
|
||||
|
||||
// Ensure metric is always set, even with empty labels (will be "unknown")
|
||||
if streamLabel == "" {
|
||||
streamLabel = "unknown"
|
||||
}
|
||||
if consumerLabel == "" {
|
||||
consumerLabel = "unknown"
|
||||
}
|
||||
|
||||
// Set the metric value
|
||||
natsConsumerPending.WithLabelValues(streamLabel, consumerLabel).Set(float64(pending))
|
||||
}
|
||||
|
||||
func labelValue(value string) string {
|
||||
|
||||
@@ -3,9 +3,12 @@ package metrics
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/prometheus/client_golang/prometheus/testutil"
|
||||
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
@@ -16,6 +19,10 @@ func TestMetrics(t *testing.T) {
|
||||
}
|
||||
|
||||
var _ = Describe("Metrics", func() {
|
||||
BeforeEach(func() {
|
||||
resetMetricsForTest()
|
||||
})
|
||||
|
||||
Describe("Handler", func() {
|
||||
It("serves metrics with the correct content type", func() {
|
||||
req := httptest.NewRequest(http.MethodGet, "/metrics", nil)
|
||||
@@ -29,25 +36,69 @@ var _ = Describe("Metrics", func() {
|
||||
})
|
||||
|
||||
Describe("labelValue helper", func() {
|
||||
It("returns unknown for empty values and lowercases input", func() {
|
||||
It("normalizes empty and uppercase values", func() {
|
||||
Expect(labelValue(" ")).To(Equal("unknown"))
|
||||
Expect(labelValue("SOME_VALUE")).To(Equal("some_value"))
|
||||
})
|
||||
})
|
||||
|
||||
Describe("record helpers", func() {
|
||||
It("can be invoked without panicking", func() {
|
||||
Expect(func() {
|
||||
RecordProcessed("parsed", "ARR", 150*time.Millisecond)
|
||||
RecordFailure("parser")
|
||||
RecordMessageHandled("TEST", "consumer", ResultOK, 205*time.Millisecond)
|
||||
RecordRetry("TEST", "consumer", RetryReasonProcessorError)
|
||||
RecordDLQMessage("TEST", "consumer")
|
||||
RecordDLQPublishFailure("TEST", "consumer")
|
||||
RecordDBQuery("insert", DBResultOK, 10*time.Millisecond)
|
||||
RecordJSAPICall("publish")
|
||||
RecordNATSConsumerPending("TEST", "consumer", 7)
|
||||
}).NotTo(Panic())
|
||||
It("record counters and histograms for message processing", func() {
|
||||
RecordProcessed("PARSED", "ARR", 150*time.Millisecond)
|
||||
RecordFailure("parser")
|
||||
RecordMessageHandled("stream1", "consumer1", ResultOK, 80*time.Millisecond)
|
||||
RecordRetry("stream1", "consumer1", RetryReasonProcessorError)
|
||||
RecordDLQMessage("stream1", "consumer1")
|
||||
RecordDLQPublishFailure("stream1", "consumer1")
|
||||
RecordPublishFailure("flightPlan")
|
||||
RecordDBQuery("insert_one", DBResultOK, 10*time.Millisecond)
|
||||
RecordJSAPICall("publish")
|
||||
|
||||
Expect(testutil.ToFloat64(processedCounter.WithLabelValues("parsed", "arr"))).To(BeNumerically("==", 1))
|
||||
Expect(testutil.CollectAndCount(parseLatency, MetricParseLatencySeconds)).To(Equal(1))
|
||||
|
||||
Expect(testutil.ToFloat64(failureCounter.WithLabelValues("parser"))).To(BeNumerically("==", 1))
|
||||
|
||||
Expect(testutil.ToFloat64(messagesTotal.WithLabelValues("stream1", "consumer1", ResultOK))).To(BeNumerically("==", 1))
|
||||
Expect(testutil.CollectAndCount(handleLatency, MetricHandleLatencySeconds)).To(Equal(1))
|
||||
|
||||
Expect(testutil.ToFloat64(retriesTotal.WithLabelValues("stream1", "consumer1", RetryReasonProcessorError))).To(BeNumerically("==", 1))
|
||||
|
||||
Expect(testutil.ToFloat64(dlqMessagesTotal.WithLabelValues("stream1", "consumer1"))).To(BeNumerically("==", 1))
|
||||
Expect(testutil.ToFloat64(dlqPublishFailures.WithLabelValues("stream1", "consumer1"))).To(BeNumerically("==", 1))
|
||||
|
||||
Expect(testutil.ToFloat64(publishFailuresTotal.WithLabelValues("flightplan"))).To(BeNumerically("==", 1))
|
||||
|
||||
Expect(testutil.ToFloat64(dbQueriesTotal.WithLabelValues("insert_one", DBResultOK))).To(BeNumerically("==", 1))
|
||||
Expect(testutil.CollectAndCount(dbQueryLatency, MetricDBQueryLatencySeconds)).To(Equal(1))
|
||||
|
||||
Expect(testutil.ToFloat64(jsAPICallsTotal.WithLabelValues("publish"))).To(BeNumerically("==", 1))
|
||||
})
|
||||
|
||||
It("records gauge values for pending messages", func() {
|
||||
RecordNATSConsumerPending("STREAM_A", "consumerA", 42)
|
||||
Expect(testutil.ToFloat64(natsConsumerPending.WithLabelValues("stream_a", "consumera"))).To(BeNumerically("==", 42))
|
||||
|
||||
RecordNATSConsumerPending("STREAM_A", "consumerA", 5)
|
||||
Expect(testutil.ToFloat64(natsConsumerPending.WithLabelValues("stream_a", "consumera"))).To(BeNumerically("==", 5))
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
func resetMetricsForTest() {
|
||||
once = sync.Once{}
|
||||
registry = nil
|
||||
processedCounter = nil
|
||||
failureCounter = nil
|
||||
parseLatency = nil
|
||||
messagesTotal = nil
|
||||
handleLatency = nil
|
||||
retriesTotal = nil
|
||||
jsAPICallsTotal = nil
|
||||
dlqMessagesTotal = nil
|
||||
dlqPublishFailures = nil
|
||||
publishFailuresTotal = nil
|
||||
dbQueriesTotal = nil
|
||||
dbQueryLatency = nil
|
||||
natsConsumerPending = nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user