aboutsummaryrefslogtreecommitdiffstats
path: root/library/go/core/metrics/solomon/histogram.go
diff options
context:
space:
mode:
authorhcpp <hcpp@ydb.tech>2023-11-08 12:09:41 +0300
committerhcpp <hcpp@ydb.tech>2023-11-08 12:56:14 +0300
commita361f5b98b98b44ea510d274f6769164640dd5e1 (patch)
treec47c80962c6e2e7b06798238752fd3da0191a3f6 /library/go/core/metrics/solomon/histogram.go
parent9478806fde1f4d40bd5a45e7cbe77237dab613e9 (diff)
downloadydb-a361f5b98b98b44ea510d274f6769164640dd5e1.tar.gz
metrics have been added
Diffstat (limited to 'library/go/core/metrics/solomon/histogram.go')
-rw-r--r--library/go/core/metrics/solomon/histogram.go182
1 files changed, 182 insertions, 0 deletions
diff --git a/library/go/core/metrics/solomon/histogram.go b/library/go/core/metrics/solomon/histogram.go
new file mode 100644
index 0000000000..6f4d3629e0
--- /dev/null
+++ b/library/go/core/metrics/solomon/histogram.go
@@ -0,0 +1,182 @@
+package solomon
+
+import (
+ "encoding/binary"
+ "encoding/json"
+ "io"
+ "sort"
+ "sync"
+ "time"
+
+ "github.com/ydb-platform/ydb/library/go/core/metrics"
+ "github.com/ydb-platform/ydb/library/go/core/xerrors"
+ "go.uber.org/atomic"
+)
+
+var (
+ _ metrics.Histogram = (*Histogram)(nil)
+ _ metrics.Timer = (*Histogram)(nil)
+ _ Metric = (*Histogram)(nil)
+)
+
+type Histogram struct {
+ name string
+ metricType metricType
+ tags map[string]string
+ bucketBounds []float64
+ bucketValues []int64
+ infValue atomic.Int64
+ mutex sync.Mutex
+ timestamp *time.Time
+ useNameTag bool
+}
+
+type histogram struct {
+ Bounds []float64 `json:"bounds"`
+ Buckets []int64 `json:"buckets"`
+ Inf int64 `json:"inf,omitempty"`
+}
+
+func (h *histogram) writeHistogram(w io.Writer) error {
+ err := writeULEB128(w, uint32(len(h.Buckets)))
+ if err != nil {
+ return xerrors.Errorf("writeULEB128 size histogram buckets failed: %w", err)
+ }
+
+ for _, upperBound := range h.Bounds {
+ err = binary.Write(w, binary.LittleEndian, float64(upperBound))
+ if err != nil {
+ return xerrors.Errorf("binary.Write upper bound failed: %w", err)
+ }
+ }
+
+ for _, bucketValue := range h.Buckets {
+ err = binary.Write(w, binary.LittleEndian, uint64(bucketValue))
+ if err != nil {
+ return xerrors.Errorf("binary.Write histogram buckets failed: %w", err)
+ }
+ }
+ return nil
+}
+
+func (h *Histogram) RecordValue(value float64) {
+ boundIndex := sort.SearchFloat64s(h.bucketBounds, value)
+
+ if boundIndex < len(h.bucketValues) {
+ h.mutex.Lock()
+ h.bucketValues[boundIndex] += 1
+ h.mutex.Unlock()
+ } else {
+ h.infValue.Inc()
+ }
+}
+
+func (h *Histogram) RecordDuration(value time.Duration) {
+ h.RecordValue(value.Seconds())
+}
+
+func (h *Histogram) Reset() {
+ h.mutex.Lock()
+ defer h.mutex.Unlock()
+
+ h.bucketValues = make([]int64, len(h.bucketValues))
+ h.infValue.Store(0)
+}
+
+func (h *Histogram) Name() string {
+ return h.name
+}
+
+func (h *Histogram) getType() metricType {
+ return h.metricType
+}
+
+func (h *Histogram) getLabels() map[string]string {
+ return h.tags
+}
+
+func (h *Histogram) getValue() interface{} {
+ return histogram{
+ Bounds: h.bucketBounds,
+ Buckets: h.bucketValues,
+ }
+}
+
+func (h *Histogram) getTimestamp() *time.Time {
+ return h.timestamp
+}
+
+func (h *Histogram) getNameTag() string {
+ if h.useNameTag {
+ return "name"
+ } else {
+ return "sensor"
+ }
+}
+
+// MarshalJSON implements json.Marshaler.
+func (h *Histogram) MarshalJSON() ([]byte, error) {
+ valuesCopy := make([]int64, len(h.bucketValues))
+ h.mutex.Lock()
+ copy(valuesCopy, h.bucketValues)
+ h.mutex.Unlock()
+ return json.Marshal(struct {
+ Type string `json:"type"`
+ Labels map[string]string `json:"labels"`
+ Histogram histogram `json:"hist"`
+ Timestamp *int64 `json:"ts,omitempty"`
+ }{
+ Type: h.metricType.String(),
+ Histogram: histogram{
+ Bounds: h.bucketBounds,
+ Buckets: valuesCopy,
+ Inf: h.infValue.Load(),
+ },
+ Labels: func() map[string]string {
+ labels := make(map[string]string, len(h.tags)+1)
+ labels[h.getNameTag()] = h.Name()
+ for k, v := range h.tags {
+ labels[k] = v
+ }
+ return labels
+ }(),
+ Timestamp: tsAsRef(h.timestamp),
+ })
+}
+
+// Snapshot returns independent copy on metric.
+func (h *Histogram) Snapshot() Metric {
+ bucketBounds := make([]float64, len(h.bucketBounds))
+ bucketValues := make([]int64, len(h.bucketValues))
+
+ copy(bucketBounds, h.bucketBounds)
+ h.mutex.Lock()
+ copy(bucketValues, h.bucketValues)
+ h.mutex.Unlock()
+
+ return &Histogram{
+ name: h.name,
+ metricType: h.metricType,
+ tags: h.tags,
+ bucketBounds: bucketBounds,
+ bucketValues: bucketValues,
+ infValue: *atomic.NewInt64(h.infValue.Load()),
+ useNameTag: h.useNameTag,
+ }
+}
+
+// InitBucketValues cleans internal bucketValues and saves new values in order.
+// Length of internal bucketValues stays unchanged.
+// If length of slice in argument bucketValues more than length of internal one,
+// the first extra element of bucketValues is stored in infValue.
+func (h *Histogram) InitBucketValues(bucketValues []int64) {
+ h.mutex.Lock()
+ defer h.mutex.Unlock()
+
+ h.bucketValues = make([]int64, len(h.bucketValues))
+ h.infValue.Store(0)
+ copy(h.bucketValues, bucketValues)
+ if len(bucketValues) > len(h.bucketValues) {
+ h.infValue.Store(bucketValues[len(h.bucketValues)])
+ }
+}