From 279123f4a7ceb427b280e55fb8c707d2f2c9d114 Mon Sep 17 00:00:00 2001 From: Shyunn <114235843+ShyunnY@users.noreply.github.com> Date: Sat, 29 Jul 2023 16:51:43 +0800 Subject: [PATCH] feat: add prometheus summary metrics (#3440) Co-authored-by: chen quan --- core/metric/histogram_test.go | 3 -- core/metric/summary.go | 65 +++++++++++++++++++++++++++++++++ core/metric/summary_test.go | 68 +++++++++++++++++++++++++++++++++++ core/proc/shutdown.go | 2 ++ 4 files changed, 135 insertions(+), 3 deletions(-) create mode 100644 core/metric/summary.go create mode 100644 core/metric/summary_test.go diff --git a/core/metric/histogram_test.go b/core/metric/histogram_test.go index 4c2f8933..4874617b 100644 --- a/core/metric/histogram_test.go +++ b/core/metric/histogram_test.go @@ -6,7 +6,6 @@ import ( "github.com/prometheus/client_golang/prometheus/testutil" "github.com/stretchr/testify/assert" - "github.com/zeromicro/go-zero/core/proc" ) func TestNewHistogramVec(t *testing.T) { @@ -48,6 +47,4 @@ func TestHistogramObserve(t *testing.T) { err := testutil.CollectAndCompare(hv.histogram, strings.NewReader(metadata+val)) assert.Nil(t, err) - - proc.Shutdown() } diff --git a/core/metric/summary.go b/core/metric/summary.go new file mode 100644 index 00000000..ecab0cbe --- /dev/null +++ b/core/metric/summary.go @@ -0,0 +1,65 @@ +package metric + +import ( + prom "github.com/prometheus/client_golang/prometheus" + "github.com/zeromicro/go-zero/core/proc" + "github.com/zeromicro/go-zero/core/prometheus" +) + +type ( + // A SummaryVecOpts is a summary vector options + SummaryVecOpts struct { + VecOpt VectorOpts + Objectives map[float64]float64 + } + + // A SummaryVec interface represents a summary vector. + SummaryVec interface { + // Observe adds observation v to labels. + Observe(v float64, labels ...string) + close() bool + } + + promSummaryVec struct { + summary *prom.SummaryVec + } +) + +// NewSummaryVec return a SummaryVec +func NewSummaryVec(cfg *SummaryVecOpts) SummaryVec { + if cfg == nil { + return nil + } + + vec := prom.NewSummaryVec( + prom.SummaryOpts{ + Namespace: cfg.VecOpt.Namespace, + Subsystem: cfg.VecOpt.Subsystem, + Name: cfg.VecOpt.Name, + Help: cfg.VecOpt.Help, + Objectives: cfg.Objectives, + }, + cfg.VecOpt.Labels, + ) + prom.MustRegister(vec) + sv := &promSummaryVec{ + summary: vec, + } + proc.AddShutdownListener(func() { + sv.close() + }) + + return sv +} + +func (sv *promSummaryVec) Observe(v float64, labels ...string) { + if !prometheus.Enabled() { + return + } + + sv.summary.WithLabelValues(labels...).Observe(v) +} + +func (sv *promSummaryVec) close() bool { + return prom.Unregister(sv.summary) +} diff --git a/core/metric/summary_test.go b/core/metric/summary_test.go new file mode 100644 index 00000000..6a599d90 --- /dev/null +++ b/core/metric/summary_test.go @@ -0,0 +1,68 @@ +package metric + +import ( + "strings" + "testing" + + "github.com/prometheus/client_golang/prometheus/testutil" + "github.com/stretchr/testify/assert" + "github.com/zeromicro/go-zero/core/proc" +) + +func TestNewSummaryVec(t *testing.T) { + summaryVec := NewSummaryVec(&SummaryVecOpts{ + VecOpt: VectorOpts{ + Namespace: "http_server", + Subsystem: "requests", + Name: "duration_quantiles", + Help: "rpc client requests duration(ms) φ quantiles ", + Labels: []string{"method"}, + }, + Objectives: map[float64]float64{ + 0.5: 0.01, + 0.9: 0.01, + }, + }) + defer summaryVec.close() + summaryVecNil := NewSummaryVec(nil) + assert.NotNil(t, summaryVec) + assert.Nil(t, summaryVecNil) +} + +func TestSummaryObserve(t *testing.T) { + startAgent() + summaryVec := NewSummaryVec(&SummaryVecOpts{ + VecOpt: VectorOpts{ + Namespace: "http_server", + Subsystem: "requests", + Name: "duration_quantiles", + Help: "rpc client requests duration(ms) φ quantiles ", + Labels: []string{"method"}, + }, + Objectives: map[float64]float64{ + 0.3: 0.01, + 0.6: 0.01, + 1: 0.01, + }, + }) + defer summaryVec.close() + sv := summaryVec.(*promSummaryVec) + sv.Observe(100, "GET") + sv.Observe(200, "GET") + sv.Observe(300, "GET") + metadata := ` + # HELP http_server_requests_duration_quantiles rpc client requests duration(ms) φ quantiles + # TYPE http_server_requests_duration_quantiles summary +` + val := ` + http_server_requests_duration_quantiles{method="GET",quantile="0.3"} 100 + http_server_requests_duration_quantiles{method="GET",quantile="0.6"} 200 + http_server_requests_duration_quantiles{method="GET",quantile="1"} 300 + http_server_requests_duration_quantiles_sum{method="GET"} 600 + http_server_requests_duration_quantiles_count{method="GET"} 3 +` + + err := testutil.CollectAndCompare(sv.summary, strings.NewReader(metadata+val)) + assert.Nil(t, err) + proc.Shutdown() +} diff --git a/core/proc/shutdown.go b/core/proc/shutdown.go index 6e4e0bd5..ece547d5 100644 --- a/core/proc/shutdown.go +++ b/core/proc/shutdown.go @@ -96,4 +96,6 @@ func (lm *listenerManager) notifyListeners() { group.RunSafe(listener) } group.Wait() + + lm.listeners = nil }