diff options
author | hcpp <hcpp@ydb.tech> | 2023-11-08 12:09:41 +0300 |
---|---|---|
committer | hcpp <hcpp@ydb.tech> | 2023-11-08 12:56:14 +0300 |
commit | a361f5b98b98b44ea510d274f6769164640dd5e1 (patch) | |
tree | c47c80962c6e2e7b06798238752fd3da0191a3f6 /library/go/core/metrics/solomon/histogram.go | |
parent | 9478806fde1f4d40bd5a45e7cbe77237dab613e9 (diff) | |
download | ydb-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.go | 182 |
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)]) + } +} |