From 60fb01a811e6cda47666abcb99c931b06720e17f Mon Sep 17 00:00:00 2001 From: greyireland Date: Tue, 7 Feb 2023 14:27:40 +0800 Subject: [PATCH 1/5] feature:compatible --- .gitignore | 1 + histogram.go | 37 ++++++++++++++++++++++++++++++++++++- set.go | 4 ++-- set_test.go | 4 ++-- 4 files changed, 41 insertions(+), 5 deletions(-) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..62c8935 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.idea/ \ No newline at end of file diff --git a/histogram.go b/histogram.go index a576681..5c7a80b 100644 --- a/histogram.go +++ b/histogram.go @@ -4,6 +4,7 @@ import ( "fmt" "io" "math" + "strings" "sync" "time" ) @@ -55,6 +56,8 @@ type Histogram struct { upper uint64 sum float64 + + compatible bool } // Reset resets the given histogram. @@ -149,7 +152,10 @@ func (h *Histogram) VisitNonZeroBuckets(f func(vmrange string, count uint64)) { // // The returned histogram is safe to use from concurrent goroutines. func NewHistogram(name string) *Histogram { - return defaultSet.NewHistogram(name) + return defaultSet.NewHistogram(name, true) +} +func NewHistogramByVM(name string) *Histogram { + return defaultSet.NewHistogram(name, false) } // GetOrCreateHistogram returns registered histogram with the given name @@ -201,6 +207,10 @@ var ( ) func (h *Histogram) marshalTo(prefix string, w io.Writer) { + if h.compatible { + h.marshalToPrometheus(prefix, w) + return + } countTotal := uint64(0) h.VisitNonZeroBuckets(func(vmrange string, count uint64) { tag := fmt.Sprintf("vmrange=%q", vmrange) @@ -221,6 +231,31 @@ func (h *Histogram) marshalTo(prefix string, w io.Writer) { } fmt.Fprintf(w, "%s_count%s %d\n", name, labels, countTotal) } +func (h *Histogram) marshalToPrometheus(prefix string, w io.Writer) { + countTotal := uint64(0) + h.VisitNonZeroBuckets(func(vmrange string, count uint64) { + v := strings.Split(vmrange, "...") + if len(v) != 2 { + return + } + tag := fmt.Sprintf("le=%q", v[1]) + metricName := addTag(prefix, tag) + name, labels := splitMetricName(metricName) + countTotal += count + fmt.Fprintf(w, "%s_bucket%s %d\n", name, labels, countTotal) + }) + if countTotal == 0 { + return + } + name, labels := splitMetricName(prefix) + sum := h.getSum() + if float64(int64(sum)) == sum { + fmt.Fprintf(w, "%s_sum%s %d\n", name, labels, int64(sum)) + } else { + fmt.Fprintf(w, "%s_sum%s %g\n", name, labels, sum) + } + fmt.Fprintf(w, "%s_count%s %d\n", name, labels, countTotal) +} func (h *Histogram) getSum() float64 { h.mu.Lock() diff --git a/set.go b/set.go index 79355ea..43667be 100644 --- a/set.go +++ b/set.go @@ -65,8 +65,8 @@ func (s *Set) WritePrometheus(w io.Writer) { // - foo{bar="baz",aaa="b"} // // The returned histogram is safe to use from concurrent goroutines. -func (s *Set) NewHistogram(name string) *Histogram { - h := &Histogram{} +func (s *Set) NewHistogram(name string, compatible bool) *Histogram { + h := &Histogram{compatible: compatible} s.registerMetric(name, h) return h } diff --git a/set_test.go b/set_test.go index 66ed07a..4ddba29 100644 --- a/set_test.go +++ b/set_test.go @@ -29,7 +29,7 @@ func TestNewSet(t *testing.T) { if sm == nil { t.Fatalf("NewSummary returned nil") } - h := s.NewHistogram(fmt.Sprintf("histogram_%d", j)) + h := s.NewHistogram(fmt.Sprintf("histogram_%d", j), false) if h == nil { t.Fatalf("NewHistogram returned nil") } @@ -71,7 +71,7 @@ func TestSetUnregisterAllMetrics(t *testing.T) { for i := 0; i < 10; i++ { _ = s.NewCounter(fmt.Sprintf("counter_%d", i)) _ = s.NewSummary(fmt.Sprintf("summary_%d", i)) - _ = s.NewHistogram(fmt.Sprintf("histogram_%d", i)) + _ = s.NewHistogram(fmt.Sprintf("histogram_%d", i), false) _ = s.NewGauge(fmt.Sprintf("gauge_%d", i), func() float64 { return 0 }) expectedMetricsCount += 4 } From 84661043035f3af3b1e14b92489c5e162734dca0 Mon Sep 17 00:00:00 2001 From: greyireland Date: Tue, 7 Feb 2023 15:00:56 +0800 Subject: [PATCH 2/5] add inf --- histogram.go | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/histogram.go b/histogram.go index 5c7a80b..c98eb39 100644 --- a/histogram.go +++ b/histogram.go @@ -233,11 +233,15 @@ func (h *Histogram) marshalTo(prefix string, w io.Writer) { } func (h *Histogram) marshalToPrometheus(prefix string, w io.Writer) { countTotal := uint64(0) + inf := false h.VisitNonZeroBuckets(func(vmrange string, count uint64) { v := strings.Split(vmrange, "...") if len(v) != 2 { return } + if v[1] == "+Inf" { + inf = true + } tag := fmt.Sprintf("le=%q", v[1]) metricName := addTag(prefix, tag) name, labels := splitMetricName(metricName) @@ -247,6 +251,12 @@ func (h *Histogram) marshalToPrometheus(prefix string, w io.Writer) { if countTotal == 0 { return } + if !inf { + tag := fmt.Sprintf("le=%q", "+Inf") + metricName := addTag(prefix, tag) + name, labels := splitMetricName(metricName) + fmt.Fprintf(w, "%s_bucket%s %d\n", name, labels, countTotal) + } name, labels := splitMetricName(prefix) sum := h.getSum() if float64(int64(sum)) == sum { From 8969e845c9f424a55debea115cdaddf066a5eb74 Mon Sep 17 00:00:00 2001 From: greyireland Date: Tue, 7 Feb 2023 15:15:54 +0800 Subject: [PATCH 3/5] update nameMetric --- histogram.go | 5 ++++- push.go | 4 ++-- set.go | 4 ++-- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/histogram.go b/histogram.go index c98eb39..e86c0c8 100644 --- a/histogram.go +++ b/histogram.go @@ -173,7 +173,10 @@ func NewHistogramByVM(name string) *Histogram { // // Performance tip: prefer NewHistogram instead of GetOrCreateHistogram. func GetOrCreateHistogram(name string) *Histogram { - return defaultSet.GetOrCreateHistogram(name) + return defaultSet.GetOrCreateHistogram(name, true) +} +func GetOrCreateHistogramByVM(name string) *Histogram { + return defaultSet.GetOrCreateHistogram(name, false) } // UpdateDuration updates request duration based on the given startTime. diff --git a/push.go b/push.go index 4215f48..f878408 100644 --- a/push.go +++ b/push.go @@ -115,8 +115,8 @@ func InitPushExt(pushURL string, interval time.Duration, extraLabels string, wri pushesTotal := pushMetrics.GetOrCreateCounter(fmt.Sprintf(`metrics_push_total{url=%q}`, pushURLRedacted)) pushErrorsTotal := pushMetrics.GetOrCreateCounter(fmt.Sprintf(`metrics_push_errors_total{url=%q}`, pushURLRedacted)) bytesPushedTotal := pushMetrics.GetOrCreateCounter(fmt.Sprintf(`metrics_push_bytes_pushed_total{url=%q}`, pushURLRedacted)) - pushDuration := pushMetrics.GetOrCreateHistogram(fmt.Sprintf(`metrics_push_duration_seconds{url=%q}`, pushURLRedacted)) - pushBlockSize := pushMetrics.GetOrCreateHistogram(fmt.Sprintf(`metrics_push_block_size_bytes{url=%q}`, pushURLRedacted)) + pushDuration := pushMetrics.GetOrCreateHistogram(fmt.Sprintf(`metrics_push_duration_seconds{url=%q}`, pushURLRedacted), false) + pushBlockSize := pushMetrics.GetOrCreateHistogram(fmt.Sprintf(`metrics_push_block_size_bytes{url=%q}`, pushURLRedacted), false) pushMetrics.GetOrCreateFloatCounter(fmt.Sprintf(`metrics_push_interval_seconds{url=%q}`, pushURLRedacted)).Set(interval.Seconds()) go func() { ticker := time.NewTicker(interval) diff --git a/set.go b/set.go index 43667be..6ecbfe2 100644 --- a/set.go +++ b/set.go @@ -84,7 +84,7 @@ func (s *Set) NewHistogram(name string, compatible bool) *Histogram { // The returned histogram is safe to use from concurrent goroutines. // // Performance tip: prefer NewHistogram instead of GetOrCreateHistogram. -func (s *Set) GetOrCreateHistogram(name string) *Histogram { +func (s *Set) GetOrCreateHistogram(name string, compatible bool) *Histogram { s.mu.Lock() nm := s.m[name] s.mu.Unlock() @@ -95,7 +95,7 @@ func (s *Set) GetOrCreateHistogram(name string) *Histogram { } nmNew := &namedMetric{ name: name, - metric: &Histogram{}, + metric: &Histogram{compatible: compatible}, } s.mu.Lock() nm = s.m[name] From 4a30cca1843403912f33c3f316351fffabb8ef58 Mon Sep 17 00:00:00 2001 From: greyireland Date: Tue, 7 Feb 2023 15:38:08 +0800 Subject: [PATCH 4/5] rename compatible --- histogram.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/histogram.go b/histogram.go index e86c0c8..bd023d8 100644 --- a/histogram.go +++ b/histogram.go @@ -152,11 +152,11 @@ func (h *Histogram) VisitNonZeroBuckets(f func(vmrange string, count uint64)) { // // The returned histogram is safe to use from concurrent goroutines. func NewHistogram(name string) *Histogram { - return defaultSet.NewHistogram(name, true) -} -func NewHistogramByVM(name string) *Histogram { return defaultSet.NewHistogram(name, false) } +func NewCompatibleHistogram(name string) *Histogram { + return defaultSet.NewHistogram(name, true) +} // GetOrCreateHistogram returns registered histogram with the given name // or creates new histogram if the registry doesn't contain histogram with @@ -173,11 +173,11 @@ func NewHistogramByVM(name string) *Histogram { // // Performance tip: prefer NewHistogram instead of GetOrCreateHistogram. func GetOrCreateHistogram(name string) *Histogram { - return defaultSet.GetOrCreateHistogram(name, true) -} -func GetOrCreateHistogramByVM(name string) *Histogram { return defaultSet.GetOrCreateHistogram(name, false) } +func GetOrCreateCompatibleHistogram(name string) *Histogram { + return defaultSet.GetOrCreateHistogram(name, true) +} // UpdateDuration updates request duration based on the given startTime. func (h *Histogram) UpdateDuration(startTime time.Time) { From 880d8e1cc60b0a3b564b1c41cc3d9b829723e759 Mon Sep 17 00:00:00 2001 From: greyireland Date: Mon, 20 Feb 2023 12:54:52 +0800 Subject: [PATCH 5/5] backward compatibility --- histogram.go | 8 ++++---- push.go | 4 ++-- set.go | 41 +++++++++++++++++++++++++++++++++++++---- set_test.go | 4 ++-- 4 files changed, 45 insertions(+), 12 deletions(-) diff --git a/histogram.go b/histogram.go index bd023d8..47af50c 100644 --- a/histogram.go +++ b/histogram.go @@ -152,10 +152,10 @@ func (h *Histogram) VisitNonZeroBuckets(f func(vmrange string, count uint64)) { // // The returned histogram is safe to use from concurrent goroutines. func NewHistogram(name string) *Histogram { - return defaultSet.NewHistogram(name, false) + return defaultSet.NewHistogram(name) } func NewCompatibleHistogram(name string) *Histogram { - return defaultSet.NewHistogram(name, true) + return defaultSet.NewCompatibleHistogram(name) } // GetOrCreateHistogram returns registered histogram with the given name @@ -173,10 +173,10 @@ func NewCompatibleHistogram(name string) *Histogram { // // Performance tip: prefer NewHistogram instead of GetOrCreateHistogram. func GetOrCreateHistogram(name string) *Histogram { - return defaultSet.GetOrCreateHistogram(name, false) + return defaultSet.GetOrCreateHistogram(name) } func GetOrCreateCompatibleHistogram(name string) *Histogram { - return defaultSet.GetOrCreateHistogram(name, true) + return defaultSet.GetOrCreateCompatibleHistogram(name) } // UpdateDuration updates request duration based on the given startTime. diff --git a/push.go b/push.go index f878408..4215f48 100644 --- a/push.go +++ b/push.go @@ -115,8 +115,8 @@ func InitPushExt(pushURL string, interval time.Duration, extraLabels string, wri pushesTotal := pushMetrics.GetOrCreateCounter(fmt.Sprintf(`metrics_push_total{url=%q}`, pushURLRedacted)) pushErrorsTotal := pushMetrics.GetOrCreateCounter(fmt.Sprintf(`metrics_push_errors_total{url=%q}`, pushURLRedacted)) bytesPushedTotal := pushMetrics.GetOrCreateCounter(fmt.Sprintf(`metrics_push_bytes_pushed_total{url=%q}`, pushURLRedacted)) - pushDuration := pushMetrics.GetOrCreateHistogram(fmt.Sprintf(`metrics_push_duration_seconds{url=%q}`, pushURLRedacted), false) - pushBlockSize := pushMetrics.GetOrCreateHistogram(fmt.Sprintf(`metrics_push_block_size_bytes{url=%q}`, pushURLRedacted), false) + pushDuration := pushMetrics.GetOrCreateHistogram(fmt.Sprintf(`metrics_push_duration_seconds{url=%q}`, pushURLRedacted)) + pushBlockSize := pushMetrics.GetOrCreateHistogram(fmt.Sprintf(`metrics_push_block_size_bytes{url=%q}`, pushURLRedacted)) pushMetrics.GetOrCreateFloatCounter(fmt.Sprintf(`metrics_push_interval_seconds{url=%q}`, pushURLRedacted)).Set(interval.Seconds()) go func() { ticker := time.NewTicker(interval) diff --git a/set.go b/set.go index 6ecbfe2..d7bc371 100644 --- a/set.go +++ b/set.go @@ -65,8 +65,13 @@ func (s *Set) WritePrometheus(w io.Writer) { // - foo{bar="baz",aaa="b"} // // The returned histogram is safe to use from concurrent goroutines. -func (s *Set) NewHistogram(name string, compatible bool) *Histogram { - h := &Histogram{compatible: compatible} +func (s *Set) NewHistogram(name string) *Histogram { + h := &Histogram{} + s.registerMetric(name, h) + return h +} +func (s *Set) NewCompatibleHistogram(name string) *Histogram { + h := &Histogram{compatible: true} s.registerMetric(name, h) return h } @@ -84,7 +89,7 @@ func (s *Set) NewHistogram(name string, compatible bool) *Histogram { // The returned histogram is safe to use from concurrent goroutines. // // Performance tip: prefer NewHistogram instead of GetOrCreateHistogram. -func (s *Set) GetOrCreateHistogram(name string, compatible bool) *Histogram { +func (s *Set) GetOrCreateHistogram(name string) *Histogram { s.mu.Lock() nm := s.m[name] s.mu.Unlock() @@ -95,7 +100,35 @@ func (s *Set) GetOrCreateHistogram(name string, compatible bool) *Histogram { } nmNew := &namedMetric{ name: name, - metric: &Histogram{compatible: compatible}, + metric: &Histogram{}, + } + s.mu.Lock() + nm = s.m[name] + if nm == nil { + nm = nmNew + s.m[name] = nm + s.a = append(s.a, nm) + } + s.mu.Unlock() + } + h, ok := nm.metric.(*Histogram) + if !ok { + panic(fmt.Errorf("BUG: metric %q isn't a Histogram. It is %T", name, nm.metric)) + } + return h +} +func (s *Set) GetOrCreateCompatibleHistogram(name string) *Histogram { + s.mu.Lock() + nm := s.m[name] + s.mu.Unlock() + if nm == nil { + // Slow path - create and register missing histogram. + if err := validateMetric(name); err != nil { + panic(fmt.Errorf("BUG: invalid metric name %q: %s", name, err)) + } + nmNew := &namedMetric{ + name: name, + metric: &Histogram{compatible: true}, } s.mu.Lock() nm = s.m[name] diff --git a/set_test.go b/set_test.go index 4ddba29..66ed07a 100644 --- a/set_test.go +++ b/set_test.go @@ -29,7 +29,7 @@ func TestNewSet(t *testing.T) { if sm == nil { t.Fatalf("NewSummary returned nil") } - h := s.NewHistogram(fmt.Sprintf("histogram_%d", j), false) + h := s.NewHistogram(fmt.Sprintf("histogram_%d", j)) if h == nil { t.Fatalf("NewHistogram returned nil") } @@ -71,7 +71,7 @@ func TestSetUnregisterAllMetrics(t *testing.T) { for i := 0; i < 10; i++ { _ = s.NewCounter(fmt.Sprintf("counter_%d", i)) _ = s.NewSummary(fmt.Sprintf("summary_%d", i)) - _ = s.NewHistogram(fmt.Sprintf("histogram_%d", i), false) + _ = s.NewHistogram(fmt.Sprintf("histogram_%d", i)) _ = s.NewGauge(fmt.Sprintf("gauge_%d", i), func() float64 { return 0 }) expectedMetricsCount += 4 }