Skip to content

Commit

Permalink
Extract logic for dimension creation and add test
Browse files Browse the repository at this point in the history
  • Loading branch information
kohrapha committed Oct 8, 2020
1 parent 2d05243 commit eecc7b0
Show file tree
Hide file tree
Showing 2 changed files with 113 additions and 35 deletions.
78 changes: 43 additions & 35 deletions exporter/awsemfexporter/metric_translator.go
Original file line number Diff line number Diff line change
Expand Up @@ -233,33 +233,26 @@ func getCWMetrics(metric *pdata.Metric, namespace string, OTLib string, dimensio

// Build CWMetric from DataPoint
func buildCWMetric(dp DataPoint, pmd *pdata.Metric, namespace string, metricSlice []map[string]string, OTLib string, dimensionRollupOption string) *CWMetrics {
// fields contains metric and dimensions key/value pairs
fieldsPairs := make(map[string]interface{})
var dimensionArray [][]string
// Dimensions Slice
var dimensionSlice []string
dimensionKV := dp.LabelsMap()

dimensionKV.ForEach(func(k string, v pdata.StringValue) {
fieldsPairs[k] = v.Value()
dimensionSlice = append(dimensionSlice, k)
})
// add OTLib as an additional dimension
fieldsPairs[OtlibDimensionKey] = OTLib
dimensionArray = append(dimensionArray, append(dimensionSlice, OtlibDimensionKey))

dimensions, fields := createDimensions(dp, OTLib, dimensionRollupOption)
cwMeasurement := &CwMeasurement{
Namespace: namespace,
Dimensions: dimensions,
Metrics: metricSlice,
}
metricList := []CwMeasurement{*cwMeasurement}
timestamp := time.Now().UnixNano() / int64(time.Millisecond)

// Extract metric
var metricVal interface{}
switch metric := dp.(type) {
case pdata.IntDataPoint:
// Put a fake but identical metric value here in order to add metric name into fieldsPairs
// Put a fake but identical metric value here in order to add metric name into fields
// since calculateRate() needs metric name as one of metric identifiers
fieldsPairs[pmd.Name()] = int64(FakeMetricValue)
metricVal = calculateRate(fieldsPairs, metric.Value(), timestamp)
fields[pmd.Name()] = int64(FakeMetricValue)
metricVal = calculateRate(fields, metric.Value(), timestamp)
case pdata.DoubleDataPoint:
fieldsPairs[pmd.Name()] = float64(FakeMetricValue)
metricVal = calculateRate(fieldsPairs, metric.Value(), timestamp)
fields[pmd.Name()] = float64(FakeMetricValue)
metricVal = calculateRate(fields, metric.Value(), timestamp)
case pdata.DoubleHistogramDataPoint:
bucketBounds := metric.ExplicitBounds()
metricVal = &CWMetricStats{
Expand All @@ -272,29 +265,44 @@ func buildCWMetric(dp DataPoint, pmd *pdata.Metric, namespace string, metricSlic
if metricVal == nil {
return nil
}
fieldsPairs[pmd.Name()] = metricVal

// EMF dimension attr takes list of list on dimensions. Including single/zero dimension rollup
rollupDimensionArray := dimensionRollup(dimensionRollupOption, dimensionSlice)
if len(rollupDimensionArray) > 0 {
dimensionArray = append(dimensionArray, rollupDimensionArray...)
}
fields[pmd.Name()] = metricVal

cwMeasurement := &CwMeasurement{
Namespace: namespace,
Dimensions: dimensionArray,
Metrics: metricSlice,
}
metricList := make([]CwMeasurement, 1)
metricList[0] = *cwMeasurement
cwMetric := &CWMetrics{
Measurements: metricList,
Timestamp: timestamp,
Fields: fieldsPairs,
Fields: fields,
}
return cwMetric
}

// Create dimensions from DataPoint labels, where dimensions is a 2D array of dimension names,
// and initialize fields with dimension key/value pairs
func createDimensions(dp DataPoint, OTLib string, dimensionRollupOption string) (dimensions [][]string, fields map[string]interface{}) {
// fields contains metric and dimensions key/value pairs
fields = make(map[string]interface{})
dimensionKV := dp.LabelsMap()

dimensionSlice := make([]string, dimensionKV.Len(), dimensionKV.Len()+1)
idx := 0
dimensionKV.ForEach(func(k string, v pdata.StringValue) {
fields[k] = v.Value()
dimensionSlice[idx] = k
idx++
})

// Add OTLib as an additional dimension
fields[OtlibDimensionKey] = OTLib
dimensions = append(dimensions, append(dimensionSlice, OtlibDimensionKey))

// EMF dimension attr takes list of list on dimensions. Including single/zero dimension rollup
rollupDimensionArray := dimensionRollup(dimensionRollupOption, dimensionSlice)
if len(rollupDimensionArray) > 0 {
dimensions = append(dimensions, rollupDimensionArray...)
}

return
}

// rate is calculated by valDelta / timeDelta
func calculateRate(fields map[string]interface{}, val interface{}, timestamp int64) interface{} {
keys := make([]string, 0, len(fields))
Expand Down
70 changes: 70 additions & 0 deletions exporter/awsemfexporter/metric_translator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ package awsemfexporter
import (
"io/ioutil"
"sort"
"strings"
"testing"
"time"

Expand All @@ -27,6 +28,7 @@ import (
"github.com/golang/protobuf/ptypes/wrappers"
"github.com/stretchr/testify/assert"
"go.opentelemetry.io/collector/consumer/consumerdata"
"go.opentelemetry.io/collector/consumer/pdata"
"go.opentelemetry.io/collector/translator/conventions"
"go.opentelemetry.io/collector/translator/internaldata"
)
Expand Down Expand Up @@ -424,6 +426,74 @@ func TestGetCWMetrics(t *testing.T) {

}

func TestCreateDimensions(t *testing.T) {
OTLib := "OTLib"
testCases := []struct {
testName string
labels map[string]string
dims [][]string
}{
{
"single label",
map[string]string{"a": "foo"},
[][]string{
{"a", OTLib},
{OTLib},
{OTLib, "a"},
},
},
{
"multiple labels",
map[string]string{"a": "foo", "b": "bar"},
[][]string{
{"a", "b", OTLib},
{OTLib},
{OTLib, "a"},
{OTLib, "b"},
},
},
{
"no labels",
map[string]string{},
[][]string{
{OTLib},
},
},
}

sliceSorter := func(slice [][]string) func(a, b int) bool {
stringified := make([]string, len(slice))
for i, v := range slice {
stringified[i] = strings.Join(v, ",")
}
return func(i, j int) bool {
return stringified[i] > stringified[j]
}
}

for _, tc := range testCases {
dp := pdata.NewIntDataPoint()
dp.InitEmpty()
dp.LabelsMap().InitFromMap(tc.labels)
dimensions, fields := createDimensions(dp, OTLib, ZeroAndSingleDimensionRollup)

// Sort slice for equality check
sort.Slice(tc.dims, sliceSorter(tc.dims))
sort.Slice(dimensions, sliceSorter(dimensions))

assert.Equal(t, tc.dims, dimensions)

expectedFields := make(map[string]interface{})
for k, v := range tc.labels {
expectedFields[k] = v
}
expectedFields[OtlibDimensionKey] = OTLib

assert.Equal(t, expectedFields, fields)
}

}

func TestCalculateRate(t *testing.T) {
prevValue := int64(0)
curValue := int64(10)
Expand Down

0 comments on commit eecc7b0

Please sign in to comment.