This repository has been archived by the owner on Jul 31, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 326
/
exporter.go
104 lines (90 loc) · 3.32 KB
/
exporter.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
package test
import (
"context"
"fmt"
"sort"
"strings"
"time"
"go.opencensus.io/metric/metricdata"
"go.opencensus.io/metric/metricexport"
"go.opencensus.io/stats/view"
)
// Exporter keeps exported metric data in memory to aid in testing the instrumentation.
//
// Metrics can be retrieved with `GetPoint()`. In order to deterministically retrieve the most recent values, you must first invoke `ReadAndExport()`.
type Exporter struct {
// points is a map from a label signature to the latest value for the time series represented by the signature.
// Use function `labelSignature` to get a signature from a `metricdata.Metric`.
points map[string]metricdata.Point
metricReader *metricexport.Reader
}
var _ metricexport.Exporter = &Exporter{}
// NewExporter returns a new exporter.
func NewExporter(metricReader *metricexport.Reader) *Exporter {
return &Exporter{points: make(map[string]metricdata.Point), metricReader: metricReader}
}
// ExportMetrics records the view data.
func (e *Exporter) ExportMetrics(ctx context.Context, data []*metricdata.Metric) error {
for _, metric := range data {
for _, ts := range metric.TimeSeries {
signature := labelSignature(metric.Descriptor.Name, labelObjectsToKeyValue(metric.Descriptor.LabelKeys, ts.LabelValues))
e.points[signature] = ts.Points[len(ts.Points)-1]
}
}
return nil
}
// GetPoint returns the latest point for the time series identified by the given labels.
func (e *Exporter) GetPoint(metricName string, labels map[string]string) (metricdata.Point, bool) {
v, ok := e.points[labelSignature(metricName, labelMapToKeyValue(labels))]
return v, ok
}
// ReadAndExport reads the current values for all metrics and makes them available to this exporter.
func (e *Exporter) ReadAndExport() {
// The next line forces the view worker to process all stats.Record* calls that
// happened within Store() before the call to ReadAndExport below. This abuses the
// worker implementation to work around lack of synchronization.
// TODO(jkohen,rghetia): figure out a clean way to make this deterministic.
view.SetReportingPeriod(time.Minute)
e.metricReader.ReadAndExport(e)
}
// String defines the “native” format for the exporter.
func (e *Exporter) String() string {
return fmt.Sprintf("points{%v}", e.points)
}
type keyValue struct {
Key string
Value string
}
func sortKeyValue(kv []keyValue) {
sort.Slice(kv, func(i, j int) bool { return kv[i].Key < kv[j].Key })
}
func labelMapToKeyValue(labels map[string]string) []keyValue {
kv := make([]keyValue, 0, len(labels))
for k, v := range labels {
kv = append(kv, keyValue{Key: k, Value: v})
}
sortKeyValue(kv)
return kv
}
func labelObjectsToKeyValue(keys []metricdata.LabelKey, values []metricdata.LabelValue) []keyValue {
if len(keys) != len(values) {
panic("keys and values must have the same length")
}
kv := make([]keyValue, 0, len(values))
for i := range keys {
if values[i].Present {
kv = append(kv, keyValue{Key: keys[i].Key, Value: values[i].Value})
}
}
sortKeyValue(kv)
return kv
}
// labelSignature returns a string that uniquely identifies the list of labels given in the input.
func labelSignature(metricName string, kv []keyValue) string {
var builder strings.Builder
for _, x := range kv {
builder.WriteString(x.Key)
builder.WriteString(x.Value)
}
return fmt.Sprintf("%s{%s}", metricName, builder.String())
}