Support a single Resource per MeterProvider in the SDK (#2120)
* Remove resource from export.Record * fix bridge/opencensus * partial fixes in exporters/otlp/otlpmetric * Use go-cmp to simplify exporter_test * OTLP http/grpc * prometheus * fix stdout (pending PR 2015) * oc bridge cleanups * Lint and changelog * pr num * cleanup * revert * fix multiple * always merge the environment * lint * precommit * qualify package names in changelog
This commit is contained in:
		
							parent
							
								
									a8bb0bf89f
								
							
						
					
					
						commit
						4e8d667f6e
					
				|  | @ -17,6 +17,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm | |||
| ### Changed | ||||
| 
 | ||||
| - Metric SDK/API implementation type `InstrumentKind` moves into `sdkapi` sub-package. (#2091) | ||||
| - The Metrics SDK export record no longer contains a Resource pointer, the SDK `"go.opentelemetry.io/otel/sdk/trace/export/metric".Exporter.Export()` function for push-based exporters now takes a single Resource argument, pull-based exporters use `"go.opentelemetry.io/otel/sdk/metric/controller/basic".Controller.Resource()`. (#2120) | ||||
| 
 | ||||
| ### Deprecated | ||||
| 
 | ||||
|  | @ -35,6 +36,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm | |||
| - The `fromEnv` detector no longer throws an error when `OTEL_RESOURCE_ATTRIBUTES` environment variable is not set or empty. (#2138) | ||||
| - Setting the global `ErrorHandler` with `"go.opentelemetry.io/otel".SetErrorHandler` multiple times is now supported. (#2160, #2140) | ||||
| - The `"go.opentelemetry.io/otel/attribute".Any` function now supports `int32` values. (#2169) | ||||
| - Multiple calls to `"go.opentelemetry.io/otel/sdk/metric/controller/basic".WithResource()` are handled correctly, and when no resources are provided `"go.opentelemetry.io/otel/sdk/resource".Default()` is used. (#2120) | ||||
| 
 | ||||
| ### Security | ||||
| 
 | ||||
|  |  | |||
|  | @ -51,7 +51,11 @@ type exporter struct { | |||
| 
 | ||||
| // ExportMetrics implements the OpenCensus metric Exporter interface
 | ||||
| func (e *exporter) ExportMetrics(ctx context.Context, metrics []*metricdata.Metric) error { | ||||
| 	return e.base.Export(ctx, &checkpointSet{metrics: metrics}) | ||||
| 	res := resource.Empty() | ||||
| 	if len(metrics) != 0 { | ||||
| 		res = convertResource(metrics[0].Resource) | ||||
| 	} | ||||
| 	return e.base.Export(ctx, res, &checkpointSet{metrics: metrics}) | ||||
| } | ||||
| 
 | ||||
| type checkpointSet struct { | ||||
|  | @ -69,7 +73,6 @@ func (d *checkpointSet) ForEach(exporter export.ExportKindSelector, f func(expor | |||
| 			otel.Handle(err) | ||||
| 			continue | ||||
| 		} | ||||
| 		res := convertResource(m.Resource) | ||||
| 		for _, ts := range m.TimeSeries { | ||||
| 			if len(ts.Points) == 0 { | ||||
| 				continue | ||||
|  | @ -87,7 +90,6 @@ func (d *checkpointSet) ForEach(exporter export.ExportKindSelector, f func(expor | |||
| 			if err := f(export.NewRecord( | ||||
| 				&descriptor, | ||||
| 				&ls, | ||||
| 				res, | ||||
| 				agg, | ||||
| 				ts.StartTime, | ||||
| 				agg.end(), | ||||
|  | @ -119,6 +121,7 @@ func convertLabels(keys []metricdata.LabelKey, values []metricdata.LabelValue) ( | |||
| } | ||||
| 
 | ||||
| // convertResource converts an OpenCensus Resource to an OpenTelemetry Resource
 | ||||
| // Note: the ocresource.Resource Type field is not used.
 | ||||
| func convertResource(res *ocresource.Resource) *resource.Resource { | ||||
| 	labels := []attribute.KeyValue{} | ||||
| 	if res == nil { | ||||
|  |  | |||
|  | @ -39,12 +39,14 @@ import ( | |||
| 
 | ||||
| type fakeExporter struct { | ||||
| 	export.Exporter | ||||
| 	records []export.Record | ||||
| 	err     error | ||||
| 	records  []export.Record | ||||
| 	resource *resource.Resource | ||||
| 	err      error | ||||
| } | ||||
| 
 | ||||
| func (f *fakeExporter) Export(ctx context.Context, cps exportmetric.CheckpointSet) error { | ||||
| func (f *fakeExporter) Export(ctx context.Context, res *resource.Resource, cps exportmetric.CheckpointSet) error { | ||||
| 	return cps.ForEach(f, func(record exportmetric.Record) error { | ||||
| 		f.resource = res | ||||
| 		f.records = append(f.records, record) | ||||
| 		return f.err | ||||
| 	}) | ||||
|  | @ -82,6 +84,7 @@ func TestExportMetrics(t *testing.T) { | |||
| 		input                []*metricdata.Metric | ||||
| 		exportErr            error | ||||
| 		expected             []export.Record | ||||
| 		expectedResource     *resource.Resource | ||||
| 		expectedHandledError error | ||||
| 	}{ | ||||
| 		{ | ||||
|  | @ -142,6 +145,12 @@ func TestExportMetrics(t *testing.T) { | |||
| 			desc: "success", | ||||
| 			input: []*metricdata.Metric{ | ||||
| 				{ | ||||
| 					Resource: &ocresource.Resource{ | ||||
| 						Labels: map[string]string{ | ||||
| 							"R1": "V1", | ||||
| 							"R2": "V2", | ||||
| 						}, | ||||
| 					}, | ||||
| 					TimeSeries: []*metricdata.TimeSeries{ | ||||
| 						{ | ||||
| 							StartTime: now, | ||||
|  | @ -152,11 +161,14 @@ func TestExportMetrics(t *testing.T) { | |||
| 					}, | ||||
| 				}, | ||||
| 			}, | ||||
| 			expectedResource: resource.NewSchemaless( | ||||
| 				attribute.String("R1", "V1"), | ||||
| 				attribute.String("R2", "V2"), | ||||
| 			), | ||||
| 			expected: []export.Record{ | ||||
| 				export.NewRecord( | ||||
| 					&basicDesc, | ||||
| 					attribute.EmptySet(), | ||||
| 					resource.NewSchemaless(), | ||||
| 					&ocExactAggregator{ | ||||
| 						points: []aggregation.Point{ | ||||
| 							{ | ||||
|  | @ -188,7 +200,6 @@ func TestExportMetrics(t *testing.T) { | |||
| 				export.NewRecord( | ||||
| 					&basicDesc, | ||||
| 					attribute.EmptySet(), | ||||
| 					resource.NewSchemaless(), | ||||
| 					&ocExactAggregator{ | ||||
| 						points: []aggregation.Point{ | ||||
| 							{ | ||||
|  | @ -223,7 +234,6 @@ func TestExportMetrics(t *testing.T) { | |||
| 				export.NewRecord( | ||||
| 					&basicDesc, | ||||
| 					attribute.EmptySet(), | ||||
| 					resource.NewSchemaless(), | ||||
| 					&ocExactAggregator{ | ||||
| 						points: []aggregation.Point{ | ||||
| 							{ | ||||
|  | @ -255,6 +265,9 @@ func TestExportMetrics(t *testing.T) { | |||
| 			if len(tc.expected) != len(output) { | ||||
| 				t.Fatalf("ExportMetrics(%+v) = %d records, want %d records", tc.input, len(output), len(tc.expected)) | ||||
| 			} | ||||
| 			if fakeExporter.resource.String() != tc.expectedResource.String() { | ||||
| 				t.Errorf("ExportMetrics(%+v)[i].Resource() = %+v, want %+v", tc.input, fakeExporter.resource.String(), tc.expectedResource.String()) | ||||
| 			} | ||||
| 			for i, expected := range tc.expected { | ||||
| 				if output[i].StartTime() != expected.StartTime() { | ||||
| 					t.Errorf("ExportMetrics(%+v)[i].StartTime() = %+v, want %+v", tc.input, output[i].StartTime(), expected.StartTime()) | ||||
|  | @ -262,9 +275,6 @@ func TestExportMetrics(t *testing.T) { | |||
| 				if output[i].EndTime() != expected.EndTime() { | ||||
| 					t.Errorf("ExportMetrics(%+v)[i].EndTime() = %+v, want %+v", tc.input, output[i].EndTime(), expected.EndTime()) | ||||
| 				} | ||||
| 				if output[i].Resource().String() != expected.Resource().String() { | ||||
| 					t.Errorf("ExportMetrics(%+v)[i].Resource() = %+v, want %+v", tc.input, output[i].Resource().String(), expected.Resource().String()) | ||||
| 				} | ||||
| 				if output[i].Descriptor().Name() != expected.Descriptor().Name() { | ||||
| 					t.Errorf("ExportMetrics(%+v)[i].Descriptor() = %+v, want %+v", tc.input, output[i].Descriptor().Name(), expected.Descriptor().Name()) | ||||
| 				} | ||||
|  |  | |||
|  | @ -23,6 +23,7 @@ import ( | |||
| 	"go.opentelemetry.io/otel/metric" | ||||
| 	metricsdk "go.opentelemetry.io/otel/sdk/export/metric" | ||||
| 	"go.opentelemetry.io/otel/sdk/export/metric/aggregation" | ||||
| 	"go.opentelemetry.io/otel/sdk/resource" | ||||
| ) | ||||
| 
 | ||||
| var ( | ||||
|  | @ -42,8 +43,8 @@ type Exporter struct { | |||
| } | ||||
| 
 | ||||
| // Export exports a batch of metrics.
 | ||||
| func (e *Exporter) Export(ctx context.Context, checkpointSet metricsdk.CheckpointSet) error { | ||||
| 	rms, err := metrictransform.CheckpointSet(ctx, e, checkpointSet, 1) | ||||
| func (e *Exporter) Export(ctx context.Context, res *resource.Resource, checkpointSet metricsdk.CheckpointSet) error { | ||||
| 	rms, err := metrictransform.CheckpointSet(ctx, e, res, checkpointSet, 1) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  |  | |||
|  | @ -16,27 +16,29 @@ package otlpmetric_test | |||
| 
 | ||||
| import ( | ||||
| 	"context" | ||||
| 	"fmt" | ||||
| 	"sync" | ||||
| 	"testing" | ||||
| 	"time" | ||||
| 
 | ||||
| 	"go.opentelemetry.io/otel/exporters/otlp/otlpmetric" | ||||
| 	"go.opentelemetry.io/otel/sdk/export/metric/aggregation" | ||||
| 	"go.opentelemetry.io/otel/sdk/metric/aggregator/histogram" | ||||
| 	"go.opentelemetry.io/otel/sdk/metric/aggregator/sum" | ||||
| 
 | ||||
| 	"github.com/google/go-cmp/cmp" | ||||
| 	"github.com/stretchr/testify/assert" | ||||
| 	"github.com/stretchr/testify/require" | ||||
| 	"google.golang.org/protobuf/testing/protocmp" | ||||
| 
 | ||||
| 	"go.opentelemetry.io/otel/attribute" | ||||
| 	"go.opentelemetry.io/otel/exporters/otlp/otlpmetric" | ||||
| 	"go.opentelemetry.io/otel/exporters/otlp/otlpmetric/internal/metrictransform" | ||||
| 	"go.opentelemetry.io/otel/metric" | ||||
| 	"go.opentelemetry.io/otel/metric/number" | ||||
| 	"go.opentelemetry.io/otel/metric/sdkapi" | ||||
| 	metricsdk "go.opentelemetry.io/otel/sdk/export/metric" | ||||
| 	"go.opentelemetry.io/otel/sdk/export/metric/aggregation" | ||||
| 	"go.opentelemetry.io/otel/sdk/metric/aggregator/histogram" | ||||
| 	"go.opentelemetry.io/otel/sdk/metric/aggregator/sum" | ||||
| 	"go.opentelemetry.io/otel/sdk/resource" | ||||
| 	commonpb "go.opentelemetry.io/proto/otlp/common/v1" | ||||
| 	metricpb "go.opentelemetry.io/proto/otlp/metrics/v1" | ||||
| 	resourcepb "go.opentelemetry.io/proto/otlp/resource/v1" | ||||
| ) | ||||
| 
 | ||||
| var ( | ||||
|  | @ -98,21 +100,17 @@ func (m *checkpointSet) ForEach(_ metricsdk.ExportKindSelector, fn func(metricsd | |||
| } | ||||
| 
 | ||||
| type record struct { | ||||
| 	name     string | ||||
| 	iKind    sdkapi.InstrumentKind | ||||
| 	nKind    number.Kind | ||||
| 	resource *resource.Resource | ||||
| 	opts     []metric.InstrumentOption | ||||
| 	labels   []attribute.KeyValue | ||||
| 	name   string | ||||
| 	iKind  sdkapi.InstrumentKind | ||||
| 	nKind  number.Kind | ||||
| 	opts   []metric.InstrumentOption | ||||
| 	labels []attribute.KeyValue | ||||
| } | ||||
| 
 | ||||
| var ( | ||||
| 	baseKeyValues = []attribute.KeyValue{attribute.String("host", "test.com")} | ||||
| 	cpuKey        = attribute.Key("CPU") | ||||
| 
 | ||||
| 	testInstA = resource.NewSchemaless(attribute.String("instance", "tester-a")) | ||||
| 	testInstB = resource.NewSchemaless(attribute.String("instance", "tester-b")) | ||||
| 
 | ||||
| 	testHistogramBoundaries = []float64{2.0, 4.0, 8.0} | ||||
| 
 | ||||
| 	cpu1Labels = []*commonpb.KeyValue{ | ||||
|  | @ -152,43 +150,21 @@ var ( | |||
| 		}, | ||||
| 	} | ||||
| 
 | ||||
| 	testerAResource = &resourcepb.Resource{ | ||||
| 		Attributes: []*commonpb.KeyValue{ | ||||
| 			{ | ||||
| 				Key: "instance", | ||||
| 				Value: &commonpb.AnyValue{ | ||||
| 					Value: &commonpb.AnyValue_StringValue{ | ||||
| 						StringValue: "tester-a", | ||||
| 					}, | ||||
| 				}, | ||||
| 			}, | ||||
| 		}, | ||||
| 	} | ||||
| 	testerBResource = &resourcepb.Resource{ | ||||
| 		Attributes: []*commonpb.KeyValue{ | ||||
| 			{ | ||||
| 				Key: "instance", | ||||
| 				Value: &commonpb.AnyValue{ | ||||
| 					Value: &commonpb.AnyValue_StringValue{ | ||||
| 						StringValue: "tester-b", | ||||
| 					}, | ||||
| 				}, | ||||
| 			}, | ||||
| 		}, | ||||
| 	} | ||||
| 	testerAResource   = resource.NewSchemaless(attribute.String("instance", "tester-a")) | ||||
| 	testerAResourcePb = metrictransform.Resource(testerAResource) | ||||
| ) | ||||
| 
 | ||||
| func TestNoGroupingExport(t *testing.T) { | ||||
| 	runMetricExportTests( | ||||
| 		t, | ||||
| 		nil, | ||||
| 		nil, | ||||
| 		[]record{ | ||||
| 			{ | ||||
| 				"int64-count", | ||||
| 				sdkapi.CounterInstrumentKind, | ||||
| 				number.Int64Kind, | ||||
| 				nil, | ||||
| 				nil, | ||||
| 				append(baseKeyValues, cpuKey.Int(1)), | ||||
| 			}, | ||||
| 			{ | ||||
|  | @ -196,7 +172,6 @@ func TestNoGroupingExport(t *testing.T) { | |||
| 				sdkapi.CounterInstrumentKind, | ||||
| 				number.Int64Kind, | ||||
| 				nil, | ||||
| 				nil, | ||||
| 				append(baseKeyValues, cpuKey.Int(2)), | ||||
| 			}, | ||||
| 		}, | ||||
|  | @ -243,7 +218,6 @@ func TestValuerecorderMetricGroupingExport(t *testing.T) { | |||
| 		sdkapi.ValueRecorderInstrumentKind, | ||||
| 		number.Int64Kind, | ||||
| 		nil, | ||||
| 		nil, | ||||
| 		append(baseKeyValues, cpuKey.Int(1)), | ||||
| 	} | ||||
| 	expected := []*metricpb.ResourceMetrics{ | ||||
|  | @ -285,7 +259,7 @@ func TestValuerecorderMetricGroupingExport(t *testing.T) { | |||
| 			}, | ||||
| 		}, | ||||
| 	} | ||||
| 	runMetricExportTests(t, nil, []record{r, r}, expected) | ||||
| 	runMetricExportTests(t, nil, nil, []record{r, r}, expected) | ||||
| } | ||||
| 
 | ||||
| func TestCountInt64MetricGroupingExport(t *testing.T) { | ||||
|  | @ -294,12 +268,12 @@ func TestCountInt64MetricGroupingExport(t *testing.T) { | |||
| 		sdkapi.CounterInstrumentKind, | ||||
| 		number.Int64Kind, | ||||
| 		nil, | ||||
| 		nil, | ||||
| 		append(baseKeyValues, cpuKey.Int(1)), | ||||
| 	} | ||||
| 	runMetricExportTests( | ||||
| 		t, | ||||
| 		nil, | ||||
| 		nil, | ||||
| 		[]record{r, r}, | ||||
| 		[]*metricpb.ResourceMetrics{ | ||||
| 			{ | ||||
|  | @ -344,12 +318,12 @@ func TestCountFloat64MetricGroupingExport(t *testing.T) { | |||
| 		sdkapi.CounterInstrumentKind, | ||||
| 		number.Float64Kind, | ||||
| 		nil, | ||||
| 		nil, | ||||
| 		append(baseKeyValues, cpuKey.Int(1)), | ||||
| 	} | ||||
| 	runMetricExportTests( | ||||
| 		t, | ||||
| 		nil, | ||||
| 		nil, | ||||
| 		[]record{r, r}, | ||||
| 		[]*metricpb.ResourceMetrics{ | ||||
| 			{ | ||||
|  | @ -392,12 +366,12 @@ func TestResourceMetricGroupingExport(t *testing.T) { | |||
| 	runMetricExportTests( | ||||
| 		t, | ||||
| 		nil, | ||||
| 		testerAResource, | ||||
| 		[]record{ | ||||
| 			{ | ||||
| 				"int64-count", | ||||
| 				sdkapi.CounterInstrumentKind, | ||||
| 				number.Int64Kind, | ||||
| 				testInstA, | ||||
| 				nil, | ||||
| 				append(baseKeyValues, cpuKey.Int(1)), | ||||
| 			}, | ||||
|  | @ -405,7 +379,6 @@ func TestResourceMetricGroupingExport(t *testing.T) { | |||
| 				"int64-count", | ||||
| 				sdkapi.CounterInstrumentKind, | ||||
| 				number.Int64Kind, | ||||
| 				testInstA, | ||||
| 				nil, | ||||
| 				append(baseKeyValues, cpuKey.Int(1)), | ||||
| 			}, | ||||
|  | @ -413,7 +386,6 @@ func TestResourceMetricGroupingExport(t *testing.T) { | |||
| 				"int64-count", | ||||
| 				sdkapi.CounterInstrumentKind, | ||||
| 				number.Int64Kind, | ||||
| 				testInstA, | ||||
| 				nil, | ||||
| 				append(baseKeyValues, cpuKey.Int(2)), | ||||
| 			}, | ||||
|  | @ -421,14 +393,13 @@ func TestResourceMetricGroupingExport(t *testing.T) { | |||
| 				"int64-count", | ||||
| 				sdkapi.CounterInstrumentKind, | ||||
| 				number.Int64Kind, | ||||
| 				testInstB, | ||||
| 				nil, | ||||
| 				append(baseKeyValues, cpuKey.Int(1)), | ||||
| 			}, | ||||
| 		}, | ||||
| 		[]*metricpb.ResourceMetrics{ | ||||
| 			{ | ||||
| 				Resource: testerAResource, | ||||
| 				Resource: testerAResourcePb, | ||||
| 				InstrumentationLibraryMetrics: []*metricpb.InstrumentationLibraryMetrics{ | ||||
| 					{ | ||||
| 						Metrics: []*metricpb.Metric{ | ||||
|  | @ -457,26 +428,6 @@ func TestResourceMetricGroupingExport(t *testing.T) { | |||
| 												StartTimeUnixNano: startTime(), | ||||
| 												TimeUnixNano:      pointTime(), | ||||
| 											}, | ||||
| 										}, | ||||
| 									}, | ||||
| 								}, | ||||
| 							}, | ||||
| 						}, | ||||
| 					}, | ||||
| 				}, | ||||
| 			}, | ||||
| 			{ | ||||
| 				Resource: testerBResource, | ||||
| 				InstrumentationLibraryMetrics: []*metricpb.InstrumentationLibraryMetrics{ | ||||
| 					{ | ||||
| 						Metrics: []*metricpb.Metric{ | ||||
| 							{ | ||||
| 								Name: "int64-count", | ||||
| 								Data: &metricpb.Metric_Sum{ | ||||
| 									Sum: &metricpb.Sum{ | ||||
| 										IsMonotonic:            true, | ||||
| 										AggregationTemporality: metricpb.AggregationTemporality_AGGREGATION_TEMPORALITY_CUMULATIVE, | ||||
| 										DataPoints: []*metricpb.NumberDataPoint{ | ||||
| 											{ | ||||
| 												Value:             &metricpb.NumberDataPoint_AsInt{AsInt: 11}, | ||||
| 												Attributes:        cpu1Labels, | ||||
|  | @ -510,12 +461,12 @@ func TestResourceInstLibMetricGroupingExport(t *testing.T) { | |||
| 	runMetricExportTests( | ||||
| 		t, | ||||
| 		nil, | ||||
| 		testerAResource, | ||||
| 		[]record{ | ||||
| 			{ | ||||
| 				"int64-count", | ||||
| 				sdkapi.CounterInstrumentKind, | ||||
| 				number.Int64Kind, | ||||
| 				testInstA, | ||||
| 				countingLib1, | ||||
| 				append(baseKeyValues, cpuKey.Int(1)), | ||||
| 			}, | ||||
|  | @ -523,7 +474,6 @@ func TestResourceInstLibMetricGroupingExport(t *testing.T) { | |||
| 				"int64-count", | ||||
| 				sdkapi.CounterInstrumentKind, | ||||
| 				number.Int64Kind, | ||||
| 				testInstA, | ||||
| 				countingLib2, | ||||
| 				append(baseKeyValues, cpuKey.Int(1)), | ||||
| 			}, | ||||
|  | @ -531,7 +481,6 @@ func TestResourceInstLibMetricGroupingExport(t *testing.T) { | |||
| 				"int64-count", | ||||
| 				sdkapi.CounterInstrumentKind, | ||||
| 				number.Int64Kind, | ||||
| 				testInstA, | ||||
| 				countingLib1, | ||||
| 				append(baseKeyValues, cpuKey.Int(1)), | ||||
| 			}, | ||||
|  | @ -539,7 +488,6 @@ func TestResourceInstLibMetricGroupingExport(t *testing.T) { | |||
| 				"int64-count", | ||||
| 				sdkapi.CounterInstrumentKind, | ||||
| 				number.Int64Kind, | ||||
| 				testInstA, | ||||
| 				countingLib1, | ||||
| 				append(baseKeyValues, cpuKey.Int(2)), | ||||
| 			}, | ||||
|  | @ -547,22 +495,13 @@ func TestResourceInstLibMetricGroupingExport(t *testing.T) { | |||
| 				"int64-count", | ||||
| 				sdkapi.CounterInstrumentKind, | ||||
| 				number.Int64Kind, | ||||
| 				testInstA, | ||||
| 				summingLib, | ||||
| 				append(baseKeyValues, cpuKey.Int(1)), | ||||
| 			}, | ||||
| 			{ | ||||
| 				"int64-count", | ||||
| 				sdkapi.CounterInstrumentKind, | ||||
| 				number.Int64Kind, | ||||
| 				testInstB, | ||||
| 				countingLib1, | ||||
| 				append(baseKeyValues, cpuKey.Int(1)), | ||||
| 			}, | ||||
| 		}, | ||||
| 		[]*metricpb.ResourceMetrics{ | ||||
| 			{ | ||||
| 				Resource: testerAResource, | ||||
| 				Resource: testerAResourcePb, | ||||
| 				InstrumentationLibraryMetrics: []*metricpb.InstrumentationLibraryMetrics{ | ||||
| 					{ | ||||
| 						InstrumentationLibrary: &commonpb.InstrumentationLibrary{ | ||||
|  | @ -652,36 +591,6 @@ func TestResourceInstLibMetricGroupingExport(t *testing.T) { | |||
| 					}, | ||||
| 				}, | ||||
| 			}, | ||||
| 			{ | ||||
| 				Resource: testerBResource, | ||||
| 				InstrumentationLibraryMetrics: []*metricpb.InstrumentationLibraryMetrics{ | ||||
| 					{ | ||||
| 						InstrumentationLibrary: &commonpb.InstrumentationLibrary{ | ||||
| 							Name:    "counting-lib", | ||||
| 							Version: "v1", | ||||
| 						}, | ||||
| 						Metrics: []*metricpb.Metric{ | ||||
| 							{ | ||||
| 								Name: "int64-count", | ||||
| 								Data: &metricpb.Metric_Sum{ | ||||
| 									Sum: &metricpb.Sum{ | ||||
| 										IsMonotonic:            true, | ||||
| 										AggregationTemporality: metricpb.AggregationTemporality_AGGREGATION_TEMPORALITY_CUMULATIVE, | ||||
| 										DataPoints: []*metricpb.NumberDataPoint{ | ||||
| 											{ | ||||
| 												Value:             &metricpb.NumberDataPoint_AsInt{AsInt: 11}, | ||||
| 												Attributes:        cpu1Labels, | ||||
| 												StartTimeUnixNano: startTime(), | ||||
| 												TimeUnixNano:      pointTime(), | ||||
| 											}, | ||||
| 										}, | ||||
| 									}, | ||||
| 								}, | ||||
| 							}, | ||||
| 						}, | ||||
| 					}, | ||||
| 				}, | ||||
| 			}, | ||||
| 		}, | ||||
| 	) | ||||
| } | ||||
|  | @ -708,19 +617,19 @@ func TestStatelessExportKind(t *testing.T) { | |||
| 						metricsdk.StatelessExportKindSelector(), | ||||
| 					), | ||||
| 				}, | ||||
| 				testerAResource, | ||||
| 				[]record{ | ||||
| 					{ | ||||
| 						"instrument", | ||||
| 						k.instrumentKind, | ||||
| 						number.Int64Kind, | ||||
| 						testInstA, | ||||
| 						nil, | ||||
| 						append(baseKeyValues, cpuKey.Int(1)), | ||||
| 					}, | ||||
| 				}, | ||||
| 				[]*metricpb.ResourceMetrics{ | ||||
| 					{ | ||||
| 						Resource: testerAResource, | ||||
| 						Resource: testerAResourcePb, | ||||
| 						InstrumentationLibraryMetrics: []*metricpb.InstrumentationLibraryMetrics{ | ||||
| 							{ | ||||
| 								Metrics: []*metricpb.Metric{ | ||||
|  | @ -751,12 +660,11 @@ func TestStatelessExportKind(t *testing.T) { | |||
| 	} | ||||
| } | ||||
| 
 | ||||
| func runMetricExportTests(t *testing.T, opts []otlpmetric.Option, rs []record, expected []*metricpb.ResourceMetrics) { | ||||
| func runMetricExportTests(t *testing.T, opts []otlpmetric.Option, res *resource.Resource, records []record, expected []*metricpb.ResourceMetrics) { | ||||
| 	exp, driver := newExporter(t, opts...) | ||||
| 
 | ||||
| 	recs := map[attribute.Distinct][]metricsdk.Record{} | ||||
| 	resources := map[attribute.Distinct]*resource.Resource{} | ||||
| 	for _, r := range rs { | ||||
| 	recs := []metricsdk.Record{} | ||||
| 	for _, r := range records { | ||||
| 		lcopy := make([]attribute.KeyValue, len(r.labels)) | ||||
| 		copy(lcopy, r.labels) | ||||
| 		desc := metric.NewDescriptor(r.name, r.iKind, r.nKind, r.opts...) | ||||
|  | @ -797,83 +705,47 @@ func runMetricExportTests(t *testing.T, opts []otlpmetric.Option, rs []record, e | |||
| 		} | ||||
| 		require.NoError(t, agg.SynchronizedMove(ckpt, &desc)) | ||||
| 
 | ||||
| 		equiv := r.resource.Equivalent() | ||||
| 		resources[equiv] = r.resource | ||||
| 		recs[equiv] = append(recs[equiv], metricsdk.NewRecord(&desc, &labs, r.resource, ckpt.Aggregation(), intervalStart, intervalEnd)) | ||||
| 	} | ||||
| 	for _, records := range recs { | ||||
| 		assert.NoError(t, exp.Export(context.Background(), &checkpointSet{records: records})) | ||||
| 		recs = append(recs, metricsdk.NewRecord(&desc, &labs, ckpt.Aggregation(), intervalStart, intervalEnd)) | ||||
| 	} | ||||
| 	assert.NoError(t, exp.Export(context.Background(), res, &checkpointSet{records: recs})) | ||||
| 
 | ||||
| 	// assert.ElementsMatch does not equate nested slices of different order,
 | ||||
| 	// therefore this requires the top level slice to be broken down.
 | ||||
| 	// Build a map of Resource/InstrumentationLibrary pairs to Metrics, from
 | ||||
| 	// that validate the metric elements match for all expected pairs. Finally,
 | ||||
| 	// make we saw all expected pairs.
 | ||||
| 	type key struct { | ||||
| 		resource, instrumentationLibrary string | ||||
| 	keyFor := func(ilm *metricpb.InstrumentationLibraryMetrics) string { | ||||
| 		return fmt.Sprintf("%s/%s", ilm.GetInstrumentationLibrary().GetName(), ilm.GetInstrumentationLibrary().GetVersion()) | ||||
| 	} | ||||
| 	got := map[key][]*metricpb.Metric{} | ||||
| 	got := map[string][]*metricpb.Metric{} | ||||
| 	for _, rm := range driver.rm { | ||||
| 		for _, ilm := range rm.InstrumentationLibraryMetrics { | ||||
| 			k := key{ | ||||
| 				resource:               rm.GetResource().String(), | ||||
| 				instrumentationLibrary: ilm.GetInstrumentationLibrary().String(), | ||||
| 			} | ||||
| 			got[k] = ilm.GetMetrics() | ||||
| 			k := keyFor(ilm) | ||||
| 			got[k] = append(got[k], ilm.GetMetrics()...) | ||||
| 		} | ||||
| 	} | ||||
| 	seen := map[key]struct{}{} | ||||
| 
 | ||||
| 	seen := map[string]struct{}{} | ||||
| 	for _, rm := range expected { | ||||
| 		for _, ilm := range rm.InstrumentationLibraryMetrics { | ||||
| 			k := key{ | ||||
| 				resource:               rm.GetResource().String(), | ||||
| 				instrumentationLibrary: ilm.GetInstrumentationLibrary().String(), | ||||
| 			} | ||||
| 			k := keyFor(ilm) | ||||
| 			seen[k] = struct{}{} | ||||
| 			g, ok := got[k] | ||||
| 			if !ok { | ||||
| 				t.Errorf("missing metrics for:\n\tResource: %s\n\tInstrumentationLibrary: %s\n", k.resource, k.instrumentationLibrary) | ||||
| 				t.Errorf("missing metrics for:\n\tInstrumentationLibrary: %q\n", k) | ||||
| 				continue | ||||
| 			} | ||||
| 			if !assert.Len(t, g, len(ilm.GetMetrics())) { | ||||
| 				continue | ||||
| 			} | ||||
| 			for i, expected := range ilm.GetMetrics() { | ||||
| 				assert.Equal(t, expected.Name, g[i].Name) | ||||
| 				assert.Equal(t, expected.Unit, g[i].Unit) | ||||
| 				assert.Equal(t, expected.Description, g[i].Description) | ||||
| 				switch g[i].Data.(type) { | ||||
| 				case *metricpb.Metric_Gauge: | ||||
| 					assert.ElementsMatch(t, expected.GetGauge().GetDataPoints(), g[i].GetGauge().GetDataPoints()) | ||||
| 				case *metricpb.Metric_Sum: | ||||
| 					assert.Equal(t, | ||||
| 						expected.GetSum().GetAggregationTemporality(), | ||||
| 						g[i].GetSum().GetAggregationTemporality(), | ||||
| 					) | ||||
| 					assert.Equal(t, | ||||
| 						expected.GetSum().GetIsMonotonic(), | ||||
| 						g[i].GetSum().GetIsMonotonic(), | ||||
| 					) | ||||
| 					assert.ElementsMatch(t, expected.GetSum().GetDataPoints(), g[i].GetSum().GetDataPoints()) | ||||
| 				case *metricpb.Metric_Histogram: | ||||
| 					assert.Equal( | ||||
| 						t, | ||||
| 						expected.GetHistogram().GetAggregationTemporality(), | ||||
| 						g[i].GetHistogram().GetAggregationTemporality(), | ||||
| 					) | ||||
| 					assert.ElementsMatch(t, expected.GetHistogram().GetDataPoints(), g[i].GetHistogram().GetDataPoints()) | ||||
| 				case *metricpb.Metric_Summary: | ||||
| 					assert.ElementsMatch(t, expected.GetSummary().GetDataPoints(), g[i].GetSummary().GetDataPoints()) | ||||
| 				default: | ||||
| 					assert.Failf(t, "unknown data type", g[i].Name) | ||||
| 				} | ||||
| 				assert.Equal(t, "", cmp.Diff(expected, g[i], protocmp.Transform())) | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	for k := range got { | ||||
| 		if _, ok := seen[k]; !ok { | ||||
| 			t.Errorf("did not expect metrics for:\n\tResource: %s\n\tInstrumentationLibrary: %s\n", k.resource, k.instrumentationLibrary) | ||||
| 			t.Errorf("did not expect metrics for:\n\tInstrumentationLibrary: %s\n", k) | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | @ -895,7 +767,7 @@ func TestEmptyMetricExport(t *testing.T) { | |||
| 		}, | ||||
| 	} { | ||||
| 		driver.Reset() | ||||
| 		require.NoError(t, exp.Export(context.Background(), &checkpointSet{records: test.records})) | ||||
| 		require.NoError(t, exp.Export(context.Background(), resource.Empty(), &checkpointSet{records: test.records})) | ||||
| 		assert.Equal(t, test.want, driver.rm) | ||||
| 	} | ||||
| } | ||||
|  |  | |||
|  | @ -4,6 +4,7 @@ go 1.15 | |||
| 
 | ||||
| require ( | ||||
| 	github.com/cenkalti/backoff/v4 v4.1.1 | ||||
| 	github.com/google/go-cmp v0.5.6 | ||||
| 	github.com/stretchr/testify v1.7.0 | ||||
| 	go.opentelemetry.io/otel v1.0.0-RC2 | ||||
| 	go.opentelemetry.io/otel/metric v0.22.0 | ||||
|  |  | |||
|  | @ -24,10 +24,8 @@ import ( | |||
| 	"sync" | ||||
| 	"time" | ||||
| 
 | ||||
| 	"go.opentelemetry.io/otel/attribute" | ||||
| 	commonpb "go.opentelemetry.io/proto/otlp/common/v1" | ||||
| 	metricpb "go.opentelemetry.io/proto/otlp/metrics/v1" | ||||
| 	resourcepb "go.opentelemetry.io/proto/otlp/resource/v1" | ||||
| 
 | ||||
| 	"go.opentelemetry.io/otel/metric/number" | ||||
| 	export "go.opentelemetry.io/otel/sdk/export/metric" | ||||
|  | @ -60,7 +58,6 @@ var ( | |||
| 
 | ||||
| // result is the product of transforming Records into OTLP Metrics.
 | ||||
| type result struct { | ||||
| 	Resource               *resource.Resource | ||||
| 	InstrumentationLibrary instrumentation.Library | ||||
| 	Metric                 *metricpb.Metric | ||||
| 	Err                    error | ||||
|  | @ -76,7 +73,7 @@ func toNanos(t time.Time) uint64 { | |||
| 
 | ||||
| // CheckpointSet transforms all records contained in a checkpoint into
 | ||||
| // batched OTLP ResourceMetrics.
 | ||||
| func CheckpointSet(ctx context.Context, exportSelector export.ExportKindSelector, cps export.CheckpointSet, numWorkers uint) ([]*metricpb.ResourceMetrics, error) { | ||||
| func CheckpointSet(ctx context.Context, exportSelector export.ExportKindSelector, res *resource.Resource, cps export.CheckpointSet, numWorkers uint) ([]*metricpb.ResourceMetrics, error) { | ||||
| 	records, errc := source(ctx, exportSelector, cps) | ||||
| 
 | ||||
| 	// Start a fixed number of goroutines to transform records.
 | ||||
|  | @ -95,7 +92,7 @@ func CheckpointSet(ctx context.Context, exportSelector export.ExportKindSelector | |||
| 	}() | ||||
| 
 | ||||
| 	// Synchronously collect the transformed records and transmit.
 | ||||
| 	rms, err := sink(ctx, transformed) | ||||
| 	rms, err := sink(ctx, res, transformed) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | @ -139,7 +136,6 @@ func transformer(ctx context.Context, exportSelector export.ExportKindSelector, | |||
| 			continue | ||||
| 		} | ||||
| 		res := result{ | ||||
| 			Resource: r.Resource(), | ||||
| 			InstrumentationLibrary: instrumentation.Library{ | ||||
| 				Name:    r.Descriptor().InstrumentationName(), | ||||
| 				Version: r.Descriptor().InstrumentationVersion(), | ||||
|  | @ -160,41 +156,21 @@ func transformer(ctx context.Context, exportSelector export.ExportKindSelector, | |||
| // Any errors encoutered transforming input will be reported with an
 | ||||
| // ErrTransforming as well as the completed ResourceMetrics. It is up to the
 | ||||
| // caller to handle any incorrect data in these ResourceMetrics.
 | ||||
| func sink(ctx context.Context, in <-chan result) ([]*metricpb.ResourceMetrics, error) { | ||||
| func sink(ctx context.Context, res *resource.Resource, in <-chan result) ([]*metricpb.ResourceMetrics, error) { | ||||
| 	var errStrings []string | ||||
| 
 | ||||
| 	type resourceBatch struct { | ||||
| 		Resource *resourcepb.Resource | ||||
| 		// Group by instrumentation library name and then the MetricDescriptor.
 | ||||
| 		InstrumentationLibraryBatches map[instrumentation.Library]map[string]*metricpb.Metric | ||||
| 		SchemaURL                     string | ||||
| 	} | ||||
| 
 | ||||
| 	// group by unique Resource string.
 | ||||
| 	grouped := make(map[attribute.Distinct]resourceBatch) | ||||
| 	// Group by instrumentation library name and then the MetricDescriptor.
 | ||||
| 	grouped := map[instrumentation.Library]map[string]*metricpb.Metric{} | ||||
| 	for res := range in { | ||||
| 		if res.Err != nil { | ||||
| 			errStrings = append(errStrings, res.Err.Error()) | ||||
| 			continue | ||||
| 		} | ||||
| 
 | ||||
| 		rID := res.Resource.Equivalent() | ||||
| 		rb, ok := grouped[rID] | ||||
| 		if !ok { | ||||
| 			rb = resourceBatch{ | ||||
| 				Resource:                      Resource(res.Resource), | ||||
| 				InstrumentationLibraryBatches: make(map[instrumentation.Library]map[string]*metricpb.Metric), | ||||
| 			} | ||||
| 			if res.Resource != nil { | ||||
| 				rb.SchemaURL = res.Resource.SchemaURL() | ||||
| 			} | ||||
| 			grouped[rID] = rb | ||||
| 		} | ||||
| 
 | ||||
| 		mb, ok := rb.InstrumentationLibraryBatches[res.InstrumentationLibrary] | ||||
| 		mb, ok := grouped[res.InstrumentationLibrary] | ||||
| 		if !ok { | ||||
| 			mb = make(map[string]*metricpb.Metric) | ||||
| 			rb.InstrumentationLibraryBatches[res.InstrumentationLibrary] = mb | ||||
| 			grouped[res.InstrumentationLibrary] = mb | ||||
| 		} | ||||
| 
 | ||||
| 		mID := res.Metric.GetName() | ||||
|  | @ -222,26 +198,26 @@ func sink(ctx context.Context, in <-chan result) ([]*metricpb.ResourceMetrics, e | |||
| 		return nil, nil | ||||
| 	} | ||||
| 
 | ||||
| 	var rms []*metricpb.ResourceMetrics | ||||
| 	for _, rb := range grouped { | ||||
| 		// TODO: populate ResourceMetrics.SchemaURL when the field is added to the Protobuf message.
 | ||||
| 		rm := &metricpb.ResourceMetrics{Resource: rb.Resource} | ||||
| 		for il, mb := range rb.InstrumentationLibraryBatches { | ||||
| 			ilm := &metricpb.InstrumentationLibraryMetrics{ | ||||
| 				Metrics: make([]*metricpb.Metric, 0, len(mb)), | ||||
| 			} | ||||
| 			if il != (instrumentation.Library{}) { | ||||
| 				ilm.InstrumentationLibrary = &commonpb.InstrumentationLibrary{ | ||||
| 					Name:    il.Name, | ||||
| 					Version: il.Version, | ||||
| 				} | ||||
| 			} | ||||
| 			for _, m := range mb { | ||||
| 				ilm.Metrics = append(ilm.Metrics, m) | ||||
| 			} | ||||
| 			rm.InstrumentationLibraryMetrics = append(rm.InstrumentationLibraryMetrics, ilm) | ||||
| 	rm := &metricpb.ResourceMetrics{ | ||||
| 		Resource: Resource(res), | ||||
| 	} | ||||
| 	if res != nil { | ||||
| 		rm.SchemaUrl = res.SchemaURL() | ||||
| 	} | ||||
| 
 | ||||
| 	rms := []*metricpb.ResourceMetrics{rm} | ||||
| 	for il, mb := range grouped { | ||||
| 		ilm := &metricpb.InstrumentationLibraryMetrics{ | ||||
| 			Metrics: make([]*metricpb.Metric, 0, len(mb)), | ||||
| 			InstrumentationLibrary: &commonpb.InstrumentationLibrary{ | ||||
| 				Name:    il.Name, | ||||
| 				Version: il.Version, | ||||
| 			}, | ||||
| 		} | ||||
| 		rms = append(rms, rm) | ||||
| 		for _, m := range mb { | ||||
| 			ilm.Metrics = append(ilm.Metrics, m) | ||||
| 		} | ||||
| 		rm.InstrumentationLibraryMetrics = append(rm.InstrumentationLibraryMetrics, ilm) | ||||
| 	} | ||||
| 
 | ||||
| 	// Report any transform errors.
 | ||||
|  |  | |||
|  | @ -36,7 +36,6 @@ import ( | |||
| 	"go.opentelemetry.io/otel/sdk/metric/aggregator/minmaxsumcount" | ||||
| 	"go.opentelemetry.io/otel/sdk/metric/aggregator/sum" | ||||
| 	sumAgg "go.opentelemetry.io/otel/sdk/metric/aggregator/sum" | ||||
| 	"go.opentelemetry.io/otel/sdk/resource" | ||||
| 	commonpb "go.opentelemetry.io/proto/otlp/common/v1" | ||||
| 	metricpb "go.opentelemetry.io/proto/otlp/metrics/v1" | ||||
| ) | ||||
|  | @ -155,7 +154,7 @@ func TestMinMaxSumCountDatapoints(t *testing.T) { | |||
| 			}, | ||||
| 		}, | ||||
| 	} | ||||
| 	record := export.NewRecord(&desc, &labels, nil, ckpt.Aggregation(), intervalStart, intervalEnd) | ||||
| 	record := export.NewRecord(&desc, &labels, ckpt.Aggregation(), intervalStart, intervalEnd) | ||||
| 	m, err := minMaxSumCount(record, ckpt) | ||||
| 	if assert.NoError(t, err) { | ||||
| 		assert.Nil(t, m.GetGauge()) | ||||
|  | @ -186,7 +185,7 @@ func TestSumIntDataPoints(t *testing.T) { | |||
| 
 | ||||
| 	assert.NoError(t, s.Update(context.Background(), number.Number(1), &desc)) | ||||
| 	require.NoError(t, s.SynchronizedMove(ckpt, &desc)) | ||||
| 	record := export.NewRecord(&desc, &labels, nil, ckpt.Aggregation(), intervalStart, intervalEnd) | ||||
| 	record := export.NewRecord(&desc, &labels, ckpt.Aggregation(), intervalStart, intervalEnd) | ||||
| 
 | ||||
| 	value, err := ckpt.Sum() | ||||
| 	require.NoError(t, err) | ||||
|  | @ -226,7 +225,7 @@ func TestSumFloatDataPoints(t *testing.T) { | |||
| 
 | ||||
| 	assert.NoError(t, s.Update(context.Background(), number.NewFloat64Number(1), &desc)) | ||||
| 	require.NoError(t, s.SynchronizedMove(ckpt, &desc)) | ||||
| 	record := export.NewRecord(&desc, &labels, nil, ckpt.Aggregation(), intervalStart, intervalEnd) | ||||
| 	record := export.NewRecord(&desc, &labels, ckpt.Aggregation(), intervalStart, intervalEnd) | ||||
| 	value, err := ckpt.Sum() | ||||
| 	require.NoError(t, err) | ||||
| 
 | ||||
|  | @ -264,7 +263,7 @@ func TestLastValueIntDataPoints(t *testing.T) { | |||
| 
 | ||||
| 	assert.NoError(t, lv.Update(context.Background(), number.Number(100), &desc)) | ||||
| 	require.NoError(t, lv.SynchronizedMove(ckpt, &desc)) | ||||
| 	record := export.NewRecord(&desc, &labels, nil, ckpt.Aggregation(), intervalStart, intervalEnd) | ||||
| 	record := export.NewRecord(&desc, &labels, ckpt.Aggregation(), intervalStart, intervalEnd) | ||||
| 	value, timestamp, err := ckpt.LastValue() | ||||
| 	require.NoError(t, err) | ||||
| 
 | ||||
|  | @ -299,7 +298,7 @@ func TestExactIntDataPoints(t *testing.T) { | |||
| 
 | ||||
| 	assert.NoError(t, e.Update(context.Background(), number.Number(100), &desc)) | ||||
| 	require.NoError(t, e.SynchronizedMove(ckpt, &desc)) | ||||
| 	record := export.NewRecord(&desc, &labels, nil, ckpt.Aggregation(), intervalStart, intervalEnd) | ||||
| 	record := export.NewRecord(&desc, &labels, ckpt.Aggregation(), intervalStart, intervalEnd) | ||||
| 	pts, err := ckpt.Points() | ||||
| 	require.NoError(t, err) | ||||
| 
 | ||||
|  | @ -333,7 +332,7 @@ func TestExactFloatDataPoints(t *testing.T) { | |||
| 	e, ckpt := &arrs[0], &arrs[1] | ||||
| 	assert.NoError(t, e.Update(context.Background(), number.NewFloat64Number(100), &desc)) | ||||
| 	require.NoError(t, e.SynchronizedMove(ckpt, &desc)) | ||||
| 	record := export.NewRecord(&desc, &labels, nil, ckpt.Aggregation(), intervalStart, intervalEnd) | ||||
| 	record := export.NewRecord(&desc, &labels, ckpt.Aggregation(), intervalStart, intervalEnd) | ||||
| 	pts, err := ckpt.Points() | ||||
| 	require.NoError(t, err) | ||||
| 
 | ||||
|  | @ -364,7 +363,7 @@ func TestSumErrUnknownValueType(t *testing.T) { | |||
| 	desc := metric.NewDescriptor("", sdkapi.ValueRecorderInstrumentKind, number.Kind(-1)) | ||||
| 	labels := attribute.NewSet() | ||||
| 	s := &sumAgg.New(1)[0] | ||||
| 	record := export.NewRecord(&desc, &labels, nil, s, intervalStart, intervalEnd) | ||||
| 	record := export.NewRecord(&desc, &labels, s, intervalStart, intervalEnd) | ||||
| 	value, err := s.Sum() | ||||
| 	require.NoError(t, err) | ||||
| 
 | ||||
|  | @ -448,12 +447,11 @@ func TestRecordAggregatorIncompatibleErrors(t *testing.T) { | |||
| 	makeMpb := func(kind aggregation.Kind, agg aggregation.Aggregation) (*metricpb.Metric, error) { | ||||
| 		desc := metric.NewDescriptor("things", sdkapi.CounterInstrumentKind, number.Int64Kind) | ||||
| 		labels := attribute.NewSet() | ||||
| 		res := resource.Empty() | ||||
| 		test := &testAgg{ | ||||
| 			kind: kind, | ||||
| 			agg:  agg, | ||||
| 		} | ||||
| 		return Record(export.CumulativeExportKindSelector(), export.NewRecord(&desc, &labels, res, test, intervalStart, intervalEnd)) | ||||
| 		return Record(export.CumulativeExportKindSelector(), export.NewRecord(&desc, &labels, test, intervalStart, intervalEnd)) | ||||
| 	} | ||||
| 
 | ||||
| 	mpb, err := makeMpb(aggregation.SumKind, &lastvalue.New(1)[0]) | ||||
|  | @ -485,8 +483,7 @@ func TestRecordAggregatorUnexpectedErrors(t *testing.T) { | |||
| 	makeMpb := func(kind aggregation.Kind, agg aggregation.Aggregation) (*metricpb.Metric, error) { | ||||
| 		desc := metric.NewDescriptor("things", sdkapi.CounterInstrumentKind, number.Int64Kind) | ||||
| 		labels := attribute.NewSet() | ||||
| 		res := resource.Empty() | ||||
| 		return Record(export.CumulativeExportKindSelector(), export.NewRecord(&desc, &labels, res, agg, intervalStart, intervalEnd)) | ||||
| 		return Record(export.CumulativeExportKindSelector(), export.NewRecord(&desc, &labels, agg, intervalStart, intervalEnd)) | ||||
| 	} | ||||
| 
 | ||||
| 	errEx := fmt.Errorf("timeout") | ||||
|  |  | |||
|  | @ -25,7 +25,6 @@ import ( | |||
| 	"go.opentelemetry.io/otel/metric/sdkapi" | ||||
| 	exportmetric "go.opentelemetry.io/otel/sdk/export/metric" | ||||
| 	"go.opentelemetry.io/otel/sdk/metric/aggregator/sum" | ||||
| 	"go.opentelemetry.io/otel/sdk/resource" | ||||
| ) | ||||
| 
 | ||||
| // Used to avoid implementing locking functions for test
 | ||||
|  | @ -63,7 +62,6 @@ func (OneRecordCheckpointSet) ForEach(kindSelector exportmetric.ExportKindSelect | |||
| 		sdkapi.CounterInstrumentKind, | ||||
| 		number.Int64Kind, | ||||
| 	) | ||||
| 	res := resource.NewSchemaless(attribute.String("a", "b")) | ||||
| 	agg := sum.New(1) | ||||
| 	if err := agg[0].Update(context.Background(), number.NewInt64Number(42), &desc); err != nil { | ||||
| 		return err | ||||
|  | @ -71,7 +69,7 @@ func (OneRecordCheckpointSet) ForEach(kindSelector exportmetric.ExportKindSelect | |||
| 	start := time.Date(2020, time.December, 8, 19, 15, 0, 0, time.UTC) | ||||
| 	end := time.Date(2020, time.December, 8, 19, 16, 0, 0, time.UTC) | ||||
| 	labels := attribute.NewSet(attribute.String("abc", "def"), attribute.Int64("one", 1)) | ||||
| 	rec := exportmetric.NewRecord(&desc, &labels, res, agg[0].Aggregation(), start, end) | ||||
| 	rec := exportmetric.NewRecord(&desc, &labels, agg[0].Aggregation(), start, end) | ||||
| 	return recordFunc(rec) | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -25,6 +25,7 @@ import ( | |||
| 	"go.opentelemetry.io/otel/exporters/otlp/otlpmetric" | ||||
| 	"go.opentelemetry.io/otel/exporters/otlp/otlpmetric/internal/otlpmetrictest" | ||||
| 	"go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc" | ||||
| 	"go.opentelemetry.io/otel/sdk/resource" | ||||
| 
 | ||||
| 	"google.golang.org/genproto/googleapis/rpc/errdetails" | ||||
| 	"google.golang.org/grpc/codes" | ||||
|  | @ -40,6 +41,8 @@ import ( | |||
| 
 | ||||
| var ( | ||||
| 	oneRecord = otlpmetrictest.OneRecordCheckpointSet{} | ||||
| 
 | ||||
| 	testResource = resource.Empty() | ||||
| ) | ||||
| 
 | ||||
| func TestNewExporter_endToEnd(t *testing.T) { | ||||
|  | @ -183,11 +186,11 @@ func TestNewExporter_collectorConnectionDiesThenReconnectsWhenInRestMode(t *test | |||
| 
 | ||||
| 	// first export, it will send disconnected message to the channel on export failure,
 | ||||
| 	// trigger almost immediate reconnection
 | ||||
| 	require.Error(t, exp.Export(ctx, oneRecord)) | ||||
| 	require.Error(t, exp.Export(ctx, testResource, oneRecord)) | ||||
| 
 | ||||
| 	// second export, it will detect connection issue, change state of exporter to disconnected and
 | ||||
| 	// send message to disconnected channel but this time reconnection gouroutine will be in (rest mode, not listening to the disconnected channel)
 | ||||
| 	require.Error(t, exp.Export(ctx, oneRecord)) | ||||
| 	require.Error(t, exp.Export(ctx, testResource, oneRecord)) | ||||
| 
 | ||||
| 	// as a result we have exporter in disconnected state waiting for disconnection message to reconnect
 | ||||
| 
 | ||||
|  | @ -202,7 +205,7 @@ func TestNewExporter_collectorConnectionDiesThenReconnectsWhenInRestMode(t *test | |||
| 	for i := 0; i < n; i++ { | ||||
| 		// when disconnected exp.Export doesnt send disconnected messages again
 | ||||
| 		// it just quits and return last connection error
 | ||||
| 		require.NoError(t, exp.Export(ctx, oneRecord)) | ||||
| 		require.NoError(t, exp.Export(ctx, testResource, oneRecord)) | ||||
| 	} | ||||
| 
 | ||||
| 	nmaMetrics := nmc.getMetrics() | ||||
|  | @ -231,7 +234,7 @@ func TestExporterExportFailureAndRecoveryModes(t *testing.T) { | |||
| 		{ | ||||
| 			name: "Do not retry if succeeded", | ||||
| 			fn: func(t *testing.T, ctx context.Context, exp *otlpmetric.Exporter, mc *mockCollector) { | ||||
| 				require.NoError(t, exp.Export(ctx, oneRecord)) | ||||
| 				require.NoError(t, exp.Export(ctx, testResource, oneRecord)) | ||||
| 
 | ||||
| 				metrics := mc.getMetrics() | ||||
| 
 | ||||
|  | @ -245,7 +248,7 @@ func TestExporterExportFailureAndRecoveryModes(t *testing.T) { | |||
| 				status.Error(codes.OK, ""), | ||||
| 			}, | ||||
| 			fn: func(t *testing.T, ctx context.Context, exp *otlpmetric.Exporter, mc *mockCollector) { | ||||
| 				require.NoError(t, exp.Export(ctx, oneRecord)) | ||||
| 				require.NoError(t, exp.Export(ctx, testResource, oneRecord)) | ||||
| 
 | ||||
| 				metrics := mc.getMetrics() | ||||
| 
 | ||||
|  | @ -267,7 +270,7 @@ func TestExporterExportFailureAndRecoveryModes(t *testing.T) { | |||
| 				status.Error(codes.Unavailable, "backend under pressure"), | ||||
| 			}, | ||||
| 			fn: func(t *testing.T, ctx context.Context, exp *otlpmetric.Exporter, mc *mockCollector) { | ||||
| 				require.NoError(t, exp.Export(ctx, oneRecord)) | ||||
| 				require.NoError(t, exp.Export(ctx, testResource, oneRecord)) | ||||
| 
 | ||||
| 				metrics := mc.getMetrics() | ||||
| 
 | ||||
|  | @ -287,7 +290,7 @@ func TestExporterExportFailureAndRecoveryModes(t *testing.T) { | |||
| 				status.Error(codes.InvalidArgument, "invalid arguments"), | ||||
| 			}, | ||||
| 			fn: func(t *testing.T, ctx context.Context, exp *otlpmetric.Exporter, mc *mockCollector) { | ||||
| 				require.Error(t, exp.Export(ctx, oneRecord)) | ||||
| 				require.Error(t, exp.Export(ctx, testResource, oneRecord)) | ||||
| 
 | ||||
| 				metric := mc.getMetrics() | ||||
| 
 | ||||
|  | @ -313,7 +316,7 @@ func TestExporterExportFailureAndRecoveryModes(t *testing.T) { | |||
| 				status.Error(codes.DataLoss, ""), | ||||
| 			}, | ||||
| 			fn: func(t *testing.T, ctx context.Context, exp *otlpmetric.Exporter, mc *mockCollector) { | ||||
| 				require.NoError(t, exp.Export(ctx, oneRecord)) | ||||
| 				require.NoError(t, exp.Export(ctx, testResource, oneRecord)) | ||||
| 
 | ||||
| 				metrics := mc.getMetrics() | ||||
| 
 | ||||
|  | @ -336,7 +339,7 @@ func TestExporterExportFailureAndRecoveryModes(t *testing.T) { | |||
| 				newThrottlingError(codes.ResourceExhausted, time.Second*30), | ||||
| 			}, | ||||
| 			fn: func(t *testing.T, ctx context.Context, exp *otlpmetric.Exporter, mc *mockCollector) { | ||||
| 				err := exp.Export(ctx, oneRecord) | ||||
| 				err := exp.Export(ctx, testResource, oneRecord) | ||||
| 				require.Error(t, err) | ||||
| 				require.Equal(t, "context deadline exceeded", err.Error()) | ||||
| 
 | ||||
|  | @ -358,7 +361,7 @@ func TestExporterExportFailureAndRecoveryModes(t *testing.T) { | |||
| 				newThrottlingError(codes.ResourceExhausted, time.Minute), | ||||
| 			}, | ||||
| 			fn: func(t *testing.T, ctx context.Context, exp *otlpmetric.Exporter, mc *mockCollector) { | ||||
| 				err := exp.Export(ctx, oneRecord) | ||||
| 				err := exp.Export(ctx, testResource, oneRecord) | ||||
| 				require.Error(t, err) | ||||
| 				require.Equal(t, "max elapsed time expired when respecting server throttle: rpc error: code = ResourceExhausted desc = ", err.Error()) | ||||
| 
 | ||||
|  | @ -385,7 +388,7 @@ func TestExporterExportFailureAndRecoveryModes(t *testing.T) { | |||
| 				status.Error(codes.Unavailable, "unavailable"), | ||||
| 			}, | ||||
| 			fn: func(t *testing.T, ctx context.Context, exp *otlpmetric.Exporter, mc *mockCollector) { | ||||
| 				err := exp.Export(ctx, oneRecord) | ||||
| 				err := exp.Export(ctx, testResource, oneRecord) | ||||
| 				require.Error(t, err) | ||||
| 
 | ||||
| 				require.Equal(t, "max elapsed time expired: rpc error: code = Unavailable desc = unavailable", err.Error()) | ||||
|  | @ -405,7 +408,7 @@ func TestExporterExportFailureAndRecoveryModes(t *testing.T) { | |||
| 				status.Error(codes.Unavailable, "unavailable"), | ||||
| 			}, | ||||
| 			fn: func(t *testing.T, ctx context.Context, exp *otlpmetric.Exporter, mc *mockCollector) { | ||||
| 				err := exp.Export(ctx, oneRecord) | ||||
| 				err := exp.Export(ctx, testResource, oneRecord) | ||||
| 				require.Error(t, err) | ||||
| 
 | ||||
| 				require.Equal(t, "rpc error: code = Unavailable desc = unavailable", err.Error()) | ||||
|  | @ -468,7 +471,7 @@ func TestPermanentErrorsShouldNotBeRetried(t *testing.T) { | |||
| 
 | ||||
| 			exp := newGRPCExporter(t, ctx, mc.endpoint) | ||||
| 
 | ||||
| 			err := exp.Export(ctx, oneRecord) | ||||
| 			err := exp.Export(ctx, testResource, oneRecord) | ||||
| 			require.Error(t, err) | ||||
| 			require.Len(t, mc.getMetrics(), 0) | ||||
| 			require.Equal(t, 1, mc.metricSvc.requests, "metric service must receive 1 permanent error requests.") | ||||
|  | @ -509,7 +512,7 @@ func TestNewExporter_collectorConnectionDiesThenReconnects(t *testing.T) { | |||
| 	for j := 0; j < 3; j++ { | ||||
| 
 | ||||
| 		// No endpoint up.
 | ||||
| 		require.Error(t, exp.Export(ctx, oneRecord)) | ||||
| 		require.Error(t, exp.Export(ctx, testResource, oneRecord)) | ||||
| 
 | ||||
| 		// Now resurrect the collector by making a new one but reusing the
 | ||||
| 		// old endpoint, and the collector should reconnect automatically.
 | ||||
|  | @ -520,7 +523,7 @@ func TestNewExporter_collectorConnectionDiesThenReconnects(t *testing.T) { | |||
| 
 | ||||
| 		n := 10 | ||||
| 		for i := 0; i < n; i++ { | ||||
| 			require.NoError(t, exp.Export(ctx, oneRecord)) | ||||
| 			require.NoError(t, exp.Export(ctx, testResource, oneRecord)) | ||||
| 		} | ||||
| 
 | ||||
| 		nmaMetrics := nmc.getMetrics() | ||||
|  | @ -582,7 +585,7 @@ func TestNewExporter_withHeaders(t *testing.T) { | |||
| 	ctx := context.Background() | ||||
| 	exp := newGRPCExporter(t, ctx, mc.endpoint, | ||||
| 		otlpmetricgrpc.WithHeaders(map[string]string{"header1": "value1"})) | ||||
| 	require.NoError(t, exp.Export(ctx, oneRecord)) | ||||
| 	require.NoError(t, exp.Export(ctx, testResource, oneRecord)) | ||||
| 
 | ||||
| 	defer func() { | ||||
| 		_ = exp.Shutdown(ctx) | ||||
|  | @ -606,7 +609,7 @@ func TestNewExporter_WithTimeout(t *testing.T) { | |||
| 		{ | ||||
| 			name: "Timeout Metrics", | ||||
| 			fn: func(exp *otlpmetric.Exporter) error { | ||||
| 				return exp.Export(context.Background(), oneRecord) | ||||
| 				return exp.Export(context.Background(), testResource, oneRecord) | ||||
| 			}, | ||||
| 			timeout: time.Millisecond * 100, | ||||
| 			code:    codes.DeadlineExceeded, | ||||
|  | @ -616,7 +619,7 @@ func TestNewExporter_WithTimeout(t *testing.T) { | |||
| 		{ | ||||
| 			name: "No Timeout Metrics", | ||||
| 			fn: func(exp *otlpmetric.Exporter) error { | ||||
| 				return exp.Export(context.Background(), oneRecord) | ||||
| 				return exp.Export(context.Background(), testResource, oneRecord) | ||||
| 			}, | ||||
| 			timeout: time.Minute, | ||||
| 			metrics: 1, | ||||
|  | @ -670,7 +673,7 @@ func TestNewExporter_withInvalidSecurityConfiguration(t *testing.T) { | |||
| 		t.Fatalf("failed to create a new collector exporter: %v", err) | ||||
| 	} | ||||
| 
 | ||||
| 	err = exp.Export(ctx, oneRecord) | ||||
| 	err = exp.Export(ctx, testResource, oneRecord) | ||||
| 
 | ||||
| 	expectedErr := fmt.Sprintf("metrics exporter is disconnected from the server %s: grpc: no transport security set (use grpc.WithInsecure() explicitly or set credentials)", mc.endpoint) | ||||
| 
 | ||||
|  | @ -698,7 +701,7 @@ func TestDisconnected(t *testing.T) { | |||
| 		assert.NoError(t, exp.Shutdown(ctx)) | ||||
| 	}() | ||||
| 
 | ||||
| 	assert.Error(t, exp.Export(ctx, oneRecord)) | ||||
| 	assert.Error(t, exp.Export(ctx, testResource, oneRecord)) | ||||
| } | ||||
| 
 | ||||
| func TestEmptyData(t *testing.T) { | ||||
|  | @ -716,7 +719,7 @@ func TestEmptyData(t *testing.T) { | |||
| 		assert.NoError(t, exp.Shutdown(ctx)) | ||||
| 	}() | ||||
| 
 | ||||
| 	assert.NoError(t, exp.Export(ctx, otlpmetrictest.EmptyCheckpointSet{})) | ||||
| 	assert.NoError(t, exp.Export(ctx, testResource, otlpmetrictest.EmptyCheckpointSet{})) | ||||
| } | ||||
| 
 | ||||
| func TestFailedMetricTransform(t *testing.T) { | ||||
|  | @ -734,5 +737,5 @@ func TestFailedMetricTransform(t *testing.T) { | |||
| 		assert.NoError(t, exp.Shutdown(ctx)) | ||||
| 	}() | ||||
| 
 | ||||
| 	assert.Error(t, exp.Export(ctx, otlpmetrictest.FailCheckpointSet{})) | ||||
| 	assert.Error(t, exp.Export(ctx, testResource, otlpmetrictest.FailCheckpointSet{})) | ||||
| } | ||||
|  |  | |||
|  | @ -7,6 +7,7 @@ require ( | |||
| 	go.opentelemetry.io/otel v1.0.0-RC2 | ||||
| 	go.opentelemetry.io/otel/exporters/otlp/otlpmetric v0.22.0 | ||||
| 	go.opentelemetry.io/otel/metric v0.22.0 | ||||
| 	go.opentelemetry.io/otel/sdk v1.0.0-RC2 | ||||
| 	go.opentelemetry.io/otel/sdk/metric v0.22.0 | ||||
| 	go.opentelemetry.io/proto/otlp v0.9.0 | ||||
| 	google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 | ||||
|  |  | |||
|  | @ -23,6 +23,7 @@ import ( | |||
| 	"time" | ||||
| 
 | ||||
| 	"go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp" | ||||
| 	"go.opentelemetry.io/otel/sdk/resource" | ||||
| 
 | ||||
| 	"github.com/stretchr/testify/assert" | ||||
| 	"github.com/stretchr/testify/require" | ||||
|  | @ -38,6 +39,8 @@ const ( | |||
| 
 | ||||
| var ( | ||||
| 	oneRecord = otlpmetrictest.OneRecordCheckpointSet{} | ||||
| 
 | ||||
| 	testResource = resource.Empty() | ||||
| ) | ||||
| 
 | ||||
| var ( | ||||
|  | @ -163,7 +166,7 @@ func TestRetry(t *testing.T) { | |||
| 	defer func() { | ||||
| 		assert.NoError(t, exporter.Shutdown(ctx)) | ||||
| 	}() | ||||
| 	err = exporter.Export(ctx, oneRecord) | ||||
| 	err = exporter.Export(ctx, testResource, oneRecord) | ||||
| 	assert.NoError(t, err) | ||||
| 	assert.Len(t, mc.GetMetrics(), 1) | ||||
| } | ||||
|  | @ -185,7 +188,7 @@ func TestTimeout(t *testing.T) { | |||
| 	defer func() { | ||||
| 		assert.NoError(t, exporter.Shutdown(ctx)) | ||||
| 	}() | ||||
| 	err = exporter.Export(ctx, oneRecord) | ||||
| 	err = exporter.Export(ctx, testResource, oneRecord) | ||||
| 	assert.Equal(t, true, os.IsTimeout(err)) | ||||
| } | ||||
| 
 | ||||
|  | @ -210,7 +213,7 @@ func TestRetryFailed(t *testing.T) { | |||
| 	defer func() { | ||||
| 		assert.NoError(t, exporter.Shutdown(ctx)) | ||||
| 	}() | ||||
| 	err = exporter.Export(ctx, oneRecord) | ||||
| 	err = exporter.Export(ctx, testResource, oneRecord) | ||||
| 	assert.Error(t, err) | ||||
| 	assert.Empty(t, mc.GetMetrics()) | ||||
| } | ||||
|  | @ -235,7 +238,7 @@ func TestNoRetry(t *testing.T) { | |||
| 	defer func() { | ||||
| 		assert.NoError(t, exporter.Shutdown(ctx)) | ||||
| 	}() | ||||
| 	err = exporter.Export(ctx, oneRecord) | ||||
| 	err = exporter.Export(ctx, testResource, oneRecord) | ||||
| 	assert.Error(t, err) | ||||
| 	assert.Equal(t, fmt.Sprintf("failed to send metrics to http://%s/v1/metrics with HTTP status 400 Bad Request", mc.endpoint), err.Error()) | ||||
| 	assert.Empty(t, mc.GetMetrics()) | ||||
|  | @ -256,7 +259,7 @@ func TestEmptyData(t *testing.T) { | |||
| 		assert.NoError(t, exporter.Shutdown(ctx)) | ||||
| 	}() | ||||
| 	assert.NoError(t, err) | ||||
| 	err = exporter.Export(ctx, oneRecord) | ||||
| 	err = exporter.Export(ctx, testResource, oneRecord) | ||||
| 	assert.NoError(t, err) | ||||
| 	assert.NotEmpty(t, mc.GetMetrics()) | ||||
| } | ||||
|  | @ -302,7 +305,7 @@ func TestUnreasonableMaxAttempts(t *testing.T) { | |||
| 			defer func() { | ||||
| 				assert.NoError(t, exporter.Shutdown(ctx)) | ||||
| 			}() | ||||
| 			err = exporter.Export(ctx, oneRecord) | ||||
| 			err = exporter.Export(ctx, testResource, oneRecord) | ||||
| 			assert.Error(t, err) | ||||
| 			assert.Empty(t, mc.GetMetrics()) | ||||
| 		}) | ||||
|  | @ -338,7 +341,7 @@ func TestUnreasonableBackoff(t *testing.T) { | |||
| 	defer func() { | ||||
| 		assert.NoError(t, exporter.Shutdown(context.Background())) | ||||
| 	}() | ||||
| 	err = exporter.Export(ctx, oneRecord) | ||||
| 	err = exporter.Export(ctx, testResource, oneRecord) | ||||
| 	assert.Error(t, err) | ||||
| 	assert.Empty(t, mc.GetMetrics()) | ||||
| } | ||||
|  | @ -363,7 +366,7 @@ func TestCancelledContext(t *testing.T) { | |||
| 		assert.NoError(t, exporter.Shutdown(context.Background())) | ||||
| 	}() | ||||
| 	cancel() | ||||
| 	_ = exporter.Export(ctx, oneRecord) | ||||
| 	_ = exporter.Export(ctx, testResource, oneRecord) | ||||
| 	assert.Empty(t, mc.GetMetrics()) | ||||
| } | ||||
| 
 | ||||
|  | @ -390,7 +393,7 @@ func TestDeadlineContext(t *testing.T) { | |||
| 	}() | ||||
| 	ctx, cancel := context.WithTimeout(ctx, time.Second) | ||||
| 	defer cancel() | ||||
| 	err = exporter.Export(ctx, oneRecord) | ||||
| 	err = exporter.Export(ctx, testResource, oneRecord) | ||||
| 	assert.Error(t, err) | ||||
| 	assert.Empty(t, mc.GetMetrics()) | ||||
| } | ||||
|  | @ -418,7 +421,7 @@ func TestStopWhileExporting(t *testing.T) { | |||
| 	}() | ||||
| 	doneCh := make(chan struct{}) | ||||
| 	go func() { | ||||
| 		err := exporter.Export(ctx, oneRecord) | ||||
| 		err := exporter.Export(ctx, testResource, oneRecord) | ||||
| 		assert.Error(t, err) | ||||
| 		assert.Empty(t, mc.GetMetrics()) | ||||
| 		close(doneCh) | ||||
|  |  | |||
|  | @ -6,6 +6,7 @@ require ( | |||
| 	github.com/stretchr/testify v1.7.0 | ||||
| 	go.opentelemetry.io/otel v1.0.0-RC2 | ||||
| 	go.opentelemetry.io/otel/exporters/otlp/otlpmetric v0.22.0 | ||||
| 	go.opentelemetry.io/otel/sdk v1.0.0-RC2 | ||||
| 	go.opentelemetry.io/proto/otlp v0.9.0 | ||||
| 	google.golang.org/protobuf v1.27.1 | ||||
| ) | ||||
|  |  | |||
|  | @ -33,6 +33,7 @@ import ( | |||
| 	export "go.opentelemetry.io/otel/sdk/export/metric" | ||||
| 	"go.opentelemetry.io/otel/sdk/export/metric/aggregation" | ||||
| 	controller "go.opentelemetry.io/otel/sdk/metric/controller/basic" | ||||
| 	"go.opentelemetry.io/otel/sdk/resource" | ||||
| ) | ||||
| 
 | ||||
| // Exporter supports Prometheus pulls.  It does not implement the
 | ||||
|  | @ -153,7 +154,7 @@ func (c *collector) Describe(ch chan<- *prometheus.Desc) { | |||
| 
 | ||||
| 	_ = c.exp.Controller().ForEach(c.exp, func(record export.Record) error { | ||||
| 		var labelKeys []string | ||||
| 		mergeLabels(record, &labelKeys, nil) | ||||
| 		mergeLabels(record, c.exp.controller.Resource(), &labelKeys, nil) | ||||
| 		ch <- c.toDesc(record, labelKeys) | ||||
| 		return nil | ||||
| 	}) | ||||
|  | @ -178,7 +179,7 @@ func (c *collector) Collect(ch chan<- prometheus.Metric) { | |||
| 		instrumentKind := record.Descriptor().InstrumentKind() | ||||
| 
 | ||||
| 		var labelKeys, labels []string | ||||
| 		mergeLabels(record, &labelKeys, &labels) | ||||
| 		mergeLabels(record, c.exp.controller.Resource(), &labelKeys, &labels) | ||||
| 
 | ||||
| 		desc := c.toDesc(record, labelKeys) | ||||
| 
 | ||||
|  | @ -294,17 +295,17 @@ func (c *collector) toDesc(record export.Record, labelKeys []string) *prometheus | |||
| // duplicate keys.  This outputs one or both of the keys and the
 | ||||
| // values as a slice, and either argument may be nil to avoid
 | ||||
| // allocating an unnecessary slice.
 | ||||
| func mergeLabels(record export.Record, keys, values *[]string) { | ||||
| func mergeLabels(record export.Record, res *resource.Resource, keys, values *[]string) { | ||||
| 	if keys != nil { | ||||
| 		*keys = make([]string, 0, record.Labels().Len()+record.Resource().Len()) | ||||
| 		*keys = make([]string, 0, record.Labels().Len()+res.Len()) | ||||
| 	} | ||||
| 	if values != nil { | ||||
| 		*values = make([]string, 0, record.Labels().Len()+record.Resource().Len()) | ||||
| 		*values = make([]string, 0, record.Labels().Len()+res.Len()) | ||||
| 	} | ||||
| 
 | ||||
| 	// Duplicate keys are resolved by taking the record label value over
 | ||||
| 	// the resource value.
 | ||||
| 	mi := attribute.NewMergeIterator(record.Labels(), record.Resource().Set()) | ||||
| 	mi := attribute.NewMergeIterator(record.Labels(), res.Set()) | ||||
| 	for mi.Next() { | ||||
| 		label := mi.Label() | ||||
| 		if keys != nil { | ||||
|  |  | |||
|  | @ -25,6 +25,7 @@ import ( | |||
| 	"go.opentelemetry.io/otel/metric" | ||||
| 	exportmetric "go.opentelemetry.io/otel/sdk/export/metric" | ||||
| 	"go.opentelemetry.io/otel/sdk/export/metric/aggregation" | ||||
| 	"go.opentelemetry.io/otel/sdk/resource" | ||||
| ) | ||||
| 
 | ||||
| type metricExporter struct { | ||||
|  | @ -49,14 +50,14 @@ func (e *metricExporter) ExportKindFor(desc *metric.Descriptor, kind aggregation | |||
| 	return exportmetric.StatelessExportKindSelector().ExportKindFor(desc, kind) | ||||
| } | ||||
| 
 | ||||
| func (e *metricExporter) Export(_ context.Context, checkpointSet exportmetric.CheckpointSet) error { | ||||
| func (e *metricExporter) Export(_ context.Context, res *resource.Resource, checkpointSet exportmetric.CheckpointSet) error { | ||||
| 	var aggError error | ||||
| 	var batch []line | ||||
| 	aggError = checkpointSet.ForEach(e, func(record exportmetric.Record) error { | ||||
| 		desc := record.Descriptor() | ||||
| 		agg := record.Aggregation() | ||||
| 		kind := desc.NumberKind() | ||||
| 		encodedResource := record.Resource().Encoded(e.config.LabelEncoder) | ||||
| 		encodedResource := res.Encoded(e.config.LabelEncoder) | ||||
| 
 | ||||
| 		var instLabels []attribute.KeyValue | ||||
| 		if name := desc.InstrumentationName(); name != "" { | ||||
|  |  | |||
|  | @ -207,47 +207,62 @@ func TestStdoutNoData(t *testing.T) { | |||
| 
 | ||||
| func TestStdoutResource(t *testing.T) { | ||||
| 	type testCase struct { | ||||
| 		name   string | ||||
| 		expect string | ||||
| 		res    *resource.Resource | ||||
| 		attrs  []attribute.KeyValue | ||||
| 	} | ||||
| 	newCase := func(expect string, res *resource.Resource, attrs ...attribute.KeyValue) testCase { | ||||
| 	newCase := func(name, expect string, res *resource.Resource, attrs ...attribute.KeyValue) testCase { | ||||
| 		return testCase{ | ||||
| 			name:   name, | ||||
| 			expect: expect, | ||||
| 			res:    res, | ||||
| 			attrs:  attrs, | ||||
| 		} | ||||
| 	} | ||||
| 	testCases := []testCase{ | ||||
| 		newCase("R1=V1,R2=V2,instrumentation.name=test,A=B,C=D", | ||||
| 		newCase("resource and attribute", | ||||
| 			"R1=V1,R2=V2,instrumentation.name=test,A=B,C=D", | ||||
| 			resource.NewSchemaless(attribute.String("R1", "V1"), attribute.String("R2", "V2")), | ||||
| 			attribute.String("A", "B"), | ||||
| 			attribute.String("C", "D")), | ||||
| 		newCase("R1=V1,R2=V2,instrumentation.name=test", | ||||
| 		newCase("only resource", | ||||
| 			"R1=V1,R2=V2,instrumentation.name=test", | ||||
| 			resource.NewSchemaless(attribute.String("R1", "V1"), attribute.String("R2", "V2")), | ||||
| 		), | ||||
| 		newCase("instrumentation.name=test,A=B,C=D", | ||||
| 			nil, | ||||
| 		newCase("empty resource", | ||||
| 			"instrumentation.name=test,A=B,C=D", | ||||
| 			resource.Empty(), | ||||
| 			attribute.String("A", "B"), | ||||
| 			attribute.String("C", "D"), | ||||
| 		), | ||||
| 		newCase("default resource", | ||||
| 			fmt.Sprint(resource.Default().Encoded(attribute.DefaultEncoder()), | ||||
| 				",instrumentation.name=test,A=B,C=D"), | ||||
| 			resource.Default(), | ||||
| 			attribute.String("A", "B"), | ||||
| 			attribute.String("C", "D"), | ||||
| 		), | ||||
| 		// We explicitly do not de-duplicate between resources
 | ||||
| 		// and metric labels in this exporter.
 | ||||
| 		newCase("R1=V1,R2=V2,instrumentation.name=test,R1=V3,R2=V4", | ||||
| 		newCase("resource deduplication", | ||||
| 			"R1=V1,R2=V2,instrumentation.name=test,R1=V3,R2=V4", | ||||
| 			resource.NewSchemaless(attribute.String("R1", "V1"), attribute.String("R2", "V2")), | ||||
| 			attribute.String("R1", "V3"), | ||||
| 			attribute.String("R2", "V4")), | ||||
| 	} | ||||
| 
 | ||||
| 	for _, tc := range testCases { | ||||
| 		ctx := context.Background() | ||||
| 		fix := newFixtureWithResource(t, tc.res) | ||||
| 		t.Run(tc.name, func(t *testing.T) { | ||||
| 			ctx := context.Background() | ||||
| 			fix := newFixtureWithResource(t, tc.res) | ||||
| 
 | ||||
| 		counter := metric.Must(fix.meter).NewFloat64Counter("name.lastvalue") | ||||
| 		counter.Add(ctx, 123.456, tc.attrs...) | ||||
| 			counter := metric.Must(fix.meter).NewFloat64Counter("name.lastvalue") | ||||
| 			counter.Add(ctx, 123.456, tc.attrs...) | ||||
| 
 | ||||
| 		require.NoError(t, fix.cont.Stop(fix.ctx)) | ||||
| 			require.NoError(t, fix.cont.Stop(fix.ctx)) | ||||
| 
 | ||||
| 		require.Equal(t, `[{"Name":"name.lastvalue{`+tc.expect+`}","Last":123.456}]`, fix.Output()) | ||||
| 			require.Equal(t, `[{"Name":"name.lastvalue{`+tc.expect+`}","Last":123.456}]`, fix.Output()) | ||||
| 		}) | ||||
| 	} | ||||
| } | ||||
|  |  | |||
|  | @ -211,7 +211,7 @@ type Exporter interface { | |||
| 	//
 | ||||
| 	// The CheckpointSet interface refers to the Processor that just
 | ||||
| 	// completed collection.
 | ||||
| 	Export(ctx context.Context, checkpointSet CheckpointSet) error | ||||
| 	Export(ctx context.Context, resource *resource.Resource, checkpointSet CheckpointSet) error | ||||
| 
 | ||||
| 	// ExportKindSelector is an interface used by the Processor
 | ||||
| 	// in deciding whether to compute Delta or Cumulative
 | ||||
|  | @ -269,7 +269,6 @@ type CheckpointSet interface { | |||
| type Metadata struct { | ||||
| 	descriptor *metric.Descriptor | ||||
| 	labels     *attribute.Set | ||||
| 	resource   *resource.Resource | ||||
| } | ||||
| 
 | ||||
| // Accumulation contains the exported data for a single metric instrument
 | ||||
|  | @ -300,21 +299,15 @@ func (m Metadata) Labels() *attribute.Set { | |||
| 	return m.labels | ||||
| } | ||||
| 
 | ||||
| // Resource contains common attributes that apply to this metric event.
 | ||||
| func (m Metadata) Resource() *resource.Resource { | ||||
| 	return m.resource | ||||
| } | ||||
| 
 | ||||
| // NewAccumulation allows Accumulator implementations to construct new
 | ||||
| // Accumulations to send to Processors. The Descriptor, Labels, Resource,
 | ||||
| // Accumulations to send to Processors. The Descriptor, Labels,
 | ||||
| // and Aggregator represent aggregate metric events received over a single
 | ||||
| // collection period.
 | ||||
| func NewAccumulation(descriptor *metric.Descriptor, labels *attribute.Set, resource *resource.Resource, aggregator Aggregator) Accumulation { | ||||
| func NewAccumulation(descriptor *metric.Descriptor, labels *attribute.Set, aggregator Aggregator) Accumulation { | ||||
| 	return Accumulation{ | ||||
| 		Metadata: Metadata{ | ||||
| 			descriptor: descriptor, | ||||
| 			labels:     labels, | ||||
| 			resource:   resource, | ||||
| 		}, | ||||
| 		aggregator: aggregator, | ||||
| 	} | ||||
|  | @ -329,12 +322,11 @@ func (r Accumulation) Aggregator() Aggregator { | |||
| // NewRecord allows Processor implementations to construct export
 | ||||
| // records.  The Descriptor, Labels, and Aggregator represent
 | ||||
| // aggregate metric events received over a single collection period.
 | ||||
| func NewRecord(descriptor *metric.Descriptor, labels *attribute.Set, resource *resource.Resource, aggregation aggregation.Aggregation, start, end time.Time) Record { | ||||
| func NewRecord(descriptor *metric.Descriptor, labels *attribute.Set, aggregation aggregation.Aggregation, start, end time.Time) Record { | ||||
| 	return Record{ | ||||
| 		Metadata: Metadata{ | ||||
| 			descriptor: descriptor, | ||||
| 			labels:     labels, | ||||
| 			resource:   resource, | ||||
| 		}, | ||||
| 		aggregation: aggregation, | ||||
| 		start:       start, | ||||
|  |  | |||
|  | @ -42,7 +42,7 @@ func newFixture(b *testing.B) *benchFixture { | |||
| 		AggregatorSelector: processortest.AggregatorSelector(), | ||||
| 	} | ||||
| 
 | ||||
| 	bf.accumulator = sdk.NewAccumulator(bf, nil) | ||||
| 	bf.accumulator = sdk.NewAccumulator(bf) | ||||
| 	bf.meter = metric.WrapMeterImpl(bf.accumulator, "benchmarks") | ||||
| 	return bf | ||||
| } | ||||
|  |  | |||
|  | @ -68,17 +68,17 @@ type Option interface { | |||
| // WithResource sets the Resource configuration option of a Config by merging it
 | ||||
| // with the Resource configuration in the environment.
 | ||||
| func WithResource(r *resource.Resource) Option { | ||||
| 	res, err := resource.Merge(resource.Environment(), r) | ||||
| 	if err != nil { | ||||
| 		otel.Handle(err) | ||||
| 	} | ||||
| 	return resourceOption{res} | ||||
| 	return resourceOption{r} | ||||
| } | ||||
| 
 | ||||
| type resourceOption struct{ *resource.Resource } | ||||
| 
 | ||||
| func (o resourceOption) apply(cfg *config) { | ||||
| 	cfg.Resource = o.Resource | ||||
| 	res, err := resource.Merge(cfg.Resource, o.Resource) | ||||
| 	if err != nil { | ||||
| 		otel.Handle(err) | ||||
| 	} | ||||
| 	cfg.Resource = res | ||||
| } | ||||
| 
 | ||||
| // WithCollectPeriod sets the CollectPeriod configuration option of a Config.
 | ||||
|  |  | |||
|  | @ -59,6 +59,7 @@ type Controller struct { | |||
| 	accumulator  *sdk.Accumulator | ||||
| 	provider     *registry.MeterProvider | ||||
| 	checkpointer export.Checkpointer | ||||
| 	resource     *resource.Resource | ||||
| 	exporter     export.Exporter | ||||
| 	wg           sync.WaitGroup | ||||
| 	stopCh       chan struct{} | ||||
|  | @ -88,16 +89,19 @@ func New(checkpointer export.Checkpointer, opts ...Option) *Controller { | |||
| 	} | ||||
| 	if c.Resource == nil { | ||||
| 		c.Resource = resource.Default() | ||||
| 	} else { | ||||
| 		var err error | ||||
| 		c.Resource, err = resource.Merge(resource.Environment(), c.Resource) | ||||
| 		if err != nil { | ||||
| 			otel.Handle(err) | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	impl := sdk.NewAccumulator( | ||||
| 		checkpointer, | ||||
| 		c.Resource, | ||||
| 	) | ||||
| 	impl := sdk.NewAccumulator(checkpointer) | ||||
| 	return &Controller{ | ||||
| 		provider:     registry.NewMeterProvider(impl), | ||||
| 		accumulator:  impl, | ||||
| 		checkpointer: checkpointer, | ||||
| 		resource:     c.Resource, | ||||
| 		exporter:     c.Exporter, | ||||
| 		stopCh:       nil, | ||||
| 		clock:        controllerTime.RealClock{}, | ||||
|  | @ -121,6 +125,12 @@ func (c *Controller) MeterProvider() metric.MeterProvider { | |||
| 	return c.provider | ||||
| } | ||||
| 
 | ||||
| // Resource returns the *resource.Resource associated with this
 | ||||
| // controller.
 | ||||
| func (c *Controller) Resource() *resource.Resource { | ||||
| 	return c.resource | ||||
| } | ||||
| 
 | ||||
| // Start begins a ticker that periodically collects and exports
 | ||||
| // metrics with the configured interval.  This is required for calling
 | ||||
| // a configured Exporter (see WithExporter) and is otherwise optional
 | ||||
|  | @ -257,7 +267,7 @@ func (c *Controller) export(ctx context.Context) error { | |||
| 		defer cancel() | ||||
| 	} | ||||
| 
 | ||||
| 	return c.exporter.Export(ctx, ckpt) | ||||
| 	return c.exporter.Export(ctx, c.resource, ckpt) | ||||
| } | ||||
| 
 | ||||
| // ForEach gives the caller read-locked access to the current
 | ||||
|  |  | |||
|  | @ -61,9 +61,11 @@ func checkTestContext(t *testing.T, ctx context.Context) { | |||
| } | ||||
| 
 | ||||
| func TestControllerUsesResource(t *testing.T) { | ||||
| 	const envVal = "T=U,key=value" | ||||
| 	store, err := ottest.SetEnvVariables(map[string]string{ | ||||
| 		envVar: "key=value,T=U", | ||||
| 		envVar: envVal, | ||||
| 	}) | ||||
| 
 | ||||
| 	require.NoError(t, err) | ||||
| 	defer func() { require.NoError(t, store.Restore()) }() | ||||
| 
 | ||||
|  | @ -75,48 +77,62 @@ func TestControllerUsesResource(t *testing.T) { | |||
| 		{ | ||||
| 			name:    "explicitly empty resource", | ||||
| 			options: []controller.Option{controller.WithResource(resource.Empty())}, | ||||
| 			wanted:  resource.Environment().Encoded(attribute.DefaultEncoder())}, | ||||
| 			wanted:  envVal, | ||||
| 		}, | ||||
| 		{ | ||||
| 			name:    "uses default if no resource option", | ||||
| 			options: nil, | ||||
| 			wanted:  resource.Default().Encoded(attribute.DefaultEncoder())}, | ||||
| 			wanted:  resource.Default().Encoded(attribute.DefaultEncoder()), | ||||
| 		}, | ||||
| 		{ | ||||
| 			name:    "explicit resource", | ||||
| 			options: []controller.Option{controller.WithResource(resource.NewSchemaless(attribute.String("R", "S")))}, | ||||
| 			wanted:  "R=S,T=U,key=value"}, | ||||
| 			wanted:  "R=S," + envVal, | ||||
| 		}, | ||||
| 		{ | ||||
| 			name: "last resource wins", | ||||
| 			name: "multi resource", | ||||
| 			options: []controller.Option{ | ||||
| 				controller.WithResource(resource.Default()), | ||||
| 				controller.WithResource(resource.NewSchemaless(attribute.String("R", "WRONG"))), | ||||
| 				controller.WithResource(resource.NewSchemaless(attribute.String("R", "S"))), | ||||
| 				controller.WithResource(resource.NewSchemaless(attribute.String("W", "X"))), | ||||
| 				controller.WithResource(resource.NewSchemaless(attribute.String("T", "V"))), | ||||
| 			}, | ||||
| 			wanted: "R=S,T=U,key=value"}, | ||||
| 			wanted: "R=S,T=V,W=X,key=value", | ||||
| 		}, | ||||
| 		{ | ||||
| 			name:    "overlapping attributes with environment resource", | ||||
| 			options: []controller.Option{controller.WithResource(resource.NewSchemaless(attribute.String("T", "V")))}, | ||||
| 			wanted:  "T=V,key=value"}, | ||||
| 			name: "user override environment", | ||||
| 			options: []controller.Option{ | ||||
| 				controller.WithResource(resource.NewSchemaless(attribute.String("T", "V"))), | ||||
| 				controller.WithResource(resource.NewSchemaless(attribute.String("key", "I win"))), | ||||
| 			}, | ||||
| 			wanted: "T=V,key=I win", | ||||
| 		}, | ||||
| 	} | ||||
| 	for _, c := range cases { | ||||
| 		t.Run(fmt.Sprintf("case-%s", c.name), func(t *testing.T) { | ||||
| 			sel := export.CumulativeExportKindSelector() | ||||
| 			exp := processortest.New(sel, attribute.DefaultEncoder()) | ||||
| 			cont := controller.New( | ||||
| 				processor.New( | ||||
| 					processortest.AggregatorSelector(), | ||||
| 					export.CumulativeExportKindSelector(), | ||||
| 					exp, | ||||
| 				), | ||||
| 				c.options..., | ||||
| 				append(c.options, controller.WithExporter(exp))..., | ||||
| 			) | ||||
| 			ctx := context.Background() | ||||
| 			require.NoError(t, cont.Start(ctx)) | ||||
| 			prov := cont.MeterProvider() | ||||
| 
 | ||||
| 			ctr := metric.Must(prov.Meter("named")).NewFloat64Counter("calls.sum") | ||||
| 			ctr.Add(context.Background(), 1.) | ||||
| 
 | ||||
| 			// Collect once
 | ||||
| 			require.NoError(t, cont.Collect(context.Background())) | ||||
| 			require.NoError(t, cont.Stop(ctx)) | ||||
| 
 | ||||
| 			expect := map[string]float64{ | ||||
| 				"calls.sum//" + c.wanted: 1., | ||||
| 			} | ||||
| 			require.EqualValues(t, expect, getMap(t, cont)) | ||||
| 			require.EqualValues(t, expect, exp.Values()) | ||||
| 		}) | ||||
| 	} | ||||
| } | ||||
|  | @ -268,9 +284,9 @@ func newBlockingExporter() *blockingExporter { | |||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (b *blockingExporter) Export(ctx context.Context, output export.CheckpointSet) error { | ||||
| func (b *blockingExporter) Export(ctx context.Context, res *resource.Resource, output export.CheckpointSet) error { | ||||
| 	var err error | ||||
| 	_ = b.exporter.Export(ctx, output) | ||||
| 	_ = b.exporter.Export(ctx, res, output) | ||||
| 	if b.calls == 0 { | ||||
| 		// timeout once
 | ||||
| 		<-ctx.Done() | ||||
|  |  | |||
|  | @ -30,11 +30,9 @@ import ( | |||
| 	"go.opentelemetry.io/otel/sdk/export/metric/aggregation" | ||||
| 	metricsdk "go.opentelemetry.io/otel/sdk/metric" | ||||
| 	"go.opentelemetry.io/otel/sdk/metric/processor/processortest" | ||||
| 	"go.opentelemetry.io/otel/sdk/resource" | ||||
| ) | ||||
| 
 | ||||
| var Must = metric.Must | ||||
| var testResource = resource.NewSchemaless(attribute.String("R", "V")) | ||||
| 
 | ||||
| type handler struct { | ||||
| 	sync.Mutex | ||||
|  | @ -87,7 +85,6 @@ func newSDK(t *testing.T) (metric.Meter, *metricsdk.Accumulator, *testSelector, | |||
| 	) | ||||
| 	accum := metricsdk.NewAccumulator( | ||||
| 		processor, | ||||
| 		testResource, | ||||
| 	) | ||||
| 	meter := metric.WrapMeterImpl(accum, "test") | ||||
| 	return meter, accum, testSelector, processor | ||||
|  | @ -109,7 +106,7 @@ func TestInputRangeCounter(t *testing.T) { | |||
| 	counter.Add(ctx, 1) | ||||
| 	checkpointed = sdk.Collect(ctx) | ||||
| 	require.Equal(t, map[string]float64{ | ||||
| 		"name.sum//R=V": 1, | ||||
| 		"name.sum//": 1, | ||||
| 	}, processor.Values()) | ||||
| 	require.Equal(t, 1, checkpointed) | ||||
| 	require.Nil(t, testHandler.Flush()) | ||||
|  | @ -128,7 +125,7 @@ func TestInputRangeUpDownCounter(t *testing.T) { | |||
| 
 | ||||
| 	checkpointed := sdk.Collect(ctx) | ||||
| 	require.Equal(t, map[string]float64{ | ||||
| 		"name.sum//R=V": 1, | ||||
| 		"name.sum//": 1, | ||||
| 	}, processor.Values()) | ||||
| 	require.Equal(t, 1, checkpointed) | ||||
| 	require.Nil(t, testHandler.Flush()) | ||||
|  | @ -153,7 +150,7 @@ func TestInputRangeValueRecorder(t *testing.T) { | |||
| 	checkpointed = sdk.Collect(ctx) | ||||
| 
 | ||||
| 	require.Equal(t, map[string]float64{ | ||||
| 		"name.exact//R=V": 3, | ||||
| 		"name.exact//": 3, | ||||
| 	}, processor.Values()) | ||||
| 	require.Equal(t, 1, checkpointed) | ||||
| 	require.Nil(t, testHandler.Flush()) | ||||
|  | @ -225,7 +222,7 @@ func TestSDKLabelsDeduplication(t *testing.T) { | |||
| 		counter.Add(ctx, 1, kvsA...) | ||||
| 		format := func(attrs []attribute.KeyValue) string { | ||||
| 			str := attribute.DefaultEncoder().Encode(newSetIter(attrs...)) | ||||
| 			return fmt.Sprint("name.sum/", str, "/R=V") | ||||
| 			return fmt.Sprint("name.sum/", str, "/") | ||||
| 		} | ||||
| 		allExpect[format(expectA)] += 2 | ||||
| 
 | ||||
|  | @ -329,20 +326,20 @@ func TestObserverCollection(t *testing.T) { | |||
| 
 | ||||
| 		mult := float64(mult) | ||||
| 		require.EqualValues(t, map[string]float64{ | ||||
| 			"float.valueobserver.lastvalue/A=B/R=V": -mult, | ||||
| 			"float.valueobserver.lastvalue/C=D/R=V": -mult, | ||||
| 			"int.valueobserver.lastvalue//R=V":      mult, | ||||
| 			"int.valueobserver.lastvalue/A=B/R=V":   mult, | ||||
| 			"float.valueobserver.lastvalue/A=B/": -mult, | ||||
| 			"float.valueobserver.lastvalue/C=D/": -mult, | ||||
| 			"int.valueobserver.lastvalue//":      mult, | ||||
| 			"int.valueobserver.lastvalue/A=B/":   mult, | ||||
| 
 | ||||
| 			"float.sumobserver.sum/A=B/R=V": 2 * mult, | ||||
| 			"float.sumobserver.sum/C=D/R=V": mult, | ||||
| 			"int.sumobserver.sum//R=V":      mult, | ||||
| 			"int.sumobserver.sum/A=B/R=V":   mult, | ||||
| 			"float.sumobserver.sum/A=B/": 2 * mult, | ||||
| 			"float.sumobserver.sum/C=D/": mult, | ||||
| 			"int.sumobserver.sum//":      mult, | ||||
| 			"int.sumobserver.sum/A=B/":   mult, | ||||
| 
 | ||||
| 			"float.updownsumobserver.sum/A=B/R=V": -2 * mult, | ||||
| 			"float.updownsumobserver.sum/C=D/R=V": mult, | ||||
| 			"int.updownsumobserver.sum//R=V":      -mult, | ||||
| 			"int.updownsumobserver.sum/A=B/R=V":   mult, | ||||
| 			"float.updownsumobserver.sum/A=B/": -2 * mult, | ||||
| 			"float.updownsumobserver.sum/C=D/": mult, | ||||
| 			"int.updownsumobserver.sum//":      -mult, | ||||
| 			"int.updownsumobserver.sum/A=B/":   mult, | ||||
| 		}, processor.Values()) | ||||
| 	} | ||||
| } | ||||
|  | @ -429,20 +426,20 @@ func TestObserverBatch(t *testing.T) { | |||
| 	require.Equal(t, collected, len(processor.Values())) | ||||
| 
 | ||||
| 	require.EqualValues(t, map[string]float64{ | ||||
| 		"float.sumobserver.sum//R=V":    1.1, | ||||
| 		"float.sumobserver.sum/A=B/R=V": 1000, | ||||
| 		"int.sumobserver.sum//R=V":      10, | ||||
| 		"int.sumobserver.sum/A=B/R=V":   100, | ||||
| 		"float.sumobserver.sum//":    1.1, | ||||
| 		"float.sumobserver.sum/A=B/": 1000, | ||||
| 		"int.sumobserver.sum//":      10, | ||||
| 		"int.sumobserver.sum/A=B/":   100, | ||||
| 
 | ||||
| 		"int.updownsumobserver.sum/A=B/R=V":   -100, | ||||
| 		"float.updownsumobserver.sum/A=B/R=V": -1000, | ||||
| 		"int.updownsumobserver.sum//R=V":      10, | ||||
| 		"float.updownsumobserver.sum/C=D/R=V": -1, | ||||
| 		"int.updownsumobserver.sum/A=B/":   -100, | ||||
| 		"float.updownsumobserver.sum/A=B/": -1000, | ||||
| 		"int.updownsumobserver.sum//":      10, | ||||
| 		"float.updownsumobserver.sum/C=D/": -1, | ||||
| 
 | ||||
| 		"float.valueobserver.lastvalue/A=B/R=V": -1, | ||||
| 		"float.valueobserver.lastvalue/C=D/R=V": -1, | ||||
| 		"int.valueobserver.lastvalue//R=V":      1, | ||||
| 		"int.valueobserver.lastvalue/A=B/R=V":   1, | ||||
| 		"float.valueobserver.lastvalue/A=B/": -1, | ||||
| 		"float.valueobserver.lastvalue/C=D/": -1, | ||||
| 		"int.valueobserver.lastvalue//":      1, | ||||
| 		"int.valueobserver.lastvalue/A=B/":   1, | ||||
| 	}, processor.Values()) | ||||
| } | ||||
| 
 | ||||
|  | @ -470,10 +467,10 @@ func TestRecordBatch(t *testing.T) { | |||
| 	sdk.Collect(ctx) | ||||
| 
 | ||||
| 	require.EqualValues(t, map[string]float64{ | ||||
| 		"int64.sum/A=B,C=D/R=V":     1, | ||||
| 		"float64.sum/A=B,C=D/R=V":   2, | ||||
| 		"int64.exact/A=B,C=D/R=V":   3, | ||||
| 		"float64.exact/A=B,C=D/R=V": 4, | ||||
| 		"int64.sum/A=B,C=D/":     1, | ||||
| 		"float64.sum/A=B,C=D/":   2, | ||||
| 		"int64.exact/A=B,C=D/":   3, | ||||
| 		"float64.exact/A=B,C=D/": 4, | ||||
| 	}, processor.Values()) | ||||
| } | ||||
| 
 | ||||
|  | @ -549,7 +546,7 @@ func TestSyncInAsync(t *testing.T) { | |||
| 	sdk.Collect(ctx) | ||||
| 
 | ||||
| 	require.EqualValues(t, map[string]float64{ | ||||
| 		"counter.sum//R=V":        100, | ||||
| 		"observer.lastvalue//R=V": 10, | ||||
| 		"counter.sum//":        100, | ||||
| 		"observer.lastvalue//": 10, | ||||
| 	}, processor.Values()) | ||||
| } | ||||
|  |  | |||
|  | @ -24,7 +24,6 @@ import ( | |||
| 	"go.opentelemetry.io/otel/metric" | ||||
| 	export "go.opentelemetry.io/otel/sdk/export/metric" | ||||
| 	"go.opentelemetry.io/otel/sdk/export/metric/aggregation" | ||||
| 	"go.opentelemetry.io/otel/sdk/resource" | ||||
| ) | ||||
| 
 | ||||
| type ( | ||||
|  | @ -38,27 +37,23 @@ type ( | |||
| 	stateKey struct { | ||||
| 		// TODO: This code is organized to support multiple
 | ||||
| 		// accumulators which could theoretically produce the
 | ||||
| 		// data for the same instrument with the same
 | ||||
| 		// resources, and this code has logic to combine data
 | ||||
| 		// properly from multiple accumulators.  However, the
 | ||||
| 		// use of *metric.Descriptor in the stateKey makes
 | ||||
| 		// such combination impossible, because each
 | ||||
| 		// accumulator allocates its own instruments.  This
 | ||||
| 		// can be fixed by using the instrument name and kind
 | ||||
| 		// instead of the descriptor pointer.  See
 | ||||
| 		// data for the same instrument, and this code has
 | ||||
| 		// logic to combine data properly from multiple
 | ||||
| 		// accumulators.  However, the use of
 | ||||
| 		// *metric.Descriptor in the stateKey makes such
 | ||||
| 		// combination impossible, because each accumulator
 | ||||
| 		// allocates its own instruments.  This can be fixed
 | ||||
| 		// by using the instrument name and kind instead of
 | ||||
| 		// the descriptor pointer.  See
 | ||||
| 		// https://github.com/open-telemetry/opentelemetry-go/issues/862.
 | ||||
| 		descriptor *metric.Descriptor | ||||
| 		distinct   attribute.Distinct | ||||
| 		resource   attribute.Distinct | ||||
| 	} | ||||
| 
 | ||||
| 	stateValue struct { | ||||
| 		// labels corresponds to the stateKey.distinct field.
 | ||||
| 		labels *attribute.Set | ||||
| 
 | ||||
| 		// resource corresponds to the stateKey.resource field.
 | ||||
| 		resource *resource.Resource | ||||
| 
 | ||||
| 		// updated indicates the last sequence number when this value had
 | ||||
| 		// Process() called by an accumulator.
 | ||||
| 		updated int64 | ||||
|  | @ -157,7 +152,6 @@ func (b *Processor) Process(accum export.Accumulation) error { | |||
| 	key := stateKey{ | ||||
| 		descriptor: desc, | ||||
| 		distinct:   accum.Labels().Equivalent(), | ||||
| 		resource:   accum.Resource().Equivalent(), | ||||
| 	} | ||||
| 	agg := accum.Aggregator() | ||||
| 
 | ||||
|  | @ -168,7 +162,6 @@ func (b *Processor) Process(accum export.Accumulation) error { | |||
| 
 | ||||
| 		newValue := &stateValue{ | ||||
| 			labels:   accum.Labels(), | ||||
| 			resource: accum.Resource(), | ||||
| 			updated:  b.state.finishedCollection, | ||||
| 			stateful: stateful, | ||||
| 			current:  agg, | ||||
|  | @ -369,7 +362,6 @@ func (b *state) ForEach(exporter export.ExportKindSelector, f func(export.Record | |||
| 		if err := f(export.NewRecord( | ||||
| 			key.descriptor, | ||||
| 			value.labels, | ||||
| 			value.resource, | ||||
| 			agg, | ||||
| 			start, | ||||
| 			b.intervalEnd, | ||||
|  |  | |||
|  | @ -108,13 +108,13 @@ func asNumber(nkind number.Kind, value int64) number.Number { | |||
| 	return number.NewFloat64Number(float64(value)) | ||||
| } | ||||
| 
 | ||||
| func updateFor(t *testing.T, desc *metric.Descriptor, selector export.AggregatorSelector, res *resource.Resource, value int64, labs ...attribute.KeyValue) export.Accumulation { | ||||
| func updateFor(t *testing.T, desc *metric.Descriptor, selector export.AggregatorSelector, value int64, labs ...attribute.KeyValue) export.Accumulation { | ||||
| 	ls := attribute.NewSet(labs...) | ||||
| 	var agg export.Aggregator | ||||
| 	selector.AggregatorFor(desc, &agg) | ||||
| 	require.NoError(t, agg.Update(context.Background(), asNumber(desc.NumberKind(), value), desc)) | ||||
| 
 | ||||
| 	return export.NewAccumulation(desc, &ls, res, agg) | ||||
| 	return export.NewAccumulation(desc, &ls, agg) | ||||
| } | ||||
| 
 | ||||
| func testProcessor( | ||||
|  | @ -127,7 +127,6 @@ func testProcessor( | |||
| 	// Note: this selector uses the instrument name to dictate
 | ||||
| 	// aggregation kind.
 | ||||
| 	selector := processorTest.AggregatorSelector() | ||||
| 	res := resource.NewSchemaless(attribute.String("R", "V")) | ||||
| 
 | ||||
| 	labs1 := []attribute.KeyValue{attribute.String("L1", "V")} | ||||
| 	labs2 := []attribute.KeyValue{attribute.String("L2", "V")} | ||||
|  | @ -154,8 +153,8 @@ func testProcessor( | |||
| 			processor.StartCollection() | ||||
| 
 | ||||
| 			for na := 0; na < nAccum; na++ { | ||||
| 				_ = processor.Process(updateFor(t, &desc1, selector, res, input, labs1...)) | ||||
| 				_ = processor.Process(updateFor(t, &desc2, selector, res, input, labs2...)) | ||||
| 				_ = processor.Process(updateFor(t, &desc1, selector, input, labs1...)) | ||||
| 				_ = processor.Process(updateFor(t, &desc2, selector, input, labs2...)) | ||||
| 			} | ||||
| 
 | ||||
| 			err := processor.FinishCollection() | ||||
|  | @ -235,8 +234,8 @@ func testProcessor( | |||
| 				exp := map[string]float64{} | ||||
| 				if hasMemory || !repetitionAfterEmptyInterval { | ||||
| 					exp = map[string]float64{ | ||||
| 						fmt.Sprintf("inst1%s/L1=V/R=V", instSuffix): float64(multiplier * 10), // labels1
 | ||||
| 						fmt.Sprintf("inst2%s/L2=V/R=V", instSuffix): float64(multiplier * 10), // labels2
 | ||||
| 						fmt.Sprintf("inst1%s/L1=V/", instSuffix): float64(multiplier * 10), // labels1
 | ||||
| 						fmt.Sprintf("inst2%s/L2=V/", instSuffix): float64(multiplier * 10), // labels2
 | ||||
| 					} | ||||
| 				} | ||||
| 
 | ||||
|  | @ -302,7 +301,7 @@ func TestBasicInconsistent(t *testing.T) { | |||
| 	b = basic.New(processorTest.AggregatorSelector(), export.StatelessExportKindSelector()) | ||||
| 
 | ||||
| 	desc := metric.NewDescriptor("inst", sdkapi.CounterInstrumentKind, number.Int64Kind) | ||||
| 	accum := export.NewAccumulation(&desc, attribute.EmptySet(), resource.Empty(), aggregatortest.NoopAggregator{}) | ||||
| 	accum := export.NewAccumulation(&desc, attribute.EmptySet(), aggregatortest.NoopAggregator{}) | ||||
| 	require.Equal(t, basic.ErrInconsistentState, b.Process(accum)) | ||||
| 
 | ||||
| 	// Test invalid kind:
 | ||||
|  | @ -327,7 +326,7 @@ func TestBasicTimestamps(t *testing.T) { | |||
| 	afterNew := time.Now() | ||||
| 
 | ||||
| 	desc := metric.NewDescriptor("inst", sdkapi.CounterInstrumentKind, number.Int64Kind) | ||||
| 	accum := export.NewAccumulation(&desc, attribute.EmptySet(), resource.Empty(), aggregatortest.NoopAggregator{}) | ||||
| 	accum := export.NewAccumulation(&desc, attribute.EmptySet(), aggregatortest.NoopAggregator{}) | ||||
| 
 | ||||
| 	b.StartCollection() | ||||
| 	_ = b.Process(accum) | ||||
|  | @ -369,7 +368,6 @@ func TestBasicTimestamps(t *testing.T) { | |||
| } | ||||
| 
 | ||||
| func TestStatefulNoMemoryCumulative(t *testing.T) { | ||||
| 	res := resource.NewSchemaless(attribute.String("R", "V")) | ||||
| 	ekindSel := export.CumulativeExportKindSelector() | ||||
| 
 | ||||
| 	desc := metric.NewDescriptor("inst.sum", sdkapi.CounterInstrumentKind, number.Int64Kind) | ||||
|  | @ -390,20 +388,19 @@ func TestStatefulNoMemoryCumulative(t *testing.T) { | |||
| 
 | ||||
| 		// Add 10
 | ||||
| 		processor.StartCollection() | ||||
| 		_ = processor.Process(updateFor(t, &desc, selector, res, 10, attribute.String("A", "B"))) | ||||
| 		_ = processor.Process(updateFor(t, &desc, selector, 10, attribute.String("A", "B"))) | ||||
| 		require.NoError(t, processor.FinishCollection()) | ||||
| 
 | ||||
| 		// Verify one element
 | ||||
| 		records = processorTest.NewOutput(attribute.DefaultEncoder()) | ||||
| 		require.NoError(t, checkpointSet.ForEach(ekindSel, records.AddRecord)) | ||||
| 		require.EqualValues(t, map[string]float64{ | ||||
| 			"inst.sum/A=B/R=V": float64(i * 10), | ||||
| 			"inst.sum/A=B/": float64(i * 10), | ||||
| 		}, records.Map()) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func TestStatefulNoMemoryDelta(t *testing.T) { | ||||
| 	res := resource.NewSchemaless(attribute.String("R", "V")) | ||||
| 	ekindSel := export.DeltaExportKindSelector() | ||||
| 
 | ||||
| 	desc := metric.NewDescriptor("inst.sum", sdkapi.SumObserverInstrumentKind, number.Int64Kind) | ||||
|  | @ -424,14 +421,14 @@ func TestStatefulNoMemoryDelta(t *testing.T) { | |||
| 
 | ||||
| 		// Add 10
 | ||||
| 		processor.StartCollection() | ||||
| 		_ = processor.Process(updateFor(t, &desc, selector, res, int64(i*10), attribute.String("A", "B"))) | ||||
| 		_ = processor.Process(updateFor(t, &desc, selector, int64(i*10), attribute.String("A", "B"))) | ||||
| 		require.NoError(t, processor.FinishCollection()) | ||||
| 
 | ||||
| 		// Verify one element
 | ||||
| 		records = processorTest.NewOutput(attribute.DefaultEncoder()) | ||||
| 		require.NoError(t, checkpointSet.ForEach(ekindSel, records.AddRecord)) | ||||
| 		require.EqualValues(t, map[string]float64{ | ||||
| 			"inst.sum/A=B/R=V": 10, | ||||
| 			"inst.sum/A=B/": 10, | ||||
| 		}, records.Map()) | ||||
| 	} | ||||
| } | ||||
|  | @ -442,7 +439,6 @@ func TestMultiObserverSum(t *testing.T) { | |||
| 		export.DeltaExportKindSelector(), | ||||
| 	} { | ||||
| 
 | ||||
| 		res := resource.NewSchemaless(attribute.String("R", "V")) | ||||
| 		desc := metric.NewDescriptor("observe.sum", sdkapi.SumObserverInstrumentKind, number.Int64Kind) | ||||
| 		selector := processorTest.AggregatorSelector() | ||||
| 
 | ||||
|  | @ -452,9 +448,9 @@ func TestMultiObserverSum(t *testing.T) { | |||
| 		for i := 1; i < 3; i++ { | ||||
| 			// Add i*10*3 times
 | ||||
| 			processor.StartCollection() | ||||
| 			_ = processor.Process(updateFor(t, &desc, selector, res, int64(i*10), attribute.String("A", "B"))) | ||||
| 			_ = processor.Process(updateFor(t, &desc, selector, res, int64(i*10), attribute.String("A", "B"))) | ||||
| 			_ = processor.Process(updateFor(t, &desc, selector, res, int64(i*10), attribute.String("A", "B"))) | ||||
| 			_ = processor.Process(updateFor(t, &desc, selector, int64(i*10), attribute.String("A", "B"))) | ||||
| 			_ = processor.Process(updateFor(t, &desc, selector, int64(i*10), attribute.String("A", "B"))) | ||||
| 			_ = processor.Process(updateFor(t, &desc, selector, int64(i*10), attribute.String("A", "B"))) | ||||
| 			require.NoError(t, processor.FinishCollection()) | ||||
| 
 | ||||
| 			// Multiplier is 1 for deltas, otherwise i.
 | ||||
|  | @ -467,7 +463,7 @@ func TestMultiObserverSum(t *testing.T) { | |||
| 			records := processorTest.NewOutput(attribute.DefaultEncoder()) | ||||
| 			require.NoError(t, checkpointSet.ForEach(ekindSel, records.AddRecord)) | ||||
| 			require.EqualValues(t, map[string]float64{ | ||||
| 				"observe.sum/A=B/R=V": float64(3 * 10 * multiplier), | ||||
| 				"observe.sum/A=B/": float64(3 * 10 * multiplier), | ||||
| 			}, records.Map()) | ||||
| 		} | ||||
| 	} | ||||
|  | @ -480,7 +476,7 @@ func TestSumObserverEndToEnd(t *testing.T) { | |||
| 		processorTest.AggregatorSelector(), | ||||
| 		eselector, | ||||
| 	) | ||||
| 	accum := sdk.NewAccumulator(proc, resource.Empty()) | ||||
| 	accum := sdk.NewAccumulator(proc) | ||||
| 	meter := metric.WrapMeterImpl(accum, "testing") | ||||
| 
 | ||||
| 	var calls int64 | ||||
|  | @ -502,7 +498,7 @@ func TestSumObserverEndToEnd(t *testing.T) { | |||
| 		require.NoError(t, proc.FinishCollection()) | ||||
| 
 | ||||
| 		exporter := processortest.New(eselector, attribute.DefaultEncoder()) | ||||
| 		require.NoError(t, exporter.Export(ctx, data)) | ||||
| 		require.NoError(t, exporter.Export(ctx, resource.Empty(), data)) | ||||
| 
 | ||||
| 		require.EqualValues(t, map[string]float64{ | ||||
| 			"observer.sum//": float64(i + 1), | ||||
|  |  | |||
|  | @ -220,7 +220,6 @@ func (o *Output) ForEach(_ export.ExportKindSelector, ff func(export.Record) err | |||
| 		if err := ff(export.NewRecord( | ||||
| 			key.desc, | ||||
| 			value.labels, | ||||
| 			value.resource, | ||||
| 			value.aggregator.Aggregation(), | ||||
| 			time.Time{}, | ||||
| 			time.Time{}, | ||||
|  | @ -236,10 +235,14 @@ func (o *Output) ForEach(_ export.ExportKindSelector, ff func(export.Record) err | |||
| // either the Sum() or the LastValue() of its Aggregation(), whichever
 | ||||
| // is defined.  Record timestamps are ignored.
 | ||||
| func (o *Output) AddRecord(rec export.Record) error { | ||||
| 	return o.AddRecordWithResource(rec, resource.Empty()) | ||||
| } | ||||
| 
 | ||||
| func (o *Output) AddRecordWithResource(rec export.Record, res *resource.Resource) error { | ||||
| 	key := mapKey{ | ||||
| 		desc:     rec.Descriptor(), | ||||
| 		labels:   rec.Labels().Equivalent(), | ||||
| 		resource: rec.Resource().Equivalent(), | ||||
| 		resource: res.Equivalent(), | ||||
| 	} | ||||
| 	if _, ok := o.m[key]; !ok { | ||||
| 		var agg export.Aggregator | ||||
|  | @ -247,7 +250,7 @@ func (o *Output) AddRecord(rec export.Record) error { | |||
| 		o.m[key] = mapValue{ | ||||
| 			aggregator: agg, | ||||
| 			labels:     rec.Labels(), | ||||
| 			resource:   rec.Resource(), | ||||
| 			resource:   res, | ||||
| 		} | ||||
| 	} | ||||
| 	return o.m[key].aggregator.Merge(rec.Aggregation().(export.Aggregator), rec.Descriptor()) | ||||
|  | @ -306,7 +309,6 @@ func (o *Output) AddAccumulation(acc export.Accumulation) error { | |||
| 		export.NewRecord( | ||||
| 			acc.Descriptor(), | ||||
| 			acc.Labels(), | ||||
| 			acc.Resource(), | ||||
| 			acc.Aggregator().Aggregation(), | ||||
| 			time.Time{}, | ||||
| 			time.Time{}, | ||||
|  | @ -330,7 +332,7 @@ func New(selector export.ExportKindSelector, encoder attribute.Encoder) *Exporte | |||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (e *Exporter) Export(_ context.Context, ckpt export.CheckpointSet) error { | ||||
| func (e *Exporter) Export(_ context.Context, res *resource.Resource, ckpt export.CheckpointSet) error { | ||||
| 	e.output.Lock() | ||||
| 	defer e.output.Unlock() | ||||
| 	e.exportCount++ | ||||
|  | @ -340,7 +342,7 @@ func (e *Exporter) Export(_ context.Context, ckpt export.CheckpointSet) error { | |||
| 				return err | ||||
| 			} | ||||
| 		} | ||||
| 		return e.output.AddRecord(r) | ||||
| 		return e.output.AddRecordWithResource(r, res) | ||||
| 	}) | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -30,10 +30,7 @@ import ( | |||
| 
 | ||||
| func generateTestData(proc export.Processor) { | ||||
| 	ctx := context.Background() | ||||
| 	accum := metricsdk.NewAccumulator( | ||||
| 		proc, | ||||
| 		resource.NewSchemaless(attribute.String("R", "V")), | ||||
| 	) | ||||
| 	accum := metricsdk.NewAccumulator(proc) | ||||
| 	meter := metric.WrapMeterImpl(accum, "testing") | ||||
| 
 | ||||
| 	counter := metric.Must(meter).NewFloat64Counter("counter.sum") | ||||
|  | @ -62,6 +59,7 @@ func TestProcessorTesting(t *testing.T) { | |||
| 
 | ||||
| 	generateTestData(checkpointer) | ||||
| 
 | ||||
| 	res := resource.NewSchemaless(attribute.String("R", "V")) | ||||
| 	expect := map[string]float64{ | ||||
| 		"counter.sum/K1=V1/R=V":  100, | ||||
| 		"counter.sum/K1=V2/R=V":  101, | ||||
|  | @ -69,16 +67,13 @@ func TestProcessorTesting(t *testing.T) { | |||
| 		"observer.sum/K1=V2/R=V": 11, | ||||
| 	} | ||||
| 
 | ||||
| 	// Validate the processor's checkpoint directly.
 | ||||
| 	require.EqualValues(t, expect, testProc.Values()) | ||||
| 
 | ||||
| 	// Export the data and validate it again.
 | ||||
| 	exporter := processorTest.New( | ||||
| 		export.StatelessExportKindSelector(), | ||||
| 		attribute.DefaultEncoder(), | ||||
| 	) | ||||
| 
 | ||||
| 	err := exporter.Export(context.Background(), checkpointer.CheckpointSet()) | ||||
| 	err := exporter.Export(context.Background(), res, checkpointer.CheckpointSet()) | ||||
| 	require.NoError(t, err) | ||||
| 	require.EqualValues(t, expect, exporter.Values()) | ||||
| } | ||||
|  |  | |||
|  | @ -60,7 +60,6 @@ func (p *Processor) Process(accum export.Accumulation) error { | |||
| 		export.NewAccumulation( | ||||
| 			accum.Descriptor(), | ||||
| 			&reduced, | ||||
| 			accum.Resource(), | ||||
| 			accum.Aggregator(), | ||||
| 		), | ||||
| 	) | ||||
|  |  | |||
|  | @ -75,15 +75,14 @@ func TestFilterProcessor(t *testing.T) { | |||
| 	) | ||||
| 	accum := metricsdk.NewAccumulator( | ||||
| 		reducer.New(testFilter{}, processorTest.Checkpointer(testProc)), | ||||
| 		resource.NewSchemaless(attribute.String("R", "V")), | ||||
| 	) | ||||
| 	generateData(accum) | ||||
| 
 | ||||
| 	accum.Collect(context.Background()) | ||||
| 
 | ||||
| 	require.EqualValues(t, map[string]float64{ | ||||
| 		"counter.sum/A=1,C=3/R=V":  200, | ||||
| 		"observer.sum/A=1,C=3/R=V": 20, | ||||
| 		"counter.sum/A=1,C=3/":  200, | ||||
| 		"observer.sum/A=1,C=3/": 20, | ||||
| 	}, testProc.Values()) | ||||
| } | ||||
| 
 | ||||
|  | @ -92,7 +91,6 @@ func TestFilterBasicProcessor(t *testing.T) { | |||
| 	basicProc := basic.New(processorTest.AggregatorSelector(), export.CumulativeExportKindSelector()) | ||||
| 	accum := metricsdk.NewAccumulator( | ||||
| 		reducer.New(testFilter{}, basicProc), | ||||
| 		resource.NewSchemaless(attribute.String("R", "V")), | ||||
| 	) | ||||
| 	exporter := processorTest.New(basicProc, attribute.DefaultEncoder()) | ||||
| 
 | ||||
|  | @ -104,7 +102,8 @@ func TestFilterBasicProcessor(t *testing.T) { | |||
| 		t.Error(err) | ||||
| 	} | ||||
| 
 | ||||
| 	require.NoError(t, exporter.Export(context.Background(), basicProc.CheckpointSet())) | ||||
| 	res := resource.NewSchemaless(attribute.String("R", "V")) | ||||
| 	require.NoError(t, exporter.Export(context.Background(), res, basicProc.CheckpointSet())) | ||||
| 
 | ||||
| 	require.EqualValues(t, map[string]float64{ | ||||
| 		"counter.sum/A=1,C=3/R=V":  200, | ||||
|  |  | |||
|  | @ -28,7 +28,6 @@ import ( | |||
| 	"go.opentelemetry.io/otel/metric/number" | ||||
| 	export "go.opentelemetry.io/otel/sdk/export/metric" | ||||
| 	"go.opentelemetry.io/otel/sdk/metric/aggregator" | ||||
| 	"go.opentelemetry.io/otel/sdk/resource" | ||||
| ) | ||||
| 
 | ||||
| type ( | ||||
|  | @ -64,9 +63,6 @@ type ( | |||
| 		// place for sorting during labels creation to avoid
 | ||||
| 		// allocation.  It is cleared after use.
 | ||||
| 		asyncSortSlice attribute.Sortable | ||||
| 
 | ||||
| 		// resource is applied to all records in this Accumulator.
 | ||||
| 		resource *resource.Resource | ||||
| 	} | ||||
| 
 | ||||
| 	syncInstrument struct { | ||||
|  | @ -304,11 +300,10 @@ func (s *syncInstrument) RecordOne(ctx context.Context, num number.Number, kvs [ | |||
| // processor will call Collect() when it receives a request to scrape
 | ||||
| // current metric values.  A push-based processor should configure its
 | ||||
| // own periodic collection.
 | ||||
| func NewAccumulator(processor export.Processor, resource *resource.Resource) *Accumulator { | ||||
| func NewAccumulator(processor export.Processor) *Accumulator { | ||||
| 	return &Accumulator{ | ||||
| 		processor:        processor, | ||||
| 		asyncInstruments: internal.NewAsyncInstrumentState(), | ||||
| 		resource:         resource, | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
|  | @ -437,7 +432,7 @@ func (m *Accumulator) checkpointRecord(r *record) int { | |||
| 		return 0 | ||||
| 	} | ||||
| 
 | ||||
| 	a := export.NewAccumulation(&r.inst.descriptor, r.labels, m.resource, r.checkpoint) | ||||
| 	a := export.NewAccumulation(&r.inst.descriptor, r.labels, r.checkpoint) | ||||
| 	err = m.processor.Process(a) | ||||
| 	if err != nil { | ||||
| 		otel.Handle(err) | ||||
|  | @ -455,7 +450,7 @@ func (m *Accumulator) checkpointAsync(a *asyncInstrument) int { | |||
| 		epochDiff := m.currentEpoch - lrec.observedEpoch | ||||
| 		if epochDiff == 0 { | ||||
| 			if lrec.observed != nil { | ||||
| 				a := export.NewAccumulation(&a.descriptor, lrec.labels, m.resource, lrec.observed) | ||||
| 				a := export.NewAccumulation(&a.descriptor, lrec.labels, lrec.observed) | ||||
| 				err := m.processor.Process(a) | ||||
| 				if err != nil { | ||||
| 					otel.Handle(err) | ||||
|  |  | |||
|  | @ -295,7 +295,7 @@ func stressTest(t *testing.T, impl testImpl) { | |||
| 	} | ||||
| 	cc := concurrency() | ||||
| 
 | ||||
| 	sdk := NewAccumulator(fixture, nil) | ||||
| 	sdk := NewAccumulator(fixture) | ||||
| 	meter := metric.WrapMeterImpl(sdk, "stress_test") | ||||
| 	fixture.wg.Add(cc + 1) | ||||
| 
 | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue