diff --git a/.chloggen/loadbalancer_exporter_refactor.yaml b/.chloggen/loadbalancer_exporter_refactor.yaml new file mode 100644 index 000000000000..cdada4c87d90 --- /dev/null +++ b/.chloggen/loadbalancer_exporter_refactor.yaml @@ -0,0 +1,27 @@ +# Use this changelog template to create an entry for release notes. + +# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix' +change_type: enhancement + +# The name of the component, or a single word describing the area of concern, (e.g. filelogreceiver) +component: loadbalancerexporter + +# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`). +note: Refactors how the load balancing exporter splits metrics + +# Mandatory: One or more tracking issues related to the change. You can use the PR number here if no issue exists. +issues: [32513] + +# (Optional) One or more lines of additional information to render under the primary note. +# These lines will be padded with 2 spaces and then inserted directly into the document. +# Use pipe (|) for multiline entries. +subtext: All splitting is *behaviorally*, the same. However, the `resource` routingID now uses the `internal/exp/metrics/identity` package to generate the load balancing key, instead of bespoke code. This means that when upgrading to this version your routes for specific metric groupings could change. However, this will be stable and all future metrics will follow the new routing + +# If your change doesn't affect end users or the exported elements of any package, +# you should instead start your pull request title with [chore] or use the "Skip Changelog" label. +# Optional: The change log or logs in which this entry should be included. +# e.g. '[user]' or '[user, api]' +# Include 'user' if the change is relevant to end users. +# Include 'api' if there is a change to a library API. +# Default: '[user]' +change_logs: [user] diff --git a/exporter/loadbalancingexporter/config.go b/exporter/loadbalancingexporter/config.go index d3109bc9056a..8f5d7e7b6d91 100644 --- a/exporter/loadbalancingexporter/config.go +++ b/exporter/loadbalancingexporter/config.go @@ -19,6 +19,13 @@ const ( resourceRouting ) +const ( + svcRoutingStr = "service" + traceIDRoutingStr = "traceID" + metricNameRoutingStr = "metric" + resourceRoutingStr = "resource" +) + // Config defines configuration for the exporter. type Config struct { Protocol Protocol `mapstructure:"protocol"` diff --git a/exporter/loadbalancingexporter/go.mod b/exporter/loadbalancingexporter/go.mod index f94544c87e5d..7ffad882a2f5 100644 --- a/exporter/loadbalancingexporter/go.mod +++ b/exporter/loadbalancingexporter/go.mod @@ -6,7 +6,11 @@ require ( github.com/aws/aws-sdk-go-v2/config v1.27.16 github.com/aws/aws-sdk-go-v2/service/servicediscovery v1.29.10 github.com/aws/smithy-go v1.20.2 + github.com/json-iterator/go v1.1.12 + github.com/open-telemetry/opentelemetry-collector-contrib/internal/exp/metrics v0.103.0 github.com/open-telemetry/opentelemetry-collector-contrib/pkg/batchpersignal v0.103.0 + github.com/open-telemetry/opentelemetry-collector-contrib/pkg/golden v0.103.0 + github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatatest v0.103.0 github.com/stretchr/testify v1.9.0 go.opencensus.io v0.24.0 go.opentelemetry.io/collector/component v0.103.0 @@ -22,6 +26,7 @@ require ( go.uber.org/goleak v1.3.0 go.uber.org/multierr v1.11.0 go.uber.org/zap v1.27.0 + gopkg.in/yaml.v2 v2.4.0 k8s.io/api v0.29.3 k8s.io/apimachinery v0.29.3 k8s.io/client-go v0.29.3 @@ -69,7 +74,6 @@ require ( github.com/imdario/mergo v0.3.6 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/josharian/intern v1.0.0 // indirect - github.com/json-iterator/go v1.1.12 // indirect github.com/klauspost/compress v1.17.8 // indirect github.com/knadh/koanf/maps v0.1.1 // indirect github.com/knadh/koanf/providers/confmap v0.1.0 // indirect @@ -82,6 +86,7 @@ require ( github.com/modern-go/reflect2 v1.0.2 // indirect github.com/mostynb/go-grpc-compression v1.2.3 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect + github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatautil v0.103.0 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect @@ -149,7 +154,6 @@ require ( google.golang.org/grpc v1.64.0 // indirect google.golang.org/protobuf v1.34.2 // indirect gopkg.in/inf.v0 v0.9.1 // indirect - gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect k8s.io/klog/v2 v2.110.1 // indirect k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00 // indirect @@ -168,3 +172,11 @@ retract ( // ambiguous import: found package cloud.google.com/go/compute/metadata in multiple modules replace cloud.google.com/go v0.65.0 => cloud.google.com/go v0.110.10 + +replace github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatautil => ../../pkg/pdatautil + +replace github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatatest => ../../pkg/pdatatest + +replace github.com/open-telemetry/opentelemetry-collector-contrib/pkg/golden => ../../pkg/golden + +replace github.com/open-telemetry/opentelemetry-collector-contrib/internal/exp/metrics => ../../internal/exp/metrics diff --git a/exporter/loadbalancingexporter/helpers.go b/exporter/loadbalancingexporter/helpers.go index 13322efb98f2..56a5f81d9739 100644 --- a/exporter/loadbalancingexporter/helpers.go +++ b/exporter/loadbalancingexporter/helpers.go @@ -4,7 +4,6 @@ package loadbalancingexporter // import "github.com/open-telemetry/opentelemetry-collector-contrib/exporter/loadbalancingexporter" import ( - "go.opentelemetry.io/collector/pdata/pmetric" "go.opentelemetry.io/collector/pdata/ptrace" ) @@ -13,9 +12,3 @@ func mergeTraces(t1 ptrace.Traces, t2 ptrace.Traces) ptrace.Traces { t2.ResourceSpans().MoveAndAppendTo(t1.ResourceSpans()) return t1 } - -// mergeMetrics concatenates two pmetric.Metrics into a single pmetric.Metrics. -func mergeMetrics(m1 pmetric.Metrics, m2 pmetric.Metrics) pmetric.Metrics { - m2.ResourceMetrics().MoveAndAppendTo(m1.ResourceMetrics()) - return m1 -} diff --git a/exporter/loadbalancingexporter/helpers_test.go b/exporter/loadbalancingexporter/helpers_test.go index e8bda317d54b..bdad0a02d6db 100644 --- a/exporter/loadbalancingexporter/helpers_test.go +++ b/exporter/loadbalancingexporter/helpers_test.go @@ -7,7 +7,6 @@ import ( "testing" "github.com/stretchr/testify/require" - "go.opentelemetry.io/collector/pdata/pmetric" "go.opentelemetry.io/collector/pdata/ptrace" conventions "go.opentelemetry.io/collector/semconv/v1.6.1" ) @@ -66,60 +65,6 @@ func TestMergeTraces(t *testing.T) { require.Equal(t, expectedTraces, mergedTraces) } -func TestMergeMetricsTwoEmpty(t *testing.T) { - expectedEmpty := pmetric.NewMetrics() - metric1 := pmetric.NewMetrics() - metric2 := pmetric.NewMetrics() - - mergedMetrics := mergeMetrics(metric1, metric2) - - require.Equal(t, expectedEmpty, mergedMetrics) -} - -func TestMergeMetricsSingleEmpty(t *testing.T) { - expectedMetrics := simpleMetricsWithResource() - - metric1 := pmetric.NewMetrics() - metric2 := simpleMetricsWithResource() - - mergedMetrics := mergeMetrics(metric1, metric2) - - require.Equal(t, expectedMetrics, mergedMetrics) -} - -func TestMergeMetrics(t *testing.T) { - expectedMetrics := pmetric.NewMetrics() - expectedMetrics.ResourceMetrics().EnsureCapacity(3) - ametrics := expectedMetrics.ResourceMetrics().AppendEmpty() - ametrics.Resource().Attributes().PutStr(conventions.AttributeServiceName, "service-name-1") - ametrics.ScopeMetrics().AppendEmpty().Metrics().AppendEmpty().SetName("m1") - bmetrics := expectedMetrics.ResourceMetrics().AppendEmpty() - bmetrics.Resource().Attributes().PutStr(conventions.AttributeServiceName, "service-name-2") - bmetrics.ScopeMetrics().AppendEmpty().Metrics().AppendEmpty().SetName("m1") - cmetrics := expectedMetrics.ResourceMetrics().AppendEmpty() - cmetrics.Resource().Attributes().PutStr(conventions.AttributeServiceName, "service-name-3") - cmetrics.ScopeMetrics().AppendEmpty().Metrics().AppendEmpty().SetName("m2") - - metric1 := pmetric.NewMetrics() - metric1.ResourceMetrics().EnsureCapacity(2) - m1ametrics := metric1.ResourceMetrics().AppendEmpty() - m1ametrics.Resource().Attributes().PutStr(conventions.AttributeServiceName, "service-name-1") - m1ametrics.ScopeMetrics().AppendEmpty().Metrics().AppendEmpty().SetName("m1") - m1bmetrics := metric1.ResourceMetrics().AppendEmpty() - m1bmetrics.Resource().Attributes().PutStr(conventions.AttributeServiceName, "service-name-2") - m1bmetrics.ScopeMetrics().AppendEmpty().Metrics().AppendEmpty().SetName("m1") - - metric2 := pmetric.NewMetrics() - metric2.ResourceMetrics().EnsureCapacity(1) - m2cmetrics := metric2.ResourceMetrics().AppendEmpty() - m2cmetrics.Resource().Attributes().PutStr(conventions.AttributeServiceName, "service-name-3") - m2cmetrics.ScopeMetrics().AppendEmpty().Metrics().AppendEmpty().SetName("m2") - - mergedMetrics := mergeMetrics(metric1, metric2) - - require.Equal(t, expectedMetrics, mergedMetrics) -} - func benchMergeTraces(b *testing.B, tracesCount int) { traces1 := ptrace.NewTraces() traces2 := ptrace.NewTraces() @@ -146,30 +91,3 @@ func BenchmarkMergeTraces_X500(b *testing.B) { func BenchmarkMergeTraces_X1000(b *testing.B) { benchMergeTraces(b, 1000) } - -func benchMergeMetrics(b *testing.B, metricsCount int) { - metrics1 := pmetric.NewMetrics() - metrics2 := pmetric.NewMetrics() - - for i := 0; i < metricsCount; i++ { - appendSimpleMetricWithID(metrics2.ResourceMetrics().AppendEmpty(), "metrics-2") - } - - b.ResetTimer() - - for i := 0; i < b.N; i++ { - mergeMetrics(metrics1, metrics2) - } -} - -func BenchmarkMergeMetrics_X100(b *testing.B) { - benchMergeMetrics(b, 100) -} - -func BenchmarkMergeMetrics_X500(b *testing.B) { - benchMergeMetrics(b, 500) -} - -func BenchmarkMergeMetrics_X1000(b *testing.B) { - benchMergeMetrics(b, 1000) -} diff --git a/exporter/loadbalancingexporter/metrics_exporter.go b/exporter/loadbalancingexporter/metrics_exporter.go index 73e522ecf2b0..70b2f4a88593 100644 --- a/exporter/loadbalancingexporter/metrics_exporter.go +++ b/exporter/loadbalancingexporter/metrics_exporter.go @@ -7,8 +7,6 @@ import ( "context" "errors" "fmt" - "sort" - "strings" "sync" "time" @@ -18,18 +16,16 @@ import ( "go.opentelemetry.io/collector/consumer" "go.opentelemetry.io/collector/exporter" "go.opentelemetry.io/collector/exporter/otlpexporter" - "go.opentelemetry.io/collector/pdata/pcommon" "go.opentelemetry.io/collector/pdata/pmetric" conventions "go.opentelemetry.io/collector/semconv/v1.6.1" "go.uber.org/multierr" - "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/batchpersignal" + "github.com/open-telemetry/opentelemetry-collector-contrib/internal/exp/metrics" + "github.com/open-telemetry/opentelemetry-collector-contrib/internal/exp/metrics/identity" ) var _ exporter.Metrics = (*metricExporterImp)(nil) -type exporterMetrics map[*wrappedExporter]pmetric.Metrics - type metricExporterImp struct { loadBalancer *loadBalancer routingKey routingKey @@ -52,12 +48,12 @@ func newMetricsExporter(params exporter.Settings, cfg component.Config) (*metric metricExporter := metricExporterImp{loadBalancer: lb, routingKey: svcRouting} switch cfg.(*Config).RoutingKey { - case "service", "": + case svcRoutingStr, "": // default case for empty routing key metricExporter.routingKey = svcRouting - case "resource": + case resourceRoutingStr: metricExporter.routingKey = resourceRouting - case "metric": + case metricNameRoutingStr: metricExporter.routingKey = metricNameRouting default: return nil, fmt.Errorf("unsupported routing_key: %q", cfg.(*Config).RoutingKey) @@ -82,52 +78,60 @@ func (e *metricExporterImp) Shutdown(ctx context.Context) error { } func (e *metricExporterImp) ConsumeMetrics(ctx context.Context, md pmetric.Metrics) error { - batches := batchpersignal.SplitMetrics(md) - - exporterSegregatedMetrics := make(exporterMetrics) - endpoints := make(map[*wrappedExporter]string) + var batches map[string]pmetric.Metrics - for _, batch := range batches { - routingIDs, err := routingIdentifiersFromMetrics(batch, e.routingKey) + switch e.routingKey { + case svcRouting: + var err error + batches, err = splitMetricsByResourceServiceName(md) if err != nil { return err } + case resourceRouting: + batches = splitMetricsByResourceID(md) + case metricNameRouting: + batches = splitMetricsByMetricName(md) + } - for rid := range routingIDs { - exp, endpoint, err := e.loadBalancer.exporterAndEndpoint([]byte(rid)) - if err != nil { - return err - } + // Now assign each batch to an exporter, and merge as we go + metricsByExporter := map[*wrappedExporter]pmetric.Metrics{} + exporterEndpoints := map[*wrappedExporter]string{} - _, ok := exporterSegregatedMetrics[exp] - if !ok { - exp.consumeWG.Add(1) - exporterSegregatedMetrics[exp] = pmetric.NewMetrics() - } - exporterSegregatedMetrics[exp] = mergeMetrics(exporterSegregatedMetrics[exp], batch) + for routingID, mds := range batches { + exp, endpoint, err := e.loadBalancer.exporterAndEndpoint([]byte(routingID)) + if err != nil { + return err + } - endpoints[exp] = endpoint + expMetrics, ok := metricsByExporter[exp] + if !ok { + exp.consumeWG.Add(1) + expMetrics = pmetric.NewMetrics() + metricsByExporter[exp] = expMetrics + exporterEndpoints[exp] = endpoint } + + metrics.Merge(expMetrics, mds) } var errs error - - for exp, metrics := range exporterSegregatedMetrics { + for exp, mds := range metricsByExporter { start := time.Now() - err := exp.ConsumeMetrics(ctx, metrics) - exp.consumeWG.Done() + err := exp.ConsumeMetrics(ctx, mds) duration := time.Since(start) + + exp.consumeWG.Done() errs = multierr.Append(errs, err) if err == nil { _ = stats.RecordWithTags( ctx, - []tag.Mutator{tag.Upsert(endpointTagKey, endpoints[exp]), successTrueMutator}, + []tag.Mutator{tag.Upsert(endpointTagKey, exporterEndpoints[exp]), successTrueMutator}, mBackendLatency.M(duration.Milliseconds())) } else { _ = stats.RecordWithTags( ctx, - []tag.Mutator{tag.Upsert(endpointTagKey, endpoints[exp]), successFalseMutator}, + []tag.Mutator{tag.Upsert(endpointTagKey, exporterEndpoints[exp]), successFalseMutator}, mBackendLatency.M(duration.Milliseconds())) } } @@ -135,90 +139,99 @@ func (e *metricExporterImp) ConsumeMetrics(ctx context.Context, md pmetric.Metri return errs } -func routingIdentifiersFromMetrics(mds pmetric.Metrics, key routingKey) (map[string]bool, error) { - ids := make(map[string]bool) +func splitMetricsByResourceServiceName(md pmetric.Metrics) (map[string]pmetric.Metrics, error) { + results := map[string]pmetric.Metrics{} - // no need to test "empty labels" - // no need to test "empty resources" + for i := 0; i < md.ResourceMetrics().Len(); i++ { + rm := md.ResourceMetrics().At(i) - rs := mds.ResourceMetrics() - if rs.Len() == 0 { - return nil, errors.New("empty resource metrics") - } + svc, ok := rm.Resource().Attributes().Get(conventions.AttributeServiceName) + if !ok { + return nil, errors.New("unable to get service name") + } - ils := rs.At(0).ScopeMetrics() - if ils.Len() == 0 { - return nil, errors.New("empty scope metrics") - } + newMD := pmetric.NewMetrics() + rmClone := newMD.ResourceMetrics().AppendEmpty() + rm.CopyTo(rmClone) - metrics := ils.At(0).Metrics() - if metrics.Len() == 0 { - return nil, errors.New("empty metrics") + key := svc.Str() + existing, ok := results[key] + if ok { + metrics.Merge(existing, newMD) + } else { + results[key] = newMD + } } - for i := 0; i < rs.Len(); i++ { - resource := rs.At(i).Resource() - switch key { - default: - case svcRouting, traceIDRouting: - svc, ok := resource.Attributes().Get(conventions.AttributeServiceName) - if !ok { - return nil, errors.New("unable to get service name") - } - ids[svc.Str()] = true - case metricNameRouting: - sm := rs.At(i).ScopeMetrics() - for j := 0; j < sm.Len(); j++ { - metrics := sm.At(j).Metrics() - for k := 0; k < metrics.Len(); k++ { - md := metrics.At(k) - rKey := metricRoutingKey(md) - ids[rKey] = true - } - } - case resourceRouting: - sm := rs.At(i).ScopeMetrics() - for j := 0; j < sm.Len(); j++ { - metrics := sm.At(j).Metrics() - for k := 0; k < metrics.Len(); k++ { - md := metrics.At(k) - rKey := resourceRoutingKey(md, resource.Attributes()) - ids[rKey] = true - } - } + return results, nil +} + +func splitMetricsByResourceID(md pmetric.Metrics) map[string]pmetric.Metrics { + results := map[string]pmetric.Metrics{} + + for i := 0; i < md.ResourceMetrics().Len(); i++ { + rm := md.ResourceMetrics().At(i) + + newMD := pmetric.NewMetrics() + rmClone := newMD.ResourceMetrics().AppendEmpty() + rm.CopyTo(rmClone) + + key := identity.OfResource(rm.Resource()).String() + existing, ok := results[key] + if ok { + metrics.Merge(existing, newMD) + } else { + results[key] = newMD } } - return ids, nil - + return results } -// maintain -func sortedMapAttrs(attrs pcommon.Map) []string { - keys := make([]string, 0) - for k := range attrs.AsRaw() { - keys = append(keys, k) - } - sort.Strings(keys) +func splitMetricsByMetricName(md pmetric.Metrics) map[string]pmetric.Metrics { + results := map[string]pmetric.Metrics{} - attrsHash := make([]string, 0) - for _, k := range keys { - attrsHash = append(attrsHash, k) - if v, ok := attrs.Get(k); ok { - attrsHash = append(attrsHash, v.AsString()) + for i := 0; i < md.ResourceMetrics().Len(); i++ { + rm := md.ResourceMetrics().At(i) + + for j := 0; j < rm.ScopeMetrics().Len(); j++ { + sm := rm.ScopeMetrics().At(j) + + for k := 0; k < sm.Metrics().Len(); k++ { + m := sm.Metrics().At(k) + + newMD, mClone := cloneMetricWithoutType(rm, sm, m) + m.CopyTo(mClone) + + key := m.Name() + existing, ok := results[key] + if ok { + metrics.Merge(existing, newMD) + } else { + results[key] = newMD + } + } } } - return attrsHash + + return results } -func resourceRoutingKey(md pmetric.Metric, attrs pcommon.Map) string { - attrsHash := sortedMapAttrs(attrs) - attrsHash = append(attrsHash, md.Name()) - routingRef := strings.Join(attrsHash, "") +func cloneMetricWithoutType(rm pmetric.ResourceMetrics, sm pmetric.ScopeMetrics, m pmetric.Metric) (md pmetric.Metrics, mClone pmetric.Metric) { + md = pmetric.NewMetrics() - return routingRef -} + rmClone := md.ResourceMetrics().AppendEmpty() + rm.Resource().CopyTo(rmClone.Resource()) + rmClone.SetSchemaUrl(rm.SchemaUrl()) + + smClone := rmClone.ScopeMetrics().AppendEmpty() + sm.Scope().CopyTo(smClone.Scope()) + smClone.SetSchemaUrl(sm.SchemaUrl()) + + mClone = smClone.Metrics().AppendEmpty() + mClone.SetName(m.Name()) + mClone.SetDescription(m.Description()) + mClone.SetUnit(m.Unit()) -func metricRoutingKey(md pmetric.Metric) string { - return md.Name() + return md, mClone } diff --git a/exporter/loadbalancingexporter/metrics_exporter_test.go b/exporter/loadbalancingexporter/metrics_exporter_test.go index 303d1a3a94a1..c3d6907c913f 100644 --- a/exporter/loadbalancingexporter/metrics_exporter_test.go +++ b/exporter/loadbalancingexporter/metrics_exporter_test.go @@ -9,6 +9,7 @@ import ( "fmt" "math/rand" "net" + "os" "path/filepath" "strconv" "sync" @@ -16,6 +17,7 @@ import ( "testing" "time" + jsoniter "github.com/json-iterator/go" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.opentelemetry.io/collector/component" @@ -26,19 +28,18 @@ import ( "go.opentelemetry.io/collector/exporter/exportertest" "go.opentelemetry.io/collector/exporter/otlpexporter" "go.opentelemetry.io/collector/otelcol/otelcoltest" - "go.opentelemetry.io/collector/pdata/pcommon" "go.opentelemetry.io/collector/pdata/pmetric" conventions "go.opentelemetry.io/collector/semconv/v1.9.0" "go.uber.org/zap" + "gopkg.in/yaml.v2" "github.com/open-telemetry/opentelemetry-collector-contrib/exporter/loadbalancingexporter/internal/metadata" + "github.com/open-telemetry/opentelemetry-collector-contrib/internal/exp/metrics" + "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/golden" + "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatatest/pmetrictest" ) const ( - serviceRouteKey = "service" - resourceRouteKey = "resource" - metricRouteKey = "metric" - ilsName1 = "library-1" ilsName2 = "library-2" keyAttr1 = "resattr-1" @@ -88,7 +89,7 @@ func TestNewMetricsExporter(t *testing.T) { { "traceID", &Config{ - RoutingKey: "service", + RoutingKey: traceIDRoutingStr, }, errNoResolver, }, @@ -162,186 +163,398 @@ func TestMetricsExporterShutdown(t *testing.T) { assert.Nil(t, res) } -func TestConsumeMetrics(t *testing.T) { - componentFactory := func(_ context.Context, _ string) (component.Component, error) { - return newNopMockMetricsExporter(), nil - } - lb, err := newLoadBalancer(exportertest.NewNopSettings(), serviceBasedRoutingConfig(), componentFactory) - require.NotNil(t, lb) +// loadMetricsMap will parse the given yaml file into a map[string]pmetric.Metrics +func loadMetricsMap(t *testing.T, path string) map[string]pmetric.Metrics { + b, err := os.ReadFile(path) require.NoError(t, err) - p, err := newMetricsExporter(exportertest.NewNopSettings(), serviceBasedRoutingConfig()) - require.NotNil(t, p) + var expectedOutputRaw map[string]any + err = yaml.Unmarshal(b, &expectedOutputRaw) require.NoError(t, err) - assert.Equal(t, p.routingKey, svcRouting) - // pre-load an exporter here, so that we don't use the actual OTLP exporter - lb.addMissingExporters(context.Background(), []string{"endpoint-1", "endpoint-2"}) - lb.res = &mockResolver{ - triggerCallbacks: true, - onResolve: func(_ context.Context) ([]string, error) { - return []string{"endpoint-1", "endpoint-2"}, nil - }, + expectedOutput := map[string]pmetric.Metrics{} + for key, data := range expectedOutputRaw { + b, err = jsoniter.Marshal(data) + require.NoError(t, err) + + unmarshaller := &pmetric.JSONUnmarshaler{} + md, err := unmarshaller.UnmarshalMetrics(b) + require.NoError(t, err) + + expectedOutput[key] = md } - p.loadBalancer = lb - err = p.Start(context.Background(), componenttest.NewNopHost()) - require.NoError(t, err) - defer func() { - require.NoError(t, p.Shutdown(context.Background())) - }() + return expectedOutput +} - // test - res := p.ConsumeMetrics(context.Background(), simpleMetricsWithNoService()) +func compareMetricsMaps(t *testing.T, expected map[string]pmetric.Metrics, actual map[string]pmetric.Metrics) { + expectedKeys := make([]string, 0, len(expected)) + for key := range expected { + expectedKeys = append(expectedKeys, key) + } - // verify - assert.Error(t, res) + actualKeys := make([]string, 0, len(actual)) + for key := range actual { + actualKeys = append(actualKeys, key) + } + require.ElementsMatch(t, expectedKeys, actualKeys, "Maps have differing keys") + + for key, actualMD := range actual { + expectedMD := expected[key] + t.Logf("Comparing map values for key: %s", key) + require.NoError(t, pmetrictest.CompareMetrics( + expectedMD, actualMD, + // We have to ignore ordering, because we do MergeMetrics() inside a map + // iteration. And golang map iteration order is random. This means the + // order of the merges is random + pmetrictest.IgnoreResourceMetricsOrder(), + pmetrictest.IgnoreScopeMetricsOrder(), + pmetrictest.IgnoreMetricsOrder(), + pmetrictest.IgnoreMetricDataPointsOrder(), + )) + } } -// this test validates that exporter is can concurrently change the endpoints while consuming metrics. -func TestConsumeMetrics_ConcurrentResolverChange(t *testing.T) { - consumeStarted := make(chan struct{}) - consumeDone := make(chan struct{}) +func TestSplitMetricsByResourceServiceName(t *testing.T) { + t.Parallel() - // imitate a slow exporter - te := &mockMetricsExporter{Component: mockComponent{}} - te.ConsumeMetricsFn = func(_ context.Context, _ pmetric.Metrics) error { - close(consumeStarted) - time.Sleep(50 * time.Millisecond) - return te.consumeErr + testCases := []string{ + "basic_resource_service_name", + "duplicate_resource_service_name", } - componentFactory := func(_ context.Context, _ string) (component.Component, error) { - return te, nil + + for _, tc := range testCases { + testName := tc + + t.Run(testName, func(t *testing.T) { + t.Parallel() + + dir := filepath.Join("testdata", "metrics", "split_metrics", testName) + + input, err := golden.ReadMetrics(filepath.Join(dir, "input.yaml")) + require.NoError(t, err) + + expectedOutput := loadMetricsMap(t, filepath.Join(dir, "output.yaml")) + + output, err := splitMetricsByResourceServiceName(input) + require.NoError(t, err) + compareMetricsMaps(t, expectedOutput, output) + }) } - lb, err := newLoadBalancer(exportertest.NewNopSettings(), simpleConfig(), componentFactory) - require.NotNil(t, lb) - require.NoError(t, err) +} - p, err := newMetricsExporter(exportertest.NewNopSettings(), simpleConfig()) - require.NotNil(t, p) +func TestSplitMetricsByResourceServiceNameFailsIfMissingServiceNameAttribute(t *testing.T) { + t.Parallel() + + input, err := golden.ReadMetrics(filepath.Join("testdata", "metrics", "split_metrics", "missing_service_name", "input.yaml")) require.NoError(t, err) - endpoints := []string{"endpoint-1"} - lb.res = &mockResolver{ - triggerCallbacks: true, - onResolve: func(_ context.Context) ([]string, error) { - return endpoints, nil + _, err = splitMetricsByResourceServiceName(input) + require.Error(t, err) +} + +func TestSplitMetrics(t *testing.T) { + t.Parallel() + + testCases := []struct { + name string + splitFunc func(md pmetric.Metrics) map[string]pmetric.Metrics + }{ + { + name: "basic_resource_id", + splitFunc: splitMetricsByResourceID, + }, + { + name: "duplicate_resource_id", + splitFunc: splitMetricsByResourceID, + }, + { + name: "basic_metric_name", + splitFunc: splitMetricsByMetricName, + }, + { + name: "duplicate_metric_name", + splitFunc: splitMetricsByMetricName, }, } - p.loadBalancer = lb - err = p.Start(context.Background(), componenttest.NewNopHost()) - require.NoError(t, err) - defer func() { - require.NoError(t, p.Shutdown(context.Background())) - }() + for _, tc := range testCases { + // Purposely make a copy since we're running in a goroutine due to t.Parallel() + tc := tc - go func() { - assert.NoError(t, p.ConsumeMetrics(context.Background(), simpleMetricsWithResource())) - close(consumeDone) - }() + t.Run(tc.name, func(t *testing.T) { + t.Parallel() - // update endpoint while consuming logs - <-consumeStarted - endpoints = []string{"endpoint-2"} - endpoint, err := lb.res.resolve(context.Background()) - require.NoError(t, err) - require.Equal(t, endpoints, endpoint) - <-consumeDone -} + dir := filepath.Join("testdata", "metrics", "split_metrics", tc.name) -func TestConsumeMetricsServiceBased(t *testing.T) { - componentFactory := func(_ context.Context, _ string) (component.Component, error) { - return newNopMockMetricsExporter(), nil + input, err := golden.ReadMetrics(filepath.Join(dir, "input.yaml")) + require.NoError(t, err) + + expectedOutput := loadMetricsMap(t, filepath.Join(dir, "output.yaml")) + + output := tc.splitFunc(input) + require.NoError(t, err) + compareMetricsMaps(t, expectedOutput, output) + }) } - lb, err := newLoadBalancer(exportertest.NewNopSettings(), serviceBasedRoutingConfig(), componentFactory) - require.NotNil(t, lb) - require.NoError(t, err) +} - p, err := newMetricsExporter(exportertest.NewNopSettings(), serviceBasedRoutingConfig()) - require.NotNil(t, p) - require.NoError(t, err) - assert.Equal(t, p.routingKey, svcRouting) +func TestConsumeMetrics_SingleEndpoint(t *testing.T) { + t.Parallel() - // pre-load an exporter here, so that we don't use the actual OTLP exporter - lb.addMissingExporters(context.Background(), []string{"endpoint-1"}) - lb.res = &mockResolver{ - triggerCallbacks: true, - onResolve: func(_ context.Context) ([]string, error) { - return []string{"endpoint-1"}, nil + testCases := []struct { + name string + routingKey string + }{ + { + name: "resource_service_name", + routingKey: svcRoutingStr, + }, + { + name: "resource_id", + routingKey: resourceRoutingStr, + }, + { + name: "metric_name", + routingKey: metricNameRoutingStr, }, } - p.loadBalancer = lb - err = p.Start(context.Background(), componenttest.NewNopHost()) - require.NoError(t, err) - defer func() { - require.NoError(t, p.Shutdown(context.Background())) - }() + for _, tc := range testCases { + // Purposely make a copy since we're running in a goroutine due to t.Parallel() + tc := tc - // test - res := p.ConsumeMetrics(context.Background(), simpleMetricsWithServiceName()) + t.Run(tc.name, func(t *testing.T) { + t.Parallel() - // verify - assert.Nil(t, res) -} + createSettings := exportertest.NewNopSettings() + config := &Config{ + Resolver: ResolverSettings{ + Static: &StaticResolver{Hostnames: []string{"endpoint-1"}}, + }, + RoutingKey: tc.routingKey, + } -func TestConsumeMetricsResourceBased(t *testing.T) { - componentFactory := func(_ context.Context, _ string) (component.Component, error) { - return newNopMockMetricsExporter(), nil + p, err := newMetricsExporter(createSettings, config) + require.NoError(t, err) + require.NotNil(t, p) + + // newMetricsExporter will internally create a loadBalancer instance which is + // hardcoded to use OTLP exporters + // We manually override that to use our testing sink + sink := consumertest.MetricsSink{} + componentFactory := func(_ context.Context, _ string) (component.Component, error) { + return newMockMetricsExporter(sink.ConsumeMetrics), nil + } + + lb, err := newLoadBalancer(createSettings, config, componentFactory) + require.NoError(t, err) + require.NotNil(t, lb) + + lb.addMissingExporters(context.Background(), []string{"endpoint-1"}) + lb.res = &mockResolver{ + triggerCallbacks: true, + onResolve: func(_ context.Context) ([]string, error) { + return []string{"endpoint-1"}, nil + }, + } + p.loadBalancer = lb + + // Start everything up + err = p.Start(context.Background(), componenttest.NewNopHost()) + require.NoError(t, err) + defer func() { + require.NoError(t, p.Shutdown(context.Background())) + }() + + // Test + dir := filepath.Join("testdata", "metrics", "consume_metrics", "single_endpoint", tc.name) + + input, err := golden.ReadMetrics(filepath.Join(dir, "input.yaml")) + require.NoError(t, err) + + err = p.ConsumeMetrics(context.Background(), input) + require.NoError(t, err) + + expectedOutput, err := golden.ReadMetrics(filepath.Join(dir, "output.yaml")) + require.NoError(t, err) + + allOutputs := sink.AllMetrics() + require.Len(t, allOutputs, 1) + + actualOutput := allOutputs[0] + require.NoError(t, pmetrictest.CompareMetrics( + expectedOutput, actualOutput, + // We have to ignore ordering, because we do MergeMetrics() inside a map + // iteration. And golang map iteration order is random. This means the + // order of the merges is random + pmetrictest.IgnoreResourceMetricsOrder(), + pmetrictest.IgnoreScopeMetricsOrder(), + pmetrictest.IgnoreMetricsOrder(), + pmetrictest.IgnoreMetricDataPointsOrder(), + )) + }) } - lb, err := newLoadBalancer(exportertest.NewNopSettings(), resourceBasedRoutingConfig(), componentFactory) - require.NotNil(t, lb) - require.NoError(t, err) +} - p, err := newMetricsExporter(exportertest.NewNopSettings(), resourceBasedRoutingConfig()) - require.NotNil(t, p) - require.NoError(t, err) - assert.Equal(t, p.routingKey, resourceRouting) +func TestConsumeMetrics_TripleEndpoint(t *testing.T) { + // I'm not fully satisfied with the design of this test. + // We're hard-reliant on the implementation of the ring hash to give use the routing. + // So if that algorithm changes, all these tests will need to be updated. In addition, + // it's not easy to "know" what the routing *should* be. Can *can* calculate it by + // hand, but it's very tedious. - // pre-load an exporter here, so that we don't use the actual OTLP exporter - lb.addMissingExporters(context.Background(), []string{"endpoint-1"}) - lb.res = &mockResolver{ - triggerCallbacks: true, - onResolve: func(_ context.Context) ([]string, error) { - return []string{"endpoint-1"}, nil + t.Parallel() + + testCases := []struct { + name string + routingKey string + }{ + { + name: "resource_service_name", + routingKey: svcRoutingStr, + }, + { + name: "resource_id", + routingKey: resourceRoutingStr, + }, + { + name: "metric_name", + routingKey: metricNameRoutingStr, }, } - p.loadBalancer = lb - err = p.Start(context.Background(), componenttest.NewNopHost()) - require.NoError(t, err) - defer func() { - require.NoError(t, p.Shutdown(context.Background())) - }() + for _, tc := range testCases { + // Purposely make a copy since we're running in a goroutine due to t.Parallel() + tc := tc - // test - res := p.ConsumeMetrics(context.Background(), simpleMetricsWithResource()) + t.Run(tc.name, func(t *testing.T) { + t.Parallel() - // verify - assert.Nil(t, res) + createSettings := exportertest.NewNopSettings() + config := &Config{ + Resolver: ResolverSettings{ + Static: &StaticResolver{Hostnames: []string{"endpoint-1", "endpoint-2", "endpoint-3"}}, + }, + RoutingKey: tc.routingKey, + } + + p, err := newMetricsExporter(createSettings, config) + require.NoError(t, err) + require.NotNil(t, p) + + // newMetricsExporter will internally create a loadBalancer instance which is + // hardcoded to use OTLP exporters + // We manually override that to use our testing sink + sink1 := consumertest.MetricsSink{} + sink2 := consumertest.MetricsSink{} + sink3 := consumertest.MetricsSink{} + componentFactory := func(_ context.Context, endpoint string) (component.Component, error) { + if endpoint == "endpoint-1:4317" { + return newMockMetricsExporter(sink1.ConsumeMetrics), nil + } + if endpoint == "endpoint-2:4317" { + return newMockMetricsExporter(sink2.ConsumeMetrics), nil + } + if endpoint == "endpoint-3:4317" { + return newMockMetricsExporter(sink3.ConsumeMetrics), nil + } + + t.Fatalf("invalid endpoint %s", endpoint) + return nil, errors.New("invalid endpoint") + } + + lb, err := newLoadBalancer(createSettings, config, componentFactory) + require.NoError(t, err) + require.NotNil(t, lb) + + lb.addMissingExporters(context.Background(), []string{"endpoint-1", "endpoint-2", "endpoint-3"}) + lb.res = &mockResolver{ + triggerCallbacks: true, + onResolve: func(_ context.Context) ([]string, error) { + return []string{"endpoint-1", "endpoint-2", "endpoint-3"}, nil + }, + } + p.loadBalancer = lb + + // Start everything up + err = p.Start(context.Background(), componenttest.NewNopHost()) + require.NoError(t, err) + defer func() { + require.NoError(t, p.Shutdown(context.Background())) + }() + + // Test + dir := filepath.Join("testdata", "metrics", "consume_metrics", "triple_endpoint", tc.name) + + input, err := golden.ReadMetrics(filepath.Join(dir, "input.yaml")) + require.NoError(t, err) + + err = p.ConsumeMetrics(context.Background(), input) + require.NoError(t, err) + + expectedOutput := loadMetricsMap(t, filepath.Join(dir, "output.yaml")) + + actualOutput := map[string]pmetric.Metrics{} + + sink1Outputs := sink1.AllMetrics() + require.LessOrEqual(t, len(sink1Outputs), 1) + if len(sink1Outputs) == 1 { + actualOutput["endpoint-1"] = sink1Outputs[0] + } else { + actualOutput["endpoint-1"] = pmetric.NewMetrics() + } + + sink2Outputs := sink2.AllMetrics() + require.LessOrEqual(t, len(sink2Outputs), 1) + if len(sink2Outputs) == 1 { + actualOutput["endpoint-2"] = sink2Outputs[0] + } else { + actualOutput["endpoint-2"] = pmetric.NewMetrics() + } + + sink3Outputs := sink3.AllMetrics() + require.LessOrEqual(t, len(sink3Outputs), 1) + if len(sink3Outputs) == 1 { + actualOutput["endpoint-3"] = sink3Outputs[0] + } else { + actualOutput["endpoint-3"] = pmetric.NewMetrics() + } + + compareMetricsMaps(t, expectedOutput, actualOutput) + }) + } } -func TestConsumeMetricsMetricNameBased(t *testing.T) { +// this test validates that exporter is can concurrently change the endpoints while consuming metrics. +func TestConsumeMetrics_ConcurrentResolverChange(t *testing.T) { + consumeStarted := make(chan struct{}) + consumeDone := make(chan struct{}) + + // imitate a slow exporter + te := &mockMetricsExporter{Component: mockComponent{}} + te.ConsumeMetricsFn = func(_ context.Context, _ pmetric.Metrics) error { + close(consumeStarted) + time.Sleep(50 * time.Millisecond) + return te.consumeErr + } componentFactory := func(_ context.Context, _ string) (component.Component, error) { - return newNopMockMetricsExporter(), nil + return te, nil } - lb, err := newLoadBalancer(exportertest.NewNopSettings(), metricNameBasedRoutingConfig(), componentFactory) + lb, err := newLoadBalancer(exportertest.NewNopSettings(), simpleConfig(), componentFactory) require.NotNil(t, lb) require.NoError(t, err) - p, err := newMetricsExporter(exportertest.NewNopSettings(), metricNameBasedRoutingConfig()) + p, err := newMetricsExporter(exportertest.NewNopSettings(), simpleConfig()) require.NotNil(t, p) require.NoError(t, err) - assert.Equal(t, p.routingKey, metricNameRouting) - // pre-load an exporter here, so that we don't use the actual OTLP exporter - lb.addMissingExporters(context.Background(), []string{"endpoint-1"}) + endpoints := []string{"endpoint-1"} lb.res = &mockResolver{ triggerCallbacks: true, onResolve: func(_ context.Context) ([]string, error) { - return []string{"endpoint-1"}, nil + return endpoints, nil }, } p.loadBalancer = lb @@ -352,34 +565,18 @@ func TestConsumeMetricsMetricNameBased(t *testing.T) { require.NoError(t, p.Shutdown(context.Background())) }() - // test - res := p.ConsumeMetrics(context.Background(), simpleMetricsWithResource()) - - // verify - assert.Nil(t, res) -} - -func TestServiceBasedRoutingForSameMetricName(t *testing.T) { + go func() { + assert.NoError(t, p.ConsumeMetrics(context.Background(), simpleMetricsWithResource())) + close(consumeDone) + }() - for _, tt := range []struct { - desc string - batch pmetric.Metrics - routingKey routingKey - res map[string]bool - }{ - { - "different services - service based routing", - twoServicesWithSameMetricName(), - svcRouting, - map[string]bool{serviceName1: true, serviceName2: true}, - }, - } { - t.Run(tt.desc, func(t *testing.T) { - res, err := routingIdentifiersFromMetrics(tt.batch, tt.routingKey) - assert.Equal(t, err, nil) - assert.Equal(t, res, tt.res) - }) - } + // update endpoint while consuming logs + <-consumeStarted + endpoints = []string{"endpoint-2"} + endpoint, err := lb.res.resolve(context.Background()) + require.NoError(t, err) + require.Equal(t, endpoints, endpoint) + <-consumeDone } func TestConsumeMetricsExporterNoEndpoint(t *testing.T) { @@ -511,84 +708,6 @@ func TestBatchWithTwoMetrics(t *testing.T) { assert.Len(t, sink.AllMetrics(), 2) } -func TestNoMetricsInBatch(t *testing.T) { - for _, tt := range []struct { - desc string - batch pmetric.Metrics - routingKey routingKey - err error - }{ - { - "no resource metrics", - pmetric.NewMetrics(), - svcRouting, - errors.New("empty resource metrics"), - }, - { - "no instrumentation library metrics", - func() pmetric.Metrics { - batch := pmetric.NewMetrics() - batch.ResourceMetrics().AppendEmpty() - return batch - }(), - svcRouting, - errors.New("empty scope metrics"), - }, - { - "no metrics", - func() pmetric.Metrics { - batch := pmetric.NewMetrics() - batch.ResourceMetrics().AppendEmpty().ScopeMetrics().AppendEmpty() - return batch - }(), - svcRouting, - errors.New("empty metrics"), - }, - } { - t.Run(tt.desc, func(t *testing.T) { - res, err := routingIdentifiersFromMetrics(tt.batch, tt.routingKey) - assert.Equal(t, err, tt.err) - assert.Equal(t, res, map[string]bool(nil)) - }) - } -} - -func TestResourceRoutingKey(t *testing.T) { - - md := pmetric.NewMetric() - md.SetName("metric") - attrs := pcommon.NewMap() - if got := resourceRoutingKey(md, attrs); got != "metric" { - t.Errorf("metricRoutingKey() = %v, want %v", got, "metric") - } - - attrs.PutStr("k1", "v1") - if got := resourceRoutingKey(md, attrs); got != "k1v1metric" { - t.Errorf("metricRoutingKey() = %v, want %v", got, "k1v1metric") - } - - attrs.PutStr("k2", "v2") - if got := resourceRoutingKey(md, attrs); got != "k1v1k2v2metric" { - t.Errorf("metricRoutingKey() = %v, want %v", got, "k1v1k2v2metric") - } -} - -func TestMetricNameRoutingKey(t *testing.T) { - - md := pmetric.NewMetric() - md.SetName(signal1Name) - if got := metricRoutingKey(md); got != signal1Name { - t.Errorf("metricRoutingKey() = %v, want %v", got, signal1Name) - } - - md = pmetric.NewMetric() - md.SetName(signal2Name) - if got := metricRoutingKey(md); got != signal2Name { - t.Errorf("metricRoutingKey() = %v, want %v", got, signal2Name) - } - -} - func TestRollingUpdatesWhenConsumeMetrics(t *testing.T) { t.Skip("Flaky Test - See https://github.com/open-telemetry/opentelemetry-collector-contrib/issues/13331") @@ -774,7 +893,7 @@ func benchConsumeMetrics(b *testing.B, endpointsCount int, metricsCount int) { } } simpleMetricsWithServiceName() - md := mergeMetrics(metric1, metric2) + md := metrics.Merge(metric1, metric2) b.ResetTimer() @@ -834,7 +953,7 @@ func resourceBasedRoutingConfig() *Config { Resolver: ResolverSettings{ Static: &StaticResolver{Hostnames: []string{"endpoint-1", "endpoint-2"}}, }, - RoutingKey: resourceRouteKey, + RoutingKey: resourceRoutingStr, } } @@ -843,7 +962,7 @@ func metricNameBasedRoutingConfig() *Config { Resolver: ResolverSettings{ Static: &StaticResolver{Hostnames: []string{"endpoint-1", "endpoint-2"}}, }, - RoutingKey: metricRouteKey, + RoutingKey: metricNameRoutingStr, } } @@ -855,12 +974,6 @@ func randomMetrics() pmetric.Metrics { return metrics } -func simpleMetricsWithNoService() pmetric.Metrics { - metrics := pmetric.NewMetrics() - appendSimpleMetricWithID(metrics.ResourceMetrics().AppendEmpty(), "simple-metric-name") - return metrics -} - func simpleMetricsWithServiceName() pmetric.Metrics { metrics := pmetric.NewMetrics() metrics.ResourceMetrics().EnsureCapacity(1) diff --git a/exporter/loadbalancingexporter/testdata/metrics/consume_metrics/single_endpoint/metric_name/input.yaml b/exporter/loadbalancingexporter/testdata/metrics/consume_metrics/single_endpoint/metric_name/input.yaml new file mode 100644 index 000000000000..d81894c1fbb4 --- /dev/null +++ b/exporter/loadbalancingexporter/testdata/metrics/consume_metrics/single_endpoint/metric_name/input.yaml @@ -0,0 +1,77 @@ +resourceMetrics: + - schemaUrl: https://test-res-schema.com/schema + resource: + attributes: + - key: resource_key + value: + stringValue: foo + scopeMetrics: + - schemaUrl: https://test-scope-schema.com/schema + scope: + name: MyTestInstrument + version: "1.2.3" + attributes: + - key: scope_key + value: + stringValue: foo + metrics: + - name: first.monotonic.sum + sum: + aggregationTemporality: 2 + isMonotonic: true + dataPoints: + - timeUnixNano: 50 + asDouble: 333 + attributes: + - key: aaa + value: + stringValue: bbb + - name: second.monotonic.sum + sum: + aggregationTemporality: 2 + isMonotonic: true + dataPoints: + - timeUnixNano: 50 + asDouble: 945 + attributes: + - key: aaa + value: + stringValue: bbb + - schemaUrl: https://test-res-schema.com/schema + resource: + attributes: + - key: resource_key + value: + stringValue: foo + scopeMetrics: + - schemaUrl: https://test-scope-schema.com/schema + scope: + name: MyTestInstrument + version: "1.2.3" + attributes: + - key: scope_key + value: + stringValue: foo + metrics: + - name: first.monotonic.sum + sum: + aggregationTemporality: 2 + isMonotonic: true + dataPoints: + - timeUnixNano: 80 + asDouble: 444 + attributes: + - key: aaa + value: + stringValue: bbb + - name: second.monotonic.sum + sum: + aggregationTemporality: 2 + isMonotonic: true + dataPoints: + - timeUnixNano: 80 + asDouble: 1000 + attributes: + - key: aaa + value: + stringValue: bbb diff --git a/exporter/loadbalancingexporter/testdata/metrics/consume_metrics/single_endpoint/metric_name/output.yaml b/exporter/loadbalancingexporter/testdata/metrics/consume_metrics/single_endpoint/metric_name/output.yaml new file mode 100644 index 000000000000..9813bfbd7324 --- /dev/null +++ b/exporter/loadbalancingexporter/testdata/metrics/consume_metrics/single_endpoint/metric_name/output.yaml @@ -0,0 +1,51 @@ +resourceMetrics: + - schemaUrl: https://test-res-schema.com/schema + resource: + attributes: + - key: resource_key + value: + stringValue: foo + scopeMetrics: + - schemaUrl: https://test-scope-schema.com/schema + scope: + name: MyTestInstrument + version: "1.2.3" + attributes: + - key: scope_key + value: + stringValue: foo + metrics: + - name: first.monotonic.sum + sum: + aggregationTemporality: 2 + isMonotonic: true + dataPoints: + - timeUnixNano: 50 + asDouble: 333 + attributes: + - key: aaa + value: + stringValue: bbb + - timeUnixNano: 80 + asDouble: 444 + attributes: + - key: aaa + value: + stringValue: bbb + - name: second.monotonic.sum + sum: + aggregationTemporality: 2 + isMonotonic: true + dataPoints: + - timeUnixNano: 50 + asDouble: 945 + attributes: + - key: aaa + value: + stringValue: bbb + - timeUnixNano: 80 + asDouble: 1000 + attributes: + - key: aaa + value: + stringValue: bbb diff --git a/exporter/loadbalancingexporter/testdata/metrics/consume_metrics/single_endpoint/resource_id/input.yaml b/exporter/loadbalancingexporter/testdata/metrics/consume_metrics/single_endpoint/resource_id/input.yaml new file mode 100644 index 000000000000..7d8e24c2cfb8 --- /dev/null +++ b/exporter/loadbalancingexporter/testdata/metrics/consume_metrics/single_endpoint/resource_id/input.yaml @@ -0,0 +1,55 @@ +resourceMetrics: + - schemaUrl: https://test-res-schema.com/schema + resource: + attributes: + - key: resource_key + value: + stringValue: foo + scopeMetrics: + - schemaUrl: https://test-scope-schema.com/schema + scope: + name: MyTestInstrument + version: "1.2.3" + attributes: + - key: scope_key + value: + stringValue: foo + metrics: + - name: cumulative.monotonic.sum + sum: + aggregationTemporality: 2 + isMonotonic: true + dataPoints: + - timeUnixNano: 50 + asDouble: 333 + attributes: + - key: aaa + value: + stringValue: bbb + - schemaUrl: https://test-res-schema.com/schema + resource: + attributes: + - key: resource_key + value: + stringValue: foo + scopeMetrics: + - schemaUrl: https://test-scope-schema.com/schema + scope: + name: MyTestInstrument + version: "1.2.3" + attributes: + - key: scope_key + value: + stringValue: foo + metrics: + - name: cumulative.monotonic.sum + sum: + aggregationTemporality: 2 + isMonotonic: true + dataPoints: + - timeUnixNano: 80 + asDouble: 555 + attributes: + - key: aaa + value: + stringValue: bbb diff --git a/exporter/loadbalancingexporter/testdata/metrics/consume_metrics/single_endpoint/resource_id/output.yaml b/exporter/loadbalancingexporter/testdata/metrics/consume_metrics/single_endpoint/resource_id/output.yaml new file mode 100644 index 000000000000..28029bd23802 --- /dev/null +++ b/exporter/loadbalancingexporter/testdata/metrics/consume_metrics/single_endpoint/resource_id/output.yaml @@ -0,0 +1,34 @@ +resourceMetrics: + - schemaUrl: https://test-res-schema.com/schema + resource: + attributes: + - key: resource_key + value: + stringValue: foo + scopeMetrics: + - schemaUrl: https://test-scope-schema.com/schema + scope: + name: MyTestInstrument + version: "1.2.3" + attributes: + - key: scope_key + value: + stringValue: foo + metrics: + - name: cumulative.monotonic.sum + sum: + aggregationTemporality: 2 + isMonotonic: true + dataPoints: + - timeUnixNano: 50 + asDouble: 333 + attributes: + - key: aaa + value: + stringValue: bbb + - timeUnixNano: 80 + asDouble: 555 + attributes: + - key: aaa + value: + stringValue: bbb diff --git a/exporter/loadbalancingexporter/testdata/metrics/consume_metrics/single_endpoint/resource_service_name/input.yaml b/exporter/loadbalancingexporter/testdata/metrics/consume_metrics/single_endpoint/resource_service_name/input.yaml new file mode 100644 index 000000000000..1a0a17714a3a --- /dev/null +++ b/exporter/loadbalancingexporter/testdata/metrics/consume_metrics/single_endpoint/resource_service_name/input.yaml @@ -0,0 +1,55 @@ +resourceMetrics: + - schemaUrl: https://test-res-schema.com/schema + resource: + attributes: + - key: service.name + value: + stringValue: serviceA + scopeMetrics: + - schemaUrl: https://test-scope-schema.com/schema + scope: + name: MyTestInstrument + version: "1.2.3" + attributes: + - key: scope_key + value: + stringValue: foo + metrics: + - name: cumulative.monotonic.sum + sum: + aggregationTemporality: 2 + isMonotonic: true + dataPoints: + - timeUnixNano: 50 + asDouble: 333 + attributes: + - key: aaa + value: + stringValue: bbb + - schemaUrl: https://test-res-schema.com/schema + resource: + attributes: + - key: service.name + value: + stringValue: serviceA + scopeMetrics: + - schemaUrl: https://test-scope-schema.com/schema + scope: + name: MyTestInstrument + version: "1.2.3" + attributes: + - key: scope_key + value: + stringValue: foo + metrics: + - name: cumulative.monotonic.sum + sum: + aggregationTemporality: 2 + isMonotonic: true + dataPoints: + - timeUnixNano: 80 + asDouble: 555 + attributes: + - key: aaa + value: + stringValue: bbb diff --git a/exporter/loadbalancingexporter/testdata/metrics/consume_metrics/single_endpoint/resource_service_name/output.yaml b/exporter/loadbalancingexporter/testdata/metrics/consume_metrics/single_endpoint/resource_service_name/output.yaml new file mode 100644 index 000000000000..7e422a478b12 --- /dev/null +++ b/exporter/loadbalancingexporter/testdata/metrics/consume_metrics/single_endpoint/resource_service_name/output.yaml @@ -0,0 +1,34 @@ +resourceMetrics: + - schemaUrl: https://test-res-schema.com/schema + resource: + attributes: + - key: service.name + value: + stringValue: serviceA + scopeMetrics: + - schemaUrl: https://test-scope-schema.com/schema + scope: + name: MyTestInstrument + version: "1.2.3" + attributes: + - key: scope_key + value: + stringValue: foo + metrics: + - name: cumulative.monotonic.sum + sum: + aggregationTemporality: 2 + isMonotonic: true + dataPoints: + - timeUnixNano: 50 + asDouble: 333 + attributes: + - key: aaa + value: + stringValue: bbb + - timeUnixNano: 80 + asDouble: 555 + attributes: + - key: aaa + value: + stringValue: bbb diff --git a/exporter/loadbalancingexporter/testdata/metrics/consume_metrics/triple_endpoint/metric_name/input.yaml b/exporter/loadbalancingexporter/testdata/metrics/consume_metrics/triple_endpoint/metric_name/input.yaml new file mode 100644 index 000000000000..481b8d7ab629 --- /dev/null +++ b/exporter/loadbalancingexporter/testdata/metrics/consume_metrics/triple_endpoint/metric_name/input.yaml @@ -0,0 +1,99 @@ +resourceMetrics: + - schemaUrl: https://test-res-schema.com/schema + resource: + attributes: + - key: resource_key + value: + stringValue: foo + scopeMetrics: + - schemaUrl: https://test-scope-schema.com/schema + scope: + name: MyTestInstrument + version: "1.2.3" + attributes: + - key: scope_key + value: + stringValue: foo + metrics: + - name: first.monotonic.sum + sum: + aggregationTemporality: 2 + isMonotonic: true + dataPoints: + - timeUnixNano: 50 + asDouble: 333 + attributes: + - key: aaa + value: + stringValue: bbb + - name: second.monotonic.sum + sum: + aggregationTemporality: 2 + isMonotonic: true + dataPoints: + - timeUnixNano: 50 + asDouble: 945 + attributes: + - key: aaa + value: + stringValue: bbb + - name: third.monotonic.sum + sum: + aggregationTemporality: 2 + isMonotonic: true + dataPoints: + - timeUnixNano: 50 + asDouble: 945 + attributes: + - key: aaa + value: + stringValue: bbb + - schemaUrl: https://test-res-schema.com/schema + resource: + attributes: + - key: resource_key + value: + stringValue: bar + scopeMetrics: + - schemaUrl: https://test-scope-schema.com/schema + scope: + name: MyTestInstrument + version: "1.2.3" + attributes: + - key: scope_key + value: + stringValue: foo + metrics: + - name: first.monotonic.sum + sum: + aggregationTemporality: 2 + isMonotonic: true + dataPoints: + - timeUnixNano: 80 + asDouble: 444 + attributes: + - key: aaa + value: + stringValue: bbb + - name: second.monotonic.sum + sum: + aggregationTemporality: 2 + isMonotonic: true + dataPoints: + - timeUnixNano: 80 + asDouble: 1000 + attributes: + - key: aaa + value: + stringValue: bbb + - name: third.monotonic.sum + sum: + aggregationTemporality: 2 + isMonotonic: true + dataPoints: + - timeUnixNano: 80 + asDouble: 1000 + attributes: + - key: aaa + value: + stringValue: bbb diff --git a/exporter/loadbalancingexporter/testdata/metrics/consume_metrics/triple_endpoint/metric_name/output.yaml b/exporter/loadbalancingexporter/testdata/metrics/consume_metrics/triple_endpoint/metric_name/output.yaml new file mode 100644 index 000000000000..30dad4b27d7c --- /dev/null +++ b/exporter/loadbalancingexporter/testdata/metrics/consume_metrics/triple_endpoint/metric_name/output.yaml @@ -0,0 +1,168 @@ +endpoint-1: + resourceMetrics: + - schemaUrl: https://test-res-schema.com/schema + resource: + attributes: + - key: resource_key + value: + stringValue: foo + scopeMetrics: + - schemaUrl: https://test-scope-schema.com/schema + scope: + name: MyTestInstrument + version: "1.2.3" + attributes: + - key: scope_key + value: + stringValue: foo + metrics: + - name: third.monotonic.sum + sum: + aggregationTemporality: 2 + isMonotonic: true + dataPoints: + - timeUnixNano: 50 + asDouble: 945 + attributes: + - key: aaa + value: + stringValue: bbb + - schemaUrl: https://test-res-schema.com/schema + resource: + attributes: + - key: resource_key + value: + stringValue: bar + scopeMetrics: + - schemaUrl: https://test-scope-schema.com/schema + scope: + name: MyTestInstrument + version: "1.2.3" + attributes: + - key: scope_key + value: + stringValue: foo + metrics: + - name: third.monotonic.sum + sum: + aggregationTemporality: 2 + isMonotonic: true + dataPoints: + - timeUnixNano: 80 + asDouble: 1000 + attributes: + - key: aaa + value: + stringValue: bbb +endpoint-2: + resourceMetrics: + - schemaUrl: https://test-res-schema.com/schema + resource: + attributes: + - key: resource_key + value: + stringValue: foo + scopeMetrics: + - schemaUrl: https://test-scope-schema.com/schema + scope: + name: MyTestInstrument + version: "1.2.3" + attributes: + - key: scope_key + value: + stringValue: foo + metrics: + - name: first.monotonic.sum + sum: + aggregationTemporality: 2 + isMonotonic: true + dataPoints: + - timeUnixNano: 50 + asDouble: 333 + attributes: + - key: aaa + value: + stringValue: bbb + - schemaUrl: https://test-res-schema.com/schema + resource: + attributes: + - key: resource_key + value: + stringValue: bar + scopeMetrics: + - schemaUrl: https://test-scope-schema.com/schema + scope: + name: MyTestInstrument + version: "1.2.3" + attributes: + - key: scope_key + value: + stringValue: foo + metrics: + - name: first.monotonic.sum + sum: + aggregationTemporality: 2 + isMonotonic: true + dataPoints: + - timeUnixNano: 80 + asDouble: 444 + attributes: + - key: aaa + value: + stringValue: bbb +endpoint-3: + resourceMetrics: + - schemaUrl: https://test-res-schema.com/schema + resource: + attributes: + - key: resource_key + value: + stringValue: foo + scopeMetrics: + - schemaUrl: https://test-scope-schema.com/schema + scope: + name: MyTestInstrument + version: "1.2.3" + attributes: + - key: scope_key + value: + stringValue: foo + metrics: + - name: second.monotonic.sum + sum: + aggregationTemporality: 2 + isMonotonic: true + dataPoints: + - timeUnixNano: 50 + asDouble: 945 + attributes: + - key: aaa + value: + stringValue: bbb + - schemaUrl: https://test-res-schema.com/schema + resource: + attributes: + - key: resource_key + value: + stringValue: bar + scopeMetrics: + - schemaUrl: https://test-scope-schema.com/schema + scope: + name: MyTestInstrument + version: "1.2.3" + attributes: + - key: scope_key + value: + stringValue: foo + metrics: + - name: second.monotonic.sum + sum: + aggregationTemporality: 2 + isMonotonic: true + dataPoints: + - timeUnixNano: 80 + asDouble: 1000 + attributes: + - key: aaa + value: + stringValue: bbb diff --git a/exporter/loadbalancingexporter/testdata/metrics/consume_metrics/triple_endpoint/resource_id/input.yaml b/exporter/loadbalancingexporter/testdata/metrics/consume_metrics/triple_endpoint/resource_id/input.yaml new file mode 100644 index 000000000000..c680eb718fc7 --- /dev/null +++ b/exporter/loadbalancingexporter/testdata/metrics/consume_metrics/triple_endpoint/resource_id/input.yaml @@ -0,0 +1,82 @@ +resourceMetrics: + - schemaUrl: https://test-res-schema.com/schema + resource: + attributes: + - key: resource_key + value: + stringValue: foo + scopeMetrics: + - schemaUrl: https://test-scope-schema.com/schema + scope: + name: MyTestInstrument + version: "1.2.3" + attributes: + - key: scope_key + value: + stringValue: foo + metrics: + - name: cumulative.monotonic.sum + sum: + aggregationTemporality: 2 + isMonotonic: true + dataPoints: + - timeUnixNano: 50 + asDouble: 333 + attributes: + - key: aaa + value: + stringValue: bbb + - schemaUrl: https://test-res-schema.com/schema + resource: + attributes: + - key: resource_key + value: + stringValue: bar + scopeMetrics: + - schemaUrl: https://test-scope-schema.com/schema + scope: + name: MyTestInstrument + version: "1.2.3" + attributes: + - key: scope_key + value: + stringValue: foo + metrics: + - name: cumulative.monotonic.sum + sum: + aggregationTemporality: 2 + isMonotonic: true + dataPoints: + - timeUnixNano: 80 + asDouble: 555 + attributes: + - key: aaa + value: + stringValue: bbb + - schemaUrl: https://test-res-schema.com/schema + resource: + attributes: + - key: resource_key + value: + stringValue: asdf + scopeMetrics: + - schemaUrl: https://test-scope-schema.com/schema + scope: + name: MyTestInstrument + version: "1.2.3" + attributes: + - key: scope_key + value: + stringValue: foo + metrics: + - name: cumulative.monotonic.sum + sum: + aggregationTemporality: 2 + isMonotonic: true + dataPoints: + - timeUnixNano: 90 + asDouble: 666 + attributes: + - key: aaa + value: + stringValue: bbb diff --git a/exporter/loadbalancingexporter/testdata/metrics/consume_metrics/triple_endpoint/resource_id/output.yaml b/exporter/loadbalancingexporter/testdata/metrics/consume_metrics/triple_endpoint/resource_id/output.yaml new file mode 100644 index 000000000000..27fdc9c8c65c --- /dev/null +++ b/exporter/loadbalancingexporter/testdata/metrics/consume_metrics/triple_endpoint/resource_id/output.yaml @@ -0,0 +1,87 @@ +endpoint-1: + resourceMetrics: + - schemaUrl: https://test-res-schema.com/schema + resource: + attributes: + - key: resource_key + value: + stringValue: asdf + scopeMetrics: + - schemaUrl: https://test-scope-schema.com/schema + scope: + name: MyTestInstrument + version: "1.2.3" + attributes: + - key: scope_key + value: + stringValue: foo + metrics: + - name: cumulative.monotonic.sum + sum: + aggregationTemporality: 2 + isMonotonic: true + dataPoints: + - timeUnixNano: 90 + asDouble: 666 + attributes: + - key: aaa + value: + stringValue: bbb +endpoint-2: + resourceMetrics: + - schemaUrl: https://test-res-schema.com/schema + resource: + attributes: + - key: resource_key + value: + stringValue: bar + scopeMetrics: + - schemaUrl: https://test-scope-schema.com/schema + scope: + name: MyTestInstrument + version: "1.2.3" + attributes: + - key: scope_key + value: + stringValue: foo + metrics: + - name: cumulative.monotonic.sum + sum: + aggregationTemporality: 2 + isMonotonic: true + dataPoints: + - timeUnixNano: 80 + asDouble: 555 + attributes: + - key: aaa + value: + stringValue: bbb +endpoint-3: + resourceMetrics: + - schemaUrl: https://test-res-schema.com/schema + resource: + attributes: + - key: resource_key + value: + stringValue: foo + scopeMetrics: + - schemaUrl: https://test-scope-schema.com/schema + scope: + name: MyTestInstrument + version: "1.2.3" + attributes: + - key: scope_key + value: + stringValue: foo + metrics: + - name: cumulative.monotonic.sum + sum: + aggregationTemporality: 2 + isMonotonic: true + dataPoints: + - timeUnixNano: 50 + asDouble: 333 + attributes: + - key: aaa + value: + stringValue: bbb diff --git a/exporter/loadbalancingexporter/testdata/metrics/consume_metrics/triple_endpoint/resource_service_name/input.yaml b/exporter/loadbalancingexporter/testdata/metrics/consume_metrics/triple_endpoint/resource_service_name/input.yaml new file mode 100644 index 000000000000..e740ad01eefc --- /dev/null +++ b/exporter/loadbalancingexporter/testdata/metrics/consume_metrics/triple_endpoint/resource_service_name/input.yaml @@ -0,0 +1,82 @@ +resourceMetrics: + - schemaUrl: https://test-res-schema.com/schema + resource: + attributes: + - key: service.name + value: + stringValue: serviceA + scopeMetrics: + - schemaUrl: https://test-scope-schema.com/schema + scope: + name: MyTestInstrument + version: "1.2.3" + attributes: + - key: scope_key + value: + stringValue: foo + metrics: + - name: cumulative.monotonic.sum + sum: + aggregationTemporality: 2 + isMonotonic: true + dataPoints: + - timeUnixNano: 50 + asDouble: 333 + attributes: + - key: aaa + value: + stringValue: bbb + - schemaUrl: https://test-res-schema.com/schema + resource: + attributes: + - key: service.name + value: + stringValue: serviceB + scopeMetrics: + - schemaUrl: https://test-scope-schema.com/schema + scope: + name: MyTestInstrument + version: "1.2.3" + attributes: + - key: scope_key + value: + stringValue: foo + metrics: + - name: cumulative.monotonic.sum + sum: + aggregationTemporality: 2 + isMonotonic: true + dataPoints: + - timeUnixNano: 80 + asDouble: 555 + attributes: + - key: aaa + value: + stringValue: bbb + - schemaUrl: https://test-res-schema.com/schema + resource: + attributes: + - key: service.name + value: + stringValue: serviceC + scopeMetrics: + - schemaUrl: https://test-scope-schema.com/schema + scope: + name: MyTestInstrument + version: "1.2.3" + attributes: + - key: scope_key + value: + stringValue: foo + metrics: + - name: cumulative.monotonic.sum + sum: + aggregationTemporality: 2 + isMonotonic: true + dataPoints: + - timeUnixNano: 90 + asDouble: 666 + attributes: + - key: aaa + value: + stringValue: bbb diff --git a/exporter/loadbalancingexporter/testdata/metrics/consume_metrics/triple_endpoint/resource_service_name/output.yaml b/exporter/loadbalancingexporter/testdata/metrics/consume_metrics/triple_endpoint/resource_service_name/output.yaml new file mode 100644 index 000000000000..848796eb1cad --- /dev/null +++ b/exporter/loadbalancingexporter/testdata/metrics/consume_metrics/triple_endpoint/resource_service_name/output.yaml @@ -0,0 +1,87 @@ +endpoint-1: + resourceMetrics: + - schemaUrl: https://test-res-schema.com/schema + resource: + attributes: + - key: service.name + value: + stringValue: serviceA + scopeMetrics: + - schemaUrl: https://test-scope-schema.com/schema + scope: + name: MyTestInstrument + version: "1.2.3" + attributes: + - key: scope_key + value: + stringValue: foo + metrics: + - name: cumulative.monotonic.sum + sum: + aggregationTemporality: 2 + isMonotonic: true + dataPoints: + - timeUnixNano: 50 + asDouble: 333 + attributes: + - key: aaa + value: + stringValue: bbb + - schemaUrl: https://test-res-schema.com/schema + resource: + attributes: + - key: service.name + value: + stringValue: serviceB + scopeMetrics: + - schemaUrl: https://test-scope-schema.com/schema + scope: + name: MyTestInstrument + version: "1.2.3" + attributes: + - key: scope_key + value: + stringValue: foo + metrics: + - name: cumulative.monotonic.sum + sum: + aggregationTemporality: 2 + isMonotonic: true + dataPoints: + - timeUnixNano: 80 + asDouble: 555 + attributes: + - key: aaa + value: + stringValue: bbb +endpoint-2: + resourceMetrics: [] +endpoint-3: + resourceMetrics: + - schemaUrl: https://test-res-schema.com/schema + resource: + attributes: + - key: service.name + value: + stringValue: serviceC + scopeMetrics: + - schemaUrl: https://test-scope-schema.com/schema + scope: + name: MyTestInstrument + version: "1.2.3" + attributes: + - key: scope_key + value: + stringValue: foo + metrics: + - name: cumulative.monotonic.sum + sum: + aggregationTemporality: 2 + isMonotonic: true + dataPoints: + - timeUnixNano: 90 + asDouble: 666 + attributes: + - key: aaa + value: + stringValue: bbb diff --git a/exporter/loadbalancingexporter/testdata/metrics/split_metrics/basic_metric_name/input.yaml b/exporter/loadbalancingexporter/testdata/metrics/split_metrics/basic_metric_name/input.yaml new file mode 100644 index 000000000000..23a11f0fa726 --- /dev/null +++ b/exporter/loadbalancingexporter/testdata/metrics/split_metrics/basic_metric_name/input.yaml @@ -0,0 +1,77 @@ +resourceMetrics: + - schemaUrl: https://test-res-schema.com/schema + resource: + attributes: + - key: resource_key + value: + stringValue: foo + scopeMetrics: + - schemaUrl: https://test-scope-schema.com/schema + scope: + name: MyTestInstrument + version: "1.2.3" + attributes: + - key: scope_key + value: + stringValue: foo + metrics: + - name: first.monotonic.sum + sum: + aggregationTemporality: 2 + isMonotonic: true + dataPoints: + - timeUnixNano: 50 + asDouble: 333 + attributes: + - key: aaa + value: + stringValue: bbb + - name: second.monotonic.sum + sum: + aggregationTemporality: 2 + isMonotonic: true + dataPoints: + - timeUnixNano: 50 + asDouble: 945 + attributes: + - key: aaa + value: + stringValue: bbb + - schemaUrl: https://test-res-schema.com/schema + resource: + attributes: + - key: resource_key + value: + stringValue: bar + scopeMetrics: + - schemaUrl: https://test-scope-schema.com/schema + scope: + name: MyTestInstrument + version: "1.2.3" + attributes: + - key: scope_key + value: + stringValue: foo + metrics: + - name: first.monotonic.sum + sum: + aggregationTemporality: 2 + isMonotonic: true + dataPoints: + - timeUnixNano: 50 + asDouble: 333 + attributes: + - key: aaa + value: + stringValue: bbb + - name: second.monotonic.sum + sum: + aggregationTemporality: 2 + isMonotonic: true + dataPoints: + - timeUnixNano: 50 + asDouble: 945 + attributes: + - key: aaa + value: + stringValue: bbb diff --git a/exporter/loadbalancingexporter/testdata/metrics/split_metrics/basic_metric_name/output.yaml b/exporter/loadbalancingexporter/testdata/metrics/split_metrics/basic_metric_name/output.yaml new file mode 100644 index 000000000000..59f0ad62b030 --- /dev/null +++ b/exporter/loadbalancingexporter/testdata/metrics/split_metrics/basic_metric_name/output.yaml @@ -0,0 +1,112 @@ +first.monotonic.sum: + resourceMetrics: + - schemaUrl: https://test-res-schema.com/schema + resource: + attributes: + - key: resource_key + value: + stringValue: foo + scopeMetrics: + - schemaUrl: https://test-scope-schema.com/schema + scope: + name: MyTestInstrument + version: "1.2.3" + attributes: + - key: scope_key + value: + stringValue: foo + metrics: + - name: first.monotonic.sum + sum: + aggregationTemporality: 2 + isMonotonic: true + dataPoints: + - timeUnixNano: 50 + asDouble: 333 + attributes: + - key: aaa + value: + stringValue: bbb + - schemaUrl: https://test-res-schema.com/schema + resource: + attributes: + - key: resource_key + value: + stringValue: bar + scopeMetrics: + - schemaUrl: https://test-scope-schema.com/schema + scope: + name: MyTestInstrument + version: "1.2.3" + attributes: + - key: scope_key + value: + stringValue: foo + metrics: + - name: first.monotonic.sum + sum: + aggregationTemporality: 2 + isMonotonic: true + dataPoints: + - timeUnixNano: 50 + asDouble: 333 + attributes: + - key: aaa + value: + stringValue: bbb +second.monotonic.sum: + resourceMetrics: + - schemaUrl: https://test-res-schema.com/schema + resource: + attributes: + - key: resource_key + value: + stringValue: foo + scopeMetrics: + - schemaUrl: https://test-scope-schema.com/schema + scope: + name: MyTestInstrument + version: "1.2.3" + attributes: + - key: scope_key + value: + stringValue: foo + metrics: + - name: second.monotonic.sum + sum: + aggregationTemporality: 2 + isMonotonic: true + dataPoints: + - timeUnixNano: 50 + asDouble: 945 + attributes: + - key: aaa + value: + stringValue: bbb + - schemaUrl: https://test-res-schema.com/schema + resource: + attributes: + - key: resource_key + value: + stringValue: bar + scopeMetrics: + - schemaUrl: https://test-scope-schema.com/schema + scope: + name: MyTestInstrument + version: "1.2.3" + attributes: + - key: scope_key + value: + stringValue: foo + metrics: + - name: second.monotonic.sum + sum: + aggregationTemporality: 2 + isMonotonic: true + dataPoints: + - timeUnixNano: 50 + asDouble: 945 + attributes: + - key: aaa + value: + stringValue: bbb diff --git a/exporter/loadbalancingexporter/testdata/metrics/split_metrics/basic_resource_id/input.yaml b/exporter/loadbalancingexporter/testdata/metrics/split_metrics/basic_resource_id/input.yaml new file mode 100644 index 000000000000..ac105660c678 --- /dev/null +++ b/exporter/loadbalancingexporter/testdata/metrics/split_metrics/basic_resource_id/input.yaml @@ -0,0 +1,55 @@ +resourceMetrics: + - schemaUrl: https://test-res-schema.com/schema + resource: + attributes: + - key: resource_key + value: + stringValue: foo + scopeMetrics: + - schemaUrl: https://test-scope-schema.com/schema + scope: + name: MyTestInstrument + version: "1.2.3" + attributes: + - key: scope_key + value: + stringValue: foo + metrics: + - name: cumulative.monotonic.sum + sum: + aggregationTemporality: 2 + isMonotonic: true + dataPoints: + - timeUnixNano: 50 + asDouble: 333 + attributes: + - key: aaa + value: + stringValue: bbb + - schemaUrl: https://test-res-schema.com/schema + resource: + attributes: + - key: resource_key + value: + stringValue: bar + scopeMetrics: + - schemaUrl: https://test-scope-schema.com/schema + scope: + name: MyTestInstrument + version: "1.2.3" + attributes: + - key: scope_key + value: + stringValue: foo + metrics: + - name: cumulative.monotonic.sum + sum: + aggregationTemporality: 2 + isMonotonic: true + dataPoints: + - timeUnixNano: 50 + asDouble: 333 + attributes: + - key: aaa + value: + stringValue: bbb diff --git a/exporter/loadbalancingexporter/testdata/metrics/split_metrics/basic_resource_id/output.yaml b/exporter/loadbalancingexporter/testdata/metrics/split_metrics/basic_resource_id/output.yaml new file mode 100644 index 000000000000..8c065a1805f0 --- /dev/null +++ b/exporter/loadbalancingexporter/testdata/metrics/split_metrics/basic_resource_id/output.yaml @@ -0,0 +1,58 @@ +resource/99d9e3f8e25dd8f6: + resourceMetrics: + - schemaUrl: https://test-res-schema.com/schema + resource: + attributes: + - key: resource_key + value: + stringValue: foo + scopeMetrics: + - schemaUrl: https://test-scope-schema.com/schema + scope: + name: MyTestInstrument + version: "1.2.3" + attributes: + - key: scope_key + value: + stringValue: foo + metrics: + - name: cumulative.monotonic.sum + sum: + aggregationTemporality: 2 + isMonotonic: true + dataPoints: + - timeUnixNano: 50 + asDouble: 333 + attributes: + - key: aaa + value: + stringValue: bbb +resource/561be85f9d0f9beb: + resourceMetrics: + - schemaUrl: https://test-res-schema.com/schema + resource: + attributes: + - key: resource_key + value: + stringValue: bar + scopeMetrics: + - schemaUrl: https://test-scope-schema.com/schema + scope: + name: MyTestInstrument + version: "1.2.3" + attributes: + - key: scope_key + value: + stringValue: foo + metrics: + - name: cumulative.monotonic.sum + sum: + aggregationTemporality: 2 + isMonotonic: true + dataPoints: + - timeUnixNano: 50 + asDouble: 333 + attributes: + - key: aaa + value: + stringValue: bbb diff --git a/exporter/loadbalancingexporter/testdata/metrics/split_metrics/basic_resource_service_name/input.yaml b/exporter/loadbalancingexporter/testdata/metrics/split_metrics/basic_resource_service_name/input.yaml new file mode 100644 index 000000000000..ea96dce95939 --- /dev/null +++ b/exporter/loadbalancingexporter/testdata/metrics/split_metrics/basic_resource_service_name/input.yaml @@ -0,0 +1,55 @@ +resourceMetrics: + - schemaUrl: https://test-res-schema.com/schema + resource: + attributes: + - key: service.name + value: + stringValue: serviceA + scopeMetrics: + - schemaUrl: https://test-scope-schema.com/schema + scope: + name: MyTestInstrument + version: "1.2.3" + attributes: + - key: scope_key + value: + stringValue: foo + metrics: + - name: cumulative.monotonic.sum + sum: + aggregationTemporality: 2 + isMonotonic: true + dataPoints: + - timeUnixNano: 50 + asDouble: 333 + attributes: + - key: aaa + value: + stringValue: bbb + - schemaUrl: https://test-res-schema.com/schema + resource: + attributes: + - key: service.name + value: + stringValue: serviceB + scopeMetrics: + - schemaUrl: https://test-scope-schema.com/schema + scope: + name: MyTestInstrument + version: "1.2.3" + attributes: + - key: scope_key + value: + stringValue: foo + metrics: + - name: cumulative.monotonic.sum + sum: + aggregationTemporality: 2 + isMonotonic: true + dataPoints: + - timeUnixNano: 80 + asDouble: 555 + attributes: + - key: aaa + value: + stringValue: bbb diff --git a/exporter/loadbalancingexporter/testdata/metrics/split_metrics/basic_resource_service_name/output.yaml b/exporter/loadbalancingexporter/testdata/metrics/split_metrics/basic_resource_service_name/output.yaml new file mode 100644 index 000000000000..d44a7d4086da --- /dev/null +++ b/exporter/loadbalancingexporter/testdata/metrics/split_metrics/basic_resource_service_name/output.yaml @@ -0,0 +1,58 @@ +serviceA: + resourceMetrics: + - schemaUrl: https://test-res-schema.com/schema + resource: + attributes: + - key: service.name + value: + stringValue: serviceA + scopeMetrics: + - schemaUrl: https://test-scope-schema.com/schema + scope: + name: MyTestInstrument + version: "1.2.3" + attributes: + - key: scope_key + value: + stringValue: foo + metrics: + - name: cumulative.monotonic.sum + sum: + aggregationTemporality: 2 + isMonotonic: true + dataPoints: + - timeUnixNano: 50 + asDouble: 333 + attributes: + - key: aaa + value: + stringValue: bbb +serviceB: + resourceMetrics: + - schemaUrl: https://test-res-schema.com/schema + resource: + attributes: + - key: service.name + value: + stringValue: serviceB + scopeMetrics: + - schemaUrl: https://test-scope-schema.com/schema + scope: + name: MyTestInstrument + version: "1.2.3" + attributes: + - key: scope_key + value: + stringValue: foo + metrics: + - name: cumulative.monotonic.sum + sum: + aggregationTemporality: 2 + isMonotonic: true + dataPoints: + - timeUnixNano: 80 + asDouble: 555 + attributes: + - key: aaa + value: + stringValue: bbb diff --git a/exporter/loadbalancingexporter/testdata/metrics/split_metrics/duplicate_metric_name/input.yaml b/exporter/loadbalancingexporter/testdata/metrics/split_metrics/duplicate_metric_name/input.yaml new file mode 100644 index 000000000000..fc56857343a3 --- /dev/null +++ b/exporter/loadbalancingexporter/testdata/metrics/split_metrics/duplicate_metric_name/input.yaml @@ -0,0 +1,77 @@ +resourceMetrics: + - schemaUrl: https://test-res-schema.com/schema + resource: + attributes: + - key: resource_key + value: + stringValue: foo + scopeMetrics: + - schemaUrl: https://test-scope-schema.com/schema + scope: + name: MyTestInstrument + version: "1.2.3" + attributes: + - key: scope_key + value: + stringValue: foo + metrics: + - name: cumulative.monotonic.sum + sum: + aggregationTemporality: 2 + isMonotonic: true + dataPoints: + - timeUnixNano: 50 + asDouble: 333 + attributes: + - key: aaa + value: + stringValue: bbb + - name: cumulative.monotonic.sum + sum: + aggregationTemporality: 2 + isMonotonic: true + dataPoints: + - timeUnixNano: 80 + asDouble: 444 + attributes: + - key: aaa + value: + stringValue: bbb + - schemaUrl: https://test-res-schema.com/schema + resource: + attributes: + - key: resource_key + value: + stringValue: foo + scopeMetrics: + - schemaUrl: https://test-scope-schema.com/schema + scope: + name: MyTestInstrument + version: "1.2.3" + attributes: + - key: scope_key + value: + stringValue: foo + metrics: + - name: cumulative.monotonic.sum + sum: + aggregationTemporality: 2 + isMonotonic: true + dataPoints: + - timeUnixNano: 120 + asDouble: 555 + attributes: + - key: aaa + value: + stringValue: bbb + - name: cumulative.monotonic.sum + sum: + aggregationTemporality: 2 + isMonotonic: true + dataPoints: + - timeUnixNano: 150 + asDouble: 945 + attributes: + - key: aaa + value: + stringValue: bbb diff --git a/exporter/loadbalancingexporter/testdata/metrics/split_metrics/duplicate_metric_name/output.yaml b/exporter/loadbalancingexporter/testdata/metrics/split_metrics/duplicate_metric_name/output.yaml new file mode 100644 index 000000000000..95cf91293989 --- /dev/null +++ b/exporter/loadbalancingexporter/testdata/metrics/split_metrics/duplicate_metric_name/output.yaml @@ -0,0 +1,47 @@ +cumulative.monotonic.sum: + resourceMetrics: + - schemaUrl: https://test-res-schema.com/schema + resource: + attributes: + - key: resource_key + value: + stringValue: foo + scopeMetrics: + - schemaUrl: https://test-scope-schema.com/schema + scope: + name: MyTestInstrument + version: "1.2.3" + attributes: + - key: scope_key + value: + stringValue: foo + metrics: + - name: cumulative.monotonic.sum + sum: + aggregationTemporality: 2 + isMonotonic: true + dataPoints: + - timeUnixNano: 50 + asDouble: 333 + attributes: + - key: aaa + value: + stringValue: bbb + - timeUnixNano: 80 + asDouble: 444 + attributes: + - key: aaa + value: + stringValue: bbb + - timeUnixNano: 120 + asDouble: 555 + attributes: + - key: aaa + value: + stringValue: bbb + - timeUnixNano: 150 + asDouble: 945 + attributes: + - key: aaa + value: + stringValue: bbb diff --git a/exporter/loadbalancingexporter/testdata/metrics/split_metrics/duplicate_resource_id/input.yaml b/exporter/loadbalancingexporter/testdata/metrics/split_metrics/duplicate_resource_id/input.yaml new file mode 100644 index 000000000000..7d8e24c2cfb8 --- /dev/null +++ b/exporter/loadbalancingexporter/testdata/metrics/split_metrics/duplicate_resource_id/input.yaml @@ -0,0 +1,55 @@ +resourceMetrics: + - schemaUrl: https://test-res-schema.com/schema + resource: + attributes: + - key: resource_key + value: + stringValue: foo + scopeMetrics: + - schemaUrl: https://test-scope-schema.com/schema + scope: + name: MyTestInstrument + version: "1.2.3" + attributes: + - key: scope_key + value: + stringValue: foo + metrics: + - name: cumulative.monotonic.sum + sum: + aggregationTemporality: 2 + isMonotonic: true + dataPoints: + - timeUnixNano: 50 + asDouble: 333 + attributes: + - key: aaa + value: + stringValue: bbb + - schemaUrl: https://test-res-schema.com/schema + resource: + attributes: + - key: resource_key + value: + stringValue: foo + scopeMetrics: + - schemaUrl: https://test-scope-schema.com/schema + scope: + name: MyTestInstrument + version: "1.2.3" + attributes: + - key: scope_key + value: + stringValue: foo + metrics: + - name: cumulative.monotonic.sum + sum: + aggregationTemporality: 2 + isMonotonic: true + dataPoints: + - timeUnixNano: 80 + asDouble: 555 + attributes: + - key: aaa + value: + stringValue: bbb diff --git a/exporter/loadbalancingexporter/testdata/metrics/split_metrics/duplicate_resource_id/output.yaml b/exporter/loadbalancingexporter/testdata/metrics/split_metrics/duplicate_resource_id/output.yaml new file mode 100644 index 000000000000..a7ffc97394e7 --- /dev/null +++ b/exporter/loadbalancingexporter/testdata/metrics/split_metrics/duplicate_resource_id/output.yaml @@ -0,0 +1,35 @@ +resource/99d9e3f8e25dd8f6: + resourceMetrics: + - schemaUrl: https://test-res-schema.com/schema + resource: + attributes: + - key: resource_key + value: + stringValue: foo + scopeMetrics: + - schemaUrl: https://test-scope-schema.com/schema + scope: + name: MyTestInstrument + version: "1.2.3" + attributes: + - key: scope_key + value: + stringValue: foo + metrics: + - name: cumulative.monotonic.sum + sum: + aggregationTemporality: 2 + isMonotonic: true + dataPoints: + - timeUnixNano: 50 + asDouble: 333 + attributes: + - key: aaa + value: + stringValue: bbb + - timeUnixNano: 80 + asDouble: 555 + attributes: + - key: aaa + value: + stringValue: bbb diff --git a/exporter/loadbalancingexporter/testdata/metrics/split_metrics/duplicate_resource_service_name/input.yaml b/exporter/loadbalancingexporter/testdata/metrics/split_metrics/duplicate_resource_service_name/input.yaml new file mode 100644 index 000000000000..1a0a17714a3a --- /dev/null +++ b/exporter/loadbalancingexporter/testdata/metrics/split_metrics/duplicate_resource_service_name/input.yaml @@ -0,0 +1,55 @@ +resourceMetrics: + - schemaUrl: https://test-res-schema.com/schema + resource: + attributes: + - key: service.name + value: + stringValue: serviceA + scopeMetrics: + - schemaUrl: https://test-scope-schema.com/schema + scope: + name: MyTestInstrument + version: "1.2.3" + attributes: + - key: scope_key + value: + stringValue: foo + metrics: + - name: cumulative.monotonic.sum + sum: + aggregationTemporality: 2 + isMonotonic: true + dataPoints: + - timeUnixNano: 50 + asDouble: 333 + attributes: + - key: aaa + value: + stringValue: bbb + - schemaUrl: https://test-res-schema.com/schema + resource: + attributes: + - key: service.name + value: + stringValue: serviceA + scopeMetrics: + - schemaUrl: https://test-scope-schema.com/schema + scope: + name: MyTestInstrument + version: "1.2.3" + attributes: + - key: scope_key + value: + stringValue: foo + metrics: + - name: cumulative.monotonic.sum + sum: + aggregationTemporality: 2 + isMonotonic: true + dataPoints: + - timeUnixNano: 80 + asDouble: 555 + attributes: + - key: aaa + value: + stringValue: bbb diff --git a/exporter/loadbalancingexporter/testdata/metrics/split_metrics/duplicate_resource_service_name/output.yaml b/exporter/loadbalancingexporter/testdata/metrics/split_metrics/duplicate_resource_service_name/output.yaml new file mode 100644 index 000000000000..0ad35224eb9a --- /dev/null +++ b/exporter/loadbalancingexporter/testdata/metrics/split_metrics/duplicate_resource_service_name/output.yaml @@ -0,0 +1,35 @@ +serviceA: + resourceMetrics: + - schemaUrl: https://test-res-schema.com/schema + resource: + attributes: + - key: service.name + value: + stringValue: serviceA + scopeMetrics: + - schemaUrl: https://test-scope-schema.com/schema + scope: + name: MyTestInstrument + version: "1.2.3" + attributes: + - key: scope_key + value: + stringValue: foo + metrics: + - name: cumulative.monotonic.sum + sum: + aggregationTemporality: 2 + isMonotonic: true + dataPoints: + - timeUnixNano: 50 + asDouble: 333 + attributes: + - key: aaa + value: + stringValue: bbb + - timeUnixNano: 80 + asDouble: 555 + attributes: + - key: aaa + value: + stringValue: bbb diff --git a/exporter/loadbalancingexporter/testdata/metrics/split_metrics/missing_service_name/input.yaml b/exporter/loadbalancingexporter/testdata/metrics/split_metrics/missing_service_name/input.yaml new file mode 100644 index 000000000000..c2063568128d --- /dev/null +++ b/exporter/loadbalancingexporter/testdata/metrics/split_metrics/missing_service_name/input.yaml @@ -0,0 +1,28 @@ +resourceMetrics: + - schemaUrl: https://test-res-schema.com/schema + resource: + attributes: + - key: resource_label + value: + stringValue: foo + scopeMetrics: + - schemaUrl: https://test-scope-schema.com/schema + scope: + name: MyTestInstrument + version: "1.2.3" + attributes: + - key: scope_key + value: + stringValue: foo + metrics: + - name: cumulative.monotonic.sum + sum: + aggregationTemporality: 2 + isMonotonic: true + dataPoints: + - timeUnixNano: 50 + asDouble: 333 + attributes: + - key: aaa + value: + stringValue: bbb diff --git a/exporter/loadbalancingexporter/trace_exporter.go b/exporter/loadbalancingexporter/trace_exporter.go index 204773eb17d7..2d441dbdad0b 100644 --- a/exporter/loadbalancingexporter/trace_exporter.go +++ b/exporter/loadbalancingexporter/trace_exporter.go @@ -49,9 +49,9 @@ func newTracesExporter(params exporter.Settings, cfg component.Config) (*traceEx traceExporter := traceExporterImp{loadBalancer: lb, routingKey: traceIDRouting} switch cfg.(*Config).RoutingKey { - case "service": + case svcRoutingStr: traceExporter.routingKey = svcRouting - case "traceID", "": + case traceIDRoutingStr, "": default: return nil, fmt.Errorf("unsupported routing_key: %s", cfg.(*Config).RoutingKey) } diff --git a/go.mod b/go.mod index 963410ef8d81..c15d9d80513a 100644 --- a/go.mod +++ b/go.mod @@ -573,6 +573,7 @@ require ( github.com/open-telemetry/opentelemetry-collector-contrib/internal/common v0.103.0 // indirect github.com/open-telemetry/opentelemetry-collector-contrib/internal/coreinternal v0.103.0 // indirect github.com/open-telemetry/opentelemetry-collector-contrib/internal/docker v0.103.0 // indirect + github.com/open-telemetry/opentelemetry-collector-contrib/internal/exp/metrics v0.103.0 // indirect github.com/open-telemetry/opentelemetry-collector-contrib/internal/filter v0.103.0 // indirect github.com/open-telemetry/opentelemetry-collector-contrib/internal/k8sconfig v0.103.0 // indirect github.com/open-telemetry/opentelemetry-collector-contrib/internal/kafka v0.103.0 // indirect @@ -1239,4 +1240,6 @@ replace github.com/open-telemetry/opentelemetry-collector-contrib/receiver/splun replace github.com/open-telemetry/opentelemetry-collector-contrib/pkg/sampling => ./pkg/sampling +replace github.com/open-telemetry/opentelemetry-collector-contrib/internal/exp/metrics => ./internal/exp/metrics + replace github.com/open-telemetry/opentelemetry-collector-contrib/internal/pdatautil => ./internal/pdatautil