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 d703ae8..00349c9 100644 --- a/histogram.go +++ b/histogram.go @@ -4,6 +4,7 @@ import ( "fmt" "io" "math" + "strings" "sync" "time" ) @@ -63,6 +64,8 @@ type Histogram struct { // sum is the sum of all the values put into Histogram sum float64 + + compatible bool } // Reset resets the given histogram. @@ -187,6 +190,9 @@ func (h *Histogram) VisitNonZeroBuckets(f func(vmrange string, count uint64)) { func NewHistogram(name string) *Histogram { return defaultSet.NewHistogram(name) } +func NewCompatibleHistogram(name string) *Histogram { + return defaultSet.NewCompatibleHistogram(name) +} // GetOrCreateHistogram returns registered histogram with the given name // or creates new histogram if the registry doesn't contain histogram with @@ -205,6 +211,9 @@ func NewHistogram(name string) *Histogram { func GetOrCreateHistogram(name string) *Histogram { return defaultSet.GetOrCreateHistogram(name) } +func GetOrCreateCompatibleHistogram(name string) *Histogram { + return defaultSet.GetOrCreateCompatibleHistogram(name) +} // UpdateDuration updates request duration based on the given startTime. func (h *Histogram) UpdateDuration(startTime time.Time) { @@ -237,6 +246,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) @@ -257,6 +270,41 @@ 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) + 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) + countTotal += count + fmt.Fprintf(w, "%s_bucket%s %d\n", name, labels, countTotal) + }) + 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 { + 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 868a01c..840a67a 100644 --- a/set.go +++ b/set.go @@ -85,6 +85,11 @@ func (s *Set) NewHistogram(name string) *Histogram { s.registerMetric(name, h) return h } +func (s *Set) NewCompatibleHistogram(name string) *Histogram { + h := &Histogram{compatible: true} + s.registerMetric(name, h) + return h +} // GetOrCreateHistogram returns registered histogram in s with the given name // or creates new histogram if s doesn't contain histogram with the given name. @@ -127,6 +132,34 @@ func (s *Set) GetOrCreateHistogram(name string) *Histogram { } 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] + 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 +} // NewCounter registers and returns new counter with the given name in the s. //