...

Source file src/go.opencensus.io/metric/test/exporter.go

Documentation: go.opencensus.io/metric/test

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

View as plain text