846 lines
		
	
	
		
			29 KiB
		
	
	
	
		
			Go
		
	
	
	
			
		
		
	
	
			846 lines
		
	
	
		
			29 KiB
		
	
	
	
		
			Go
		
	
	
	
| // Copyright The OpenTelemetry Authors
 | |
| //
 | |
| // Licensed under the Apache License, Version 2.0 (the "License");
 | |
| // you may not use this file except in compliance with the License.
 | |
| // You may obtain a copy of the License at
 | |
| //
 | |
| //       http://www.apache.org/licenses/LICENSE-2.0
 | |
| //
 | |
| // Unless required by applicable law or agreed to in writing, software
 | |
| // distributed under the License is distributed on an "AS IS" BASIS,
 | |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | |
| // See the License for the specific language governing permissions and
 | |
| // limitations under the License.
 | |
| 
 | |
| package batchprocessor
 | |
| 
 | |
| import (
 | |
| 	"context"
 | |
| 	"fmt"
 | |
| 	"math"
 | |
| 	"sync"
 | |
| 	"testing"
 | |
| 	"time"
 | |
| 
 | |
| 	"github.com/stretchr/testify/assert"
 | |
| 	"github.com/stretchr/testify/require"
 | |
| 	"go.opencensus.io/stats/view"
 | |
| 
 | |
| 	"go.opentelemetry.io/collector/component/componenttest"
 | |
| 	"go.opentelemetry.io/collector/config"
 | |
| 	"go.opentelemetry.io/collector/config/configtelemetry"
 | |
| 	"go.opentelemetry.io/collector/consumer"
 | |
| 	"go.opentelemetry.io/collector/consumer/consumertest"
 | |
| 	"go.opentelemetry.io/collector/internal/testdata"
 | |
| 	"go.opentelemetry.io/collector/pdata/plog"
 | |
| 	"go.opentelemetry.io/collector/pdata/pmetric"
 | |
| 	"go.opentelemetry.io/collector/pdata/ptrace"
 | |
| )
 | |
| 
 | |
| func TestBatchProcessorSpansDelivered(t *testing.T) {
 | |
| 	sink := new(consumertest.TracesSink)
 | |
| 	cfg := createDefaultConfig().(*Config)
 | |
| 	cfg.SendBatchSize = 128
 | |
| 	creationSet := componenttest.NewNopProcessorCreateSettings()
 | |
| 	batcher, err := newBatchTracesProcessor(creationSet, sink, cfg, configtelemetry.LevelDetailed)
 | |
| 	require.NoError(t, err)
 | |
| 	require.NoError(t, batcher.Start(context.Background(), componenttest.NewNopHost()))
 | |
| 
 | |
| 	requestCount := 1000
 | |
| 	spansPerRequest := 100
 | |
| 	sentResourceSpans := ptrace.NewTraces().ResourceSpans()
 | |
| 	for requestNum := 0; requestNum < requestCount; requestNum++ {
 | |
| 		td := testdata.GenerateTraces(spansPerRequest)
 | |
| 		spans := td.ResourceSpans().At(0).ScopeSpans().At(0).Spans()
 | |
| 		for spanIndex := 0; spanIndex < spansPerRequest; spanIndex++ {
 | |
| 			spans.At(spanIndex).SetName(getTestSpanName(requestNum, spanIndex))
 | |
| 		}
 | |
| 		td.ResourceSpans().At(0).CopyTo(sentResourceSpans.AppendEmpty())
 | |
| 		assert.NoError(t, batcher.ConsumeTraces(context.Background(), td))
 | |
| 	}
 | |
| 
 | |
| 	// Added to test logic that check for empty resources.
 | |
| 	td := ptrace.NewTraces()
 | |
| 	assert.NoError(t, batcher.ConsumeTraces(context.Background(), td))
 | |
| 
 | |
| 	require.NoError(t, batcher.Shutdown(context.Background()))
 | |
| 
 | |
| 	require.Equal(t, requestCount*spansPerRequest, sink.SpanCount())
 | |
| 	receivedTraces := sink.AllTraces()
 | |
| 	spansReceivedByName := spansReceivedByName(receivedTraces)
 | |
| 	for requestNum := 0; requestNum < requestCount; requestNum++ {
 | |
| 		spans := sentResourceSpans.At(requestNum).ScopeSpans().At(0).Spans()
 | |
| 		for spanIndex := 0; spanIndex < spansPerRequest; spanIndex++ {
 | |
| 			require.EqualValues(t,
 | |
| 				spans.At(spanIndex),
 | |
| 				spansReceivedByName[getTestSpanName(requestNum, spanIndex)])
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func TestBatchProcessorSpansDeliveredEnforceBatchSize(t *testing.T) {
 | |
| 	sink := new(consumertest.TracesSink)
 | |
| 	cfg := createDefaultConfig().(*Config)
 | |
| 	cfg.SendBatchSize = 128
 | |
| 	cfg.SendBatchMaxSize = 130
 | |
| 	creationSet := componenttest.NewNopProcessorCreateSettings()
 | |
| 	batcher, err := newBatchTracesProcessor(creationSet, sink, cfg, configtelemetry.LevelBasic)
 | |
| 	require.NoError(t, err)
 | |
| 	require.NoError(t, batcher.Start(context.Background(), componenttest.NewNopHost()))
 | |
| 
 | |
| 	requestCount := 1000
 | |
| 	spansPerRequest := 150
 | |
| 	for requestNum := 0; requestNum < requestCount; requestNum++ {
 | |
| 		td := testdata.GenerateTraces(spansPerRequest)
 | |
| 		spans := td.ResourceSpans().At(0).ScopeSpans().At(0).Spans()
 | |
| 		for spanIndex := 0; spanIndex < spansPerRequest; spanIndex++ {
 | |
| 			spans.At(spanIndex).SetName(getTestSpanName(requestNum, spanIndex))
 | |
| 		}
 | |
| 		assert.NoError(t, batcher.ConsumeTraces(context.Background(), td))
 | |
| 	}
 | |
| 
 | |
| 	// Added to test logic that check for empty resources.
 | |
| 	td := ptrace.NewTraces()
 | |
| 	require.NoError(t, batcher.ConsumeTraces(context.Background(), td))
 | |
| 
 | |
| 	// wait for all spans to be reported
 | |
| 	for {
 | |
| 		if sink.SpanCount() == requestCount*spansPerRequest {
 | |
| 			break
 | |
| 		}
 | |
| 		<-time.After(cfg.Timeout)
 | |
| 	}
 | |
| 
 | |
| 	require.NoError(t, batcher.Shutdown(context.Background()))
 | |
| 
 | |
| 	require.Equal(t, requestCount*spansPerRequest, sink.SpanCount())
 | |
| 	for i := 0; i < len(sink.AllTraces())-1; i++ {
 | |
| 		assert.Equal(t, int(cfg.SendBatchMaxSize), sink.AllTraces()[i].SpanCount())
 | |
| 	}
 | |
| 	// the last batch has the remaining size
 | |
| 	assert.Equal(t, (requestCount*spansPerRequest)%int(cfg.SendBatchMaxSize), sink.AllTraces()[len(sink.AllTraces())-1].SpanCount())
 | |
| }
 | |
| 
 | |
| func TestBatchProcessorSentBySize(t *testing.T) {
 | |
| 	sizer := &ptrace.ProtoMarshaler{}
 | |
| 	views := MetricViews()
 | |
| 	require.NoError(t, view.Register(views...))
 | |
| 	defer view.Unregister(views...)
 | |
| 
 | |
| 	sink := new(consumertest.TracesSink)
 | |
| 	cfg := createDefaultConfig().(*Config)
 | |
| 	sendBatchSize := 20
 | |
| 	cfg.SendBatchSize = uint32(sendBatchSize)
 | |
| 	cfg.Timeout = 500 * time.Millisecond
 | |
| 	creationSet := componenttest.NewNopProcessorCreateSettings()
 | |
| 	batcher, err := newBatchTracesProcessor(creationSet, sink, cfg, configtelemetry.LevelDetailed)
 | |
| 	require.NoError(t, err)
 | |
| 	require.NoError(t, batcher.Start(context.Background(), componenttest.NewNopHost()))
 | |
| 
 | |
| 	requestCount := 100
 | |
| 	spansPerRequest := 5
 | |
| 
 | |
| 	start := time.Now()
 | |
| 	sizeSum := 0
 | |
| 	for requestNum := 0; requestNum < requestCount; requestNum++ {
 | |
| 		td := testdata.GenerateTraces(spansPerRequest)
 | |
| 		sizeSum += sizer.TracesSize(td)
 | |
| 		assert.NoError(t, batcher.ConsumeTraces(context.Background(), td))
 | |
| 	}
 | |
| 
 | |
| 	require.NoError(t, batcher.Shutdown(context.Background()))
 | |
| 
 | |
| 	elapsed := time.Since(start)
 | |
| 	require.LessOrEqual(t, elapsed.Nanoseconds(), cfg.Timeout.Nanoseconds())
 | |
| 
 | |
| 	expectedBatchesNum := requestCount * spansPerRequest / sendBatchSize
 | |
| 	expectedBatchingFactor := sendBatchSize / spansPerRequest
 | |
| 
 | |
| 	require.Equal(t, requestCount*spansPerRequest, sink.SpanCount())
 | |
| 	receivedTraces := sink.AllTraces()
 | |
| 	require.EqualValues(t, expectedBatchesNum, len(receivedTraces))
 | |
| 	for _, td := range receivedTraces {
 | |
| 		rss := td.ResourceSpans()
 | |
| 		require.Equal(t, expectedBatchingFactor, rss.Len())
 | |
| 		for i := 0; i < expectedBatchingFactor; i++ {
 | |
| 			require.Equal(t, spansPerRequest, rss.At(i).ScopeSpans().At(0).Spans().Len())
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	viewData, err := view.RetrieveData("processor/batch/" + statBatchSendSize.Name())
 | |
| 	require.NoError(t, err)
 | |
| 	assert.Equal(t, 1, len(viewData))
 | |
| 	distData := viewData[0].Data.(*view.DistributionData)
 | |
| 	assert.Equal(t, int64(expectedBatchesNum), distData.Count)
 | |
| 	assert.Equal(t, sink.SpanCount(), int(distData.Sum()))
 | |
| 	assert.Equal(t, sendBatchSize, int(distData.Min))
 | |
| 	assert.Equal(t, sendBatchSize, int(distData.Max))
 | |
| 
 | |
| 	viewData, err = view.RetrieveData("processor/batch/" + statBatchSendSizeBytes.Name())
 | |
| 	require.NoError(t, err)
 | |
| 	assert.Equal(t, 1, len(viewData))
 | |
| 	distData = viewData[0].Data.(*view.DistributionData)
 | |
| 	assert.Equal(t, int64(expectedBatchesNum), distData.Count)
 | |
| 	assert.Equal(t, sizeSum, int(distData.Sum()))
 | |
| }
 | |
| 
 | |
| func TestBatchProcessorSentBySize_withMaxSize(t *testing.T) {
 | |
| 	views := MetricViews()
 | |
| 	require.NoError(t, view.Register(views...))
 | |
| 	defer view.Unregister(views...)
 | |
| 
 | |
| 	sink := new(consumertest.TracesSink)
 | |
| 	cfg := createDefaultConfig().(*Config)
 | |
| 	sendBatchSize := 20
 | |
| 	sendBatchMaxSize := 37
 | |
| 	cfg.SendBatchSize = uint32(sendBatchSize)
 | |
| 	cfg.SendBatchMaxSize = uint32(sendBatchMaxSize)
 | |
| 	cfg.Timeout = 500 * time.Millisecond
 | |
| 	creationSet := componenttest.NewNopProcessorCreateSettings()
 | |
| 	batcher, err := newBatchTracesProcessor(creationSet, sink, cfg, configtelemetry.LevelDetailed)
 | |
| 	require.NoError(t, err)
 | |
| 	require.NoError(t, batcher.Start(context.Background(), componenttest.NewNopHost()))
 | |
| 
 | |
| 	requestCount := 1
 | |
| 	spansPerRequest := 500
 | |
| 	totalSpans := requestCount * spansPerRequest
 | |
| 
 | |
| 	start := time.Now()
 | |
| 	for requestNum := 0; requestNum < requestCount; requestNum++ {
 | |
| 		td := testdata.GenerateTraces(spansPerRequest)
 | |
| 		assert.NoError(t, batcher.ConsumeTraces(context.Background(), td))
 | |
| 	}
 | |
| 
 | |
| 	require.NoError(t, batcher.Shutdown(context.Background()))
 | |
| 
 | |
| 	elapsed := time.Since(start)
 | |
| 	require.LessOrEqual(t, elapsed.Nanoseconds(), cfg.Timeout.Nanoseconds())
 | |
| 
 | |
| 	// The max batch size is not a divisor of the total number of spans
 | |
| 	expectedBatchesNum := int(math.Ceil(float64(totalSpans) / float64(sendBatchMaxSize)))
 | |
| 
 | |
| 	require.Equal(t, totalSpans, sink.SpanCount())
 | |
| 	receivedTraces := sink.AllTraces()
 | |
| 	require.EqualValues(t, expectedBatchesNum, len(receivedTraces))
 | |
| 
 | |
| 	viewData, err := view.RetrieveData("processor/batch/" + statBatchSendSize.Name())
 | |
| 	require.NoError(t, err)
 | |
| 	assert.Equal(t, 1, len(viewData))
 | |
| 	distData := viewData[0].Data.(*view.DistributionData)
 | |
| 	assert.Equal(t, int64(expectedBatchesNum), distData.Count)
 | |
| 	assert.Equal(t, sink.SpanCount(), int(distData.Sum()))
 | |
| 	assert.Equal(t, totalSpans%sendBatchMaxSize, int(distData.Min))
 | |
| 	assert.Equal(t, sendBatchMaxSize, int(distData.Max))
 | |
| }
 | |
| 
 | |
| func TestBatchProcessorSentByTimeout(t *testing.T) {
 | |
| 	sink := new(consumertest.TracesSink)
 | |
| 	cfg := createDefaultConfig().(*Config)
 | |
| 	sendBatchSize := 100
 | |
| 	cfg.SendBatchSize = uint32(sendBatchSize)
 | |
| 	cfg.Timeout = 100 * time.Millisecond
 | |
| 	creationSet := componenttest.NewNopProcessorCreateSettings()
 | |
| 
 | |
| 	requestCount := 5
 | |
| 	spansPerRequest := 10
 | |
| 	start := time.Now()
 | |
| 
 | |
| 	batcher, err := newBatchTracesProcessor(creationSet, sink, cfg, configtelemetry.LevelDetailed)
 | |
| 	require.NoError(t, err)
 | |
| 	require.NoError(t, batcher.Start(context.Background(), componenttest.NewNopHost()))
 | |
| 
 | |
| 	for requestNum := 0; requestNum < requestCount; requestNum++ {
 | |
| 		td := testdata.GenerateTraces(spansPerRequest)
 | |
| 		assert.NoError(t, batcher.ConsumeTraces(context.Background(), td))
 | |
| 	}
 | |
| 
 | |
| 	// Wait for at least one batch to be sent.
 | |
| 	for {
 | |
| 		if sink.SpanCount() != 0 {
 | |
| 			break
 | |
| 		}
 | |
| 		<-time.After(cfg.Timeout)
 | |
| 	}
 | |
| 
 | |
| 	elapsed := time.Since(start)
 | |
| 	require.LessOrEqual(t, cfg.Timeout.Nanoseconds(), elapsed.Nanoseconds())
 | |
| 
 | |
| 	// This should not change the results in the sink, verified by the expectedBatchesNum
 | |
| 	require.NoError(t, batcher.Shutdown(context.Background()))
 | |
| 
 | |
| 	expectedBatchesNum := 1
 | |
| 	expectedBatchingFactor := 5
 | |
| 
 | |
| 	require.Equal(t, requestCount*spansPerRequest, sink.SpanCount())
 | |
| 	receivedTraces := sink.AllTraces()
 | |
| 	require.EqualValues(t, expectedBatchesNum, len(receivedTraces))
 | |
| 	for _, td := range receivedTraces {
 | |
| 		rss := td.ResourceSpans()
 | |
| 		require.Equal(t, expectedBatchingFactor, rss.Len())
 | |
| 		for i := 0; i < expectedBatchingFactor; i++ {
 | |
| 			require.Equal(t, spansPerRequest, rss.At(i).ScopeSpans().At(0).Spans().Len())
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func TestBatchProcessorTraceSendWhenClosing(t *testing.T) {
 | |
| 	cfg := Config{
 | |
| 		ProcessorSettings: config.NewProcessorSettings(config.NewComponentID(typeStr)),
 | |
| 		Timeout:           3 * time.Second,
 | |
| 		SendBatchSize:     1000,
 | |
| 	}
 | |
| 	sink := new(consumertest.TracesSink)
 | |
| 
 | |
| 	creationSet := componenttest.NewNopProcessorCreateSettings()
 | |
| 	batcher, err := newBatchTracesProcessor(creationSet, sink, &cfg, configtelemetry.LevelDetailed)
 | |
| 	require.NoError(t, err)
 | |
| 	require.NoError(t, batcher.Start(context.Background(), componenttest.NewNopHost()))
 | |
| 
 | |
| 	requestCount := 10
 | |
| 	spansPerRequest := 10
 | |
| 	for requestNum := 0; requestNum < requestCount; requestNum++ {
 | |
| 		td := testdata.GenerateTraces(spansPerRequest)
 | |
| 		assert.NoError(t, batcher.ConsumeTraces(context.Background(), td))
 | |
| 	}
 | |
| 
 | |
| 	require.NoError(t, batcher.Shutdown(context.Background()))
 | |
| 
 | |
| 	require.Equal(t, requestCount*spansPerRequest, sink.SpanCount())
 | |
| 	require.Equal(t, 1, len(sink.AllTraces()))
 | |
| }
 | |
| 
 | |
| func TestBatchMetricProcessor_ReceivingData(t *testing.T) {
 | |
| 	// Instantiate the batch processor with low config values to test data
 | |
| 	// gets sent through the processor.
 | |
| 	cfg := Config{
 | |
| 		ProcessorSettings: config.NewProcessorSettings(config.NewComponentID(typeStr)),
 | |
| 		Timeout:           200 * time.Millisecond,
 | |
| 		SendBatchSize:     50,
 | |
| 	}
 | |
| 
 | |
| 	requestCount := 100
 | |
| 	metricsPerRequest := 5
 | |
| 	sink := new(consumertest.MetricsSink)
 | |
| 
 | |
| 	creationSet := componenttest.NewNopProcessorCreateSettings()
 | |
| 	batcher, err := newBatchMetricsProcessor(creationSet, sink, &cfg, configtelemetry.LevelDetailed)
 | |
| 	require.NoError(t, err)
 | |
| 	require.NoError(t, batcher.Start(context.Background(), componenttest.NewNopHost()))
 | |
| 
 | |
| 	sentResourceMetrics := pmetric.NewMetrics().ResourceMetrics()
 | |
| 
 | |
| 	for requestNum := 0; requestNum < requestCount; requestNum++ {
 | |
| 		md := testdata.GenerateMetrics(metricsPerRequest)
 | |
| 		metrics := md.ResourceMetrics().At(0).ScopeMetrics().At(0).Metrics()
 | |
| 		for metricIndex := 0; metricIndex < metricsPerRequest; metricIndex++ {
 | |
| 			metrics.At(metricIndex).SetName(getTestMetricName(requestNum, metricIndex))
 | |
| 		}
 | |
| 		md.ResourceMetrics().At(0).CopyTo(sentResourceMetrics.AppendEmpty())
 | |
| 		assert.NoError(t, batcher.ConsumeMetrics(context.Background(), md))
 | |
| 	}
 | |
| 
 | |
| 	// Added to test case with empty resources sent.
 | |
| 	md := pmetric.NewMetrics()
 | |
| 	assert.NoError(t, batcher.ConsumeMetrics(context.Background(), md))
 | |
| 
 | |
| 	require.NoError(t, batcher.Shutdown(context.Background()))
 | |
| 
 | |
| 	require.Equal(t, requestCount*2*metricsPerRequest, sink.DataPointCount())
 | |
| 	receivedMds := sink.AllMetrics()
 | |
| 	metricsReceivedByName := metricsReceivedByName(receivedMds)
 | |
| 	for requestNum := 0; requestNum < requestCount; requestNum++ {
 | |
| 		metrics := sentResourceMetrics.At(requestNum).ScopeMetrics().At(0).Metrics()
 | |
| 		for metricIndex := 0; metricIndex < metricsPerRequest; metricIndex++ {
 | |
| 			require.EqualValues(t,
 | |
| 				metrics.At(metricIndex),
 | |
| 				metricsReceivedByName[getTestMetricName(requestNum, metricIndex)])
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func TestBatchMetricProcessor_BatchSize(t *testing.T) {
 | |
| 	sizer := &pmetric.ProtoMarshaler{}
 | |
| 	views := MetricViews()
 | |
| 	require.NoError(t, view.Register(views...))
 | |
| 	defer view.Unregister(views...)
 | |
| 
 | |
| 	// Instantiate the batch processor with low config values to test data
 | |
| 	// gets sent through the processor.
 | |
| 	cfg := Config{
 | |
| 		ProcessorSettings: config.NewProcessorSettings(config.NewComponentID(typeStr)),
 | |
| 		Timeout:           100 * time.Millisecond,
 | |
| 		SendBatchSize:     50,
 | |
| 	}
 | |
| 
 | |
| 	requestCount := 100
 | |
| 	metricsPerRequest := 5
 | |
| 	dataPointsPerMetric := 2 // Since the int counter uses two datapoints.
 | |
| 	dataPointsPerRequest := metricsPerRequest * dataPointsPerMetric
 | |
| 	sink := new(consumertest.MetricsSink)
 | |
| 
 | |
| 	creationSet := componenttest.NewNopProcessorCreateSettings()
 | |
| 	batcher, err := newBatchMetricsProcessor(creationSet, sink, &cfg, configtelemetry.LevelDetailed)
 | |
| 	require.NoError(t, err)
 | |
| 	require.NoError(t, batcher.Start(context.Background(), componenttest.NewNopHost()))
 | |
| 
 | |
| 	start := time.Now()
 | |
| 	size := 0
 | |
| 	for requestNum := 0; requestNum < requestCount; requestNum++ {
 | |
| 		md := testdata.GenerateMetrics(metricsPerRequest)
 | |
| 		size += sizer.MetricsSize(md)
 | |
| 		assert.NoError(t, batcher.ConsumeMetrics(context.Background(), md))
 | |
| 	}
 | |
| 	require.NoError(t, batcher.Shutdown(context.Background()))
 | |
| 
 | |
| 	elapsed := time.Since(start)
 | |
| 	require.LessOrEqual(t, elapsed.Nanoseconds(), cfg.Timeout.Nanoseconds())
 | |
| 
 | |
| 	expectedBatchesNum := requestCount * dataPointsPerRequest / int(cfg.SendBatchSize)
 | |
| 	expectedBatchingFactor := int(cfg.SendBatchSize) / dataPointsPerRequest
 | |
| 
 | |
| 	require.Equal(t, requestCount*2*metricsPerRequest, sink.DataPointCount())
 | |
| 	receivedMds := sink.AllMetrics()
 | |
| 	require.Equal(t, expectedBatchesNum, len(receivedMds))
 | |
| 	for _, md := range receivedMds {
 | |
| 		require.Equal(t, expectedBatchingFactor, md.ResourceMetrics().Len())
 | |
| 		for i := 0; i < expectedBatchingFactor; i++ {
 | |
| 			require.Equal(t, metricsPerRequest, md.ResourceMetrics().At(i).ScopeMetrics().At(0).Metrics().Len())
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	viewData, err := view.RetrieveData("processor/batch/" + statBatchSendSize.Name())
 | |
| 	require.NoError(t, err)
 | |
| 	assert.Equal(t, 1, len(viewData))
 | |
| 	distData := viewData[0].Data.(*view.DistributionData)
 | |
| 	assert.Equal(t, int64(expectedBatchesNum), distData.Count)
 | |
| 	assert.Equal(t, sink.DataPointCount(), int(distData.Sum()))
 | |
| 	assert.Equal(t, cfg.SendBatchSize, uint32(distData.Min))
 | |
| 	assert.Equal(t, cfg.SendBatchSize, uint32(distData.Max))
 | |
| 
 | |
| 	viewData, err = view.RetrieveData("processor/batch/" + statBatchSendSizeBytes.Name())
 | |
| 	require.NoError(t, err)
 | |
| 	assert.Equal(t, 1, len(viewData))
 | |
| 	distData = viewData[0].Data.(*view.DistributionData)
 | |
| 	assert.Equal(t, int64(expectedBatchesNum), distData.Count)
 | |
| 	assert.Equal(t, size, int(distData.Sum()))
 | |
| }
 | |
| 
 | |
| func TestBatchMetrics_UnevenBatchMaxSize(t *testing.T) {
 | |
| 	ctx := context.Background()
 | |
| 	sink := new(metricsSink)
 | |
| 	metricsCount := 50
 | |
| 	dataPointsPerMetric := 2
 | |
| 	sendBatchMaxSize := 99
 | |
| 
 | |
| 	batchMetrics := newBatchMetrics(sink)
 | |
| 	md := testdata.GenerateMetrics(metricsCount)
 | |
| 
 | |
| 	batchMetrics.add(md)
 | |
| 	require.Equal(t, dataPointsPerMetric*metricsCount, batchMetrics.dataPointCount)
 | |
| 	sent, _, sendErr := batchMetrics.export(ctx, sendBatchMaxSize, false)
 | |
| 	require.NoError(t, sendErr)
 | |
| 	require.Equal(t, sendBatchMaxSize, sent)
 | |
| 	remainingDataPointCount := metricsCount*dataPointsPerMetric - sendBatchMaxSize
 | |
| 	require.Equal(t, remainingDataPointCount, batchMetrics.dataPointCount)
 | |
| }
 | |
| 
 | |
| func TestBatchMetricsProcessor_Timeout(t *testing.T) {
 | |
| 	cfg := Config{
 | |
| 		ProcessorSettings: config.NewProcessorSettings(config.NewComponentID(typeStr)),
 | |
| 		Timeout:           100 * time.Millisecond,
 | |
| 		SendBatchSize:     101,
 | |
| 	}
 | |
| 	requestCount := 5
 | |
| 	metricsPerRequest := 10
 | |
| 	sink := new(consumertest.MetricsSink)
 | |
| 
 | |
| 	creationSet := componenttest.NewNopProcessorCreateSettings()
 | |
| 	batcher, err := newBatchMetricsProcessor(creationSet, sink, &cfg, configtelemetry.LevelDetailed)
 | |
| 	require.NoError(t, err)
 | |
| 	require.NoError(t, batcher.Start(context.Background(), componenttest.NewNopHost()))
 | |
| 
 | |
| 	start := time.Now()
 | |
| 	for requestNum := 0; requestNum < requestCount; requestNum++ {
 | |
| 		md := testdata.GenerateMetrics(metricsPerRequest)
 | |
| 		assert.NoError(t, batcher.ConsumeMetrics(context.Background(), md))
 | |
| 	}
 | |
| 
 | |
| 	// Wait for at least one batch to be sent.
 | |
| 	for {
 | |
| 		if sink.DataPointCount() != 0 {
 | |
| 			break
 | |
| 		}
 | |
| 		<-time.After(cfg.Timeout)
 | |
| 	}
 | |
| 
 | |
| 	elapsed := time.Since(start)
 | |
| 	require.LessOrEqual(t, cfg.Timeout.Nanoseconds(), elapsed.Nanoseconds())
 | |
| 
 | |
| 	// This should not change the results in the sink, verified by the expectedBatchesNum
 | |
| 	require.NoError(t, batcher.Shutdown(context.Background()))
 | |
| 
 | |
| 	expectedBatchesNum := 1
 | |
| 	expectedBatchingFactor := 5
 | |
| 
 | |
| 	require.Equal(t, requestCount*2*metricsPerRequest, sink.DataPointCount())
 | |
| 	receivedMds := sink.AllMetrics()
 | |
| 	require.Equal(t, expectedBatchesNum, len(receivedMds))
 | |
| 	for _, md := range receivedMds {
 | |
| 		require.Equal(t, expectedBatchingFactor, md.ResourceMetrics().Len())
 | |
| 		for i := 0; i < expectedBatchingFactor; i++ {
 | |
| 			require.Equal(t, metricsPerRequest, md.ResourceMetrics().At(i).ScopeMetrics().At(0).Metrics().Len())
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func TestBatchMetricProcessor_Shutdown(t *testing.T) {
 | |
| 	cfg := Config{
 | |
| 		ProcessorSettings: config.NewProcessorSettings(config.NewComponentID(typeStr)),
 | |
| 		Timeout:           3 * time.Second,
 | |
| 		SendBatchSize:     1000,
 | |
| 	}
 | |
| 	requestCount := 5
 | |
| 	metricsPerRequest := 10
 | |
| 	sink := new(consumertest.MetricsSink)
 | |
| 
 | |
| 	creationSet := componenttest.NewNopProcessorCreateSettings()
 | |
| 	batcher, err := newBatchMetricsProcessor(creationSet, sink, &cfg, configtelemetry.LevelDetailed)
 | |
| 	require.NoError(t, err)
 | |
| 	require.NoError(t, batcher.Start(context.Background(), componenttest.NewNopHost()))
 | |
| 
 | |
| 	for requestNum := 0; requestNum < requestCount; requestNum++ {
 | |
| 		md := testdata.GenerateMetrics(metricsPerRequest)
 | |
| 		assert.NoError(t, batcher.ConsumeMetrics(context.Background(), md))
 | |
| 	}
 | |
| 
 | |
| 	require.NoError(t, batcher.Shutdown(context.Background()))
 | |
| 
 | |
| 	require.Equal(t, requestCount*2*metricsPerRequest, sink.DataPointCount())
 | |
| 	require.Equal(t, 1, len(sink.AllMetrics()))
 | |
| }
 | |
| 
 | |
| func getTestSpanName(requestNum, index int) string {
 | |
| 	return fmt.Sprintf("test-span-%d-%d", requestNum, index)
 | |
| }
 | |
| 
 | |
| func spansReceivedByName(tds []ptrace.Traces) map[string]ptrace.Span {
 | |
| 	spansReceivedByName := map[string]ptrace.Span{}
 | |
| 	for i := range tds {
 | |
| 		rss := tds[i].ResourceSpans()
 | |
| 		for i := 0; i < rss.Len(); i++ {
 | |
| 			ilss := rss.At(i).ScopeSpans()
 | |
| 			for j := 0; j < ilss.Len(); j++ {
 | |
| 				spans := ilss.At(j).Spans()
 | |
| 				for k := 0; k < spans.Len(); k++ {
 | |
| 					span := spans.At(k)
 | |
| 					spansReceivedByName[spans.At(k).Name()] = span
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 	return spansReceivedByName
 | |
| }
 | |
| 
 | |
| func metricsReceivedByName(mds []pmetric.Metrics) map[string]pmetric.Metric {
 | |
| 	metricsReceivedByName := map[string]pmetric.Metric{}
 | |
| 	for _, md := range mds {
 | |
| 		rms := md.ResourceMetrics()
 | |
| 		for i := 0; i < rms.Len(); i++ {
 | |
| 			ilms := rms.At(i).ScopeMetrics()
 | |
| 			for j := 0; j < ilms.Len(); j++ {
 | |
| 				metrics := ilms.At(j).Metrics()
 | |
| 				for k := 0; k < metrics.Len(); k++ {
 | |
| 					metric := metrics.At(k)
 | |
| 					metricsReceivedByName[metric.Name()] = metric
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 	return metricsReceivedByName
 | |
| }
 | |
| 
 | |
| func getTestMetricName(requestNum, index int) string {
 | |
| 	return fmt.Sprintf("test-metric-int-%d-%d", requestNum, index)
 | |
| }
 | |
| 
 | |
| func BenchmarkTraceSizeBytes(b *testing.B) {
 | |
| 	sizer := &ptrace.ProtoMarshaler{}
 | |
| 	td := testdata.GenerateTraces(8192)
 | |
| 	for n := 0; n < b.N; n++ {
 | |
| 		fmt.Println(sizer.TracesSize(td))
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func BenchmarkTraceSizeSpanCount(b *testing.B) {
 | |
| 	td := testdata.GenerateTraces(8192)
 | |
| 	for n := 0; n < b.N; n++ {
 | |
| 		td.SpanCount()
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func BenchmarkBatchMetricProcessor(b *testing.B) {
 | |
| 	b.StopTimer()
 | |
| 	cfg := Config{
 | |
| 		ProcessorSettings: config.NewProcessorSettings(config.NewComponentID(typeStr)),
 | |
| 		Timeout:           100 * time.Millisecond,
 | |
| 		SendBatchSize:     2000,
 | |
| 	}
 | |
| 	ctx := context.Background()
 | |
| 	sink := new(metricsSink)
 | |
| 	creationSet := componenttest.NewNopProcessorCreateSettings()
 | |
| 	metricsPerRequest := 1000
 | |
| 
 | |
| 	batcher, err := newBatchMetricsProcessor(creationSet, sink, &cfg, configtelemetry.LevelDetailed)
 | |
| 	require.NoError(b, err)
 | |
| 	require.NoError(b, batcher.Start(ctx, componenttest.NewNopHost()))
 | |
| 
 | |
| 	mds := make([]pmetric.Metrics, 0, b.N)
 | |
| 	for n := 0; n < b.N; n++ {
 | |
| 		mds = append(mds,
 | |
| 			testdata.GenerateMetrics(metricsPerRequest),
 | |
| 		)
 | |
| 	}
 | |
| 	b.StartTimer()
 | |
| 	for n := 0; n < b.N; n++ {
 | |
| 		require.NoError(b, batcher.ConsumeMetrics(ctx, mds[n]))
 | |
| 	}
 | |
| 	b.StopTimer()
 | |
| 	require.NoError(b, batcher.Shutdown(ctx))
 | |
| 	require.Equal(b, b.N*metricsPerRequest, sink.metricsCount)
 | |
| }
 | |
| 
 | |
| type metricsSink struct {
 | |
| 	mu           sync.Mutex
 | |
| 	metricsCount int
 | |
| }
 | |
| 
 | |
| func (sme *metricsSink) Capabilities() consumer.Capabilities {
 | |
| 	return consumer.Capabilities{
 | |
| 		MutatesData: false,
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func (sme *metricsSink) ConsumeMetrics(_ context.Context, md pmetric.Metrics) error {
 | |
| 	sme.mu.Lock()
 | |
| 	defer sme.mu.Unlock()
 | |
| 	sme.metricsCount += md.MetricCount()
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func TestBatchLogProcessor_ReceivingData(t *testing.T) {
 | |
| 	// Instantiate the batch processor with low config values to test data
 | |
| 	// gets sent through the processor.
 | |
| 	cfg := Config{
 | |
| 		ProcessorSettings: config.NewProcessorSettings(config.NewComponentID(typeStr)),
 | |
| 		Timeout:           200 * time.Millisecond,
 | |
| 		SendBatchSize:     50,
 | |
| 	}
 | |
| 
 | |
| 	requestCount := 100
 | |
| 	logsPerRequest := 5
 | |
| 	sink := new(consumertest.LogsSink)
 | |
| 
 | |
| 	creationSet := componenttest.NewNopProcessorCreateSettings()
 | |
| 	batcher, err := newBatchLogsProcessor(creationSet, sink, &cfg, configtelemetry.LevelDetailed)
 | |
| 	require.NoError(t, err)
 | |
| 	require.NoError(t, batcher.Start(context.Background(), componenttest.NewNopHost()))
 | |
| 
 | |
| 	sentResourceLogs := plog.NewLogs().ResourceLogs()
 | |
| 
 | |
| 	for requestNum := 0; requestNum < requestCount; requestNum++ {
 | |
| 		ld := testdata.GenerateLogs(logsPerRequest)
 | |
| 		logs := ld.ResourceLogs().At(0).ScopeLogs().At(0).LogRecords()
 | |
| 		for logIndex := 0; logIndex < logsPerRequest; logIndex++ {
 | |
| 			logs.At(logIndex).SetSeverityText(getTestLogSeverityText(requestNum, logIndex))
 | |
| 		}
 | |
| 		ld.ResourceLogs().At(0).CopyTo(sentResourceLogs.AppendEmpty())
 | |
| 		assert.NoError(t, batcher.ConsumeLogs(context.Background(), ld))
 | |
| 	}
 | |
| 
 | |
| 	// Added to test case with empty resources sent.
 | |
| 	ld := plog.NewLogs()
 | |
| 	assert.NoError(t, batcher.ConsumeLogs(context.Background(), ld))
 | |
| 
 | |
| 	require.NoError(t, batcher.Shutdown(context.Background()))
 | |
| 
 | |
| 	require.Equal(t, requestCount*logsPerRequest, sink.LogRecordCount())
 | |
| 	receivedMds := sink.AllLogs()
 | |
| 	logsReceivedBySeverityText := logsReceivedBySeverityText(receivedMds)
 | |
| 	for requestNum := 0; requestNum < requestCount; requestNum++ {
 | |
| 		logs := sentResourceLogs.At(requestNum).ScopeLogs().At(0).LogRecords()
 | |
| 		for logIndex := 0; logIndex < logsPerRequest; logIndex++ {
 | |
| 			require.EqualValues(t,
 | |
| 				logs.At(logIndex),
 | |
| 				logsReceivedBySeverityText[getTestLogSeverityText(requestNum, logIndex)])
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func TestBatchLogProcessor_BatchSize(t *testing.T) {
 | |
| 	sizer := &plog.ProtoMarshaler{}
 | |
| 	views := MetricViews()
 | |
| 	require.NoError(t, view.Register(views...))
 | |
| 	defer view.Unregister(views...)
 | |
| 
 | |
| 	// Instantiate the batch processor with low config values to test data
 | |
| 	// gets sent through the processor.
 | |
| 	cfg := Config{
 | |
| 		ProcessorSettings: config.NewProcessorSettings(config.NewComponentID(typeStr)),
 | |
| 		Timeout:           100 * time.Millisecond,
 | |
| 		SendBatchSize:     50,
 | |
| 	}
 | |
| 
 | |
| 	requestCount := 100
 | |
| 	logsPerRequest := 5
 | |
| 	sink := new(consumertest.LogsSink)
 | |
| 
 | |
| 	creationSet := componenttest.NewNopProcessorCreateSettings()
 | |
| 	batcher, err := newBatchLogsProcessor(creationSet, sink, &cfg, configtelemetry.LevelDetailed)
 | |
| 	require.NoError(t, err)
 | |
| 	require.NoError(t, batcher.Start(context.Background(), componenttest.NewNopHost()))
 | |
| 
 | |
| 	start := time.Now()
 | |
| 	size := 0
 | |
| 	for requestNum := 0; requestNum < requestCount; requestNum++ {
 | |
| 		ld := testdata.GenerateLogs(logsPerRequest)
 | |
| 		size += sizer.LogsSize(ld)
 | |
| 		assert.NoError(t, batcher.ConsumeLogs(context.Background(), ld))
 | |
| 	}
 | |
| 	require.NoError(t, batcher.Shutdown(context.Background()))
 | |
| 
 | |
| 	elapsed := time.Since(start)
 | |
| 	require.LessOrEqual(t, elapsed.Nanoseconds(), cfg.Timeout.Nanoseconds())
 | |
| 
 | |
| 	expectedBatchesNum := requestCount * logsPerRequest / int(cfg.SendBatchSize)
 | |
| 	expectedBatchingFactor := int(cfg.SendBatchSize) / logsPerRequest
 | |
| 
 | |
| 	require.Equal(t, requestCount*logsPerRequest, sink.LogRecordCount())
 | |
| 	receivedMds := sink.AllLogs()
 | |
| 	require.Equal(t, expectedBatchesNum, len(receivedMds))
 | |
| 	for _, ld := range receivedMds {
 | |
| 		require.Equal(t, expectedBatchingFactor, ld.ResourceLogs().Len())
 | |
| 		for i := 0; i < expectedBatchingFactor; i++ {
 | |
| 			require.Equal(t, logsPerRequest, ld.ResourceLogs().At(i).ScopeLogs().At(0).LogRecords().Len())
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	viewData, err := view.RetrieveData("processor/batch/" + statBatchSendSize.Name())
 | |
| 	require.NoError(t, err)
 | |
| 	assert.Equal(t, 1, len(viewData))
 | |
| 	distData := viewData[0].Data.(*view.DistributionData)
 | |
| 	assert.Equal(t, int64(expectedBatchesNum), distData.Count)
 | |
| 	assert.Equal(t, sink.LogRecordCount(), int(distData.Sum()))
 | |
| 	assert.Equal(t, cfg.SendBatchSize, uint32(distData.Min))
 | |
| 	assert.Equal(t, cfg.SendBatchSize, uint32(distData.Max))
 | |
| 
 | |
| 	viewData, err = view.RetrieveData("processor/batch/" + statBatchSendSizeBytes.Name())
 | |
| 	require.NoError(t, err)
 | |
| 	assert.Equal(t, 1, len(viewData))
 | |
| 	distData = viewData[0].Data.(*view.DistributionData)
 | |
| 	assert.Equal(t, int64(expectedBatchesNum), distData.Count)
 | |
| 	assert.Equal(t, size, int(distData.Sum()))
 | |
| }
 | |
| 
 | |
| func TestBatchLogsProcessor_Timeout(t *testing.T) {
 | |
| 	cfg := Config{
 | |
| 		ProcessorSettings: config.NewProcessorSettings(config.NewComponentID(typeStr)),
 | |
| 		Timeout:           100 * time.Millisecond,
 | |
| 		SendBatchSize:     100,
 | |
| 	}
 | |
| 	requestCount := 5
 | |
| 	logsPerRequest := 10
 | |
| 	sink := new(consumertest.LogsSink)
 | |
| 
 | |
| 	creationSet := componenttest.NewNopProcessorCreateSettings()
 | |
| 	batcher, err := newBatchLogsProcessor(creationSet, sink, &cfg, configtelemetry.LevelDetailed)
 | |
| 	require.NoError(t, err)
 | |
| 	require.NoError(t, batcher.Start(context.Background(), componenttest.NewNopHost()))
 | |
| 
 | |
| 	start := time.Now()
 | |
| 	for requestNum := 0; requestNum < requestCount; requestNum++ {
 | |
| 		ld := testdata.GenerateLogs(logsPerRequest)
 | |
| 		assert.NoError(t, batcher.ConsumeLogs(context.Background(), ld))
 | |
| 	}
 | |
| 
 | |
| 	// Wait for at least one batch to be sent.
 | |
| 	for {
 | |
| 		if sink.LogRecordCount() != 0 {
 | |
| 			break
 | |
| 		}
 | |
| 		<-time.After(cfg.Timeout)
 | |
| 	}
 | |
| 
 | |
| 	elapsed := time.Since(start)
 | |
| 	require.LessOrEqual(t, cfg.Timeout.Nanoseconds(), elapsed.Nanoseconds())
 | |
| 
 | |
| 	// This should not change the results in the sink, verified by the expectedBatchesNum
 | |
| 	require.NoError(t, batcher.Shutdown(context.Background()))
 | |
| 
 | |
| 	expectedBatchesNum := 1
 | |
| 	expectedBatchingFactor := 5
 | |
| 
 | |
| 	require.Equal(t, requestCount*logsPerRequest, sink.LogRecordCount())
 | |
| 	receivedMds := sink.AllLogs()
 | |
| 	require.Equal(t, expectedBatchesNum, len(receivedMds))
 | |
| 	for _, ld := range receivedMds {
 | |
| 		require.Equal(t, expectedBatchingFactor, ld.ResourceLogs().Len())
 | |
| 		for i := 0; i < expectedBatchingFactor; i++ {
 | |
| 			require.Equal(t, logsPerRequest, ld.ResourceLogs().At(i).ScopeLogs().At(0).LogRecords().Len())
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func TestBatchLogProcessor_Shutdown(t *testing.T) {
 | |
| 	cfg := Config{
 | |
| 		ProcessorSettings: config.NewProcessorSettings(config.NewComponentID(typeStr)),
 | |
| 		Timeout:           3 * time.Second,
 | |
| 		SendBatchSize:     1000,
 | |
| 	}
 | |
| 	requestCount := 5
 | |
| 	logsPerRequest := 10
 | |
| 	sink := new(consumertest.LogsSink)
 | |
| 
 | |
| 	creationSet := componenttest.NewNopProcessorCreateSettings()
 | |
| 	batcher, err := newBatchLogsProcessor(creationSet, sink, &cfg, configtelemetry.LevelDetailed)
 | |
| 	require.NoError(t, err)
 | |
| 	require.NoError(t, batcher.Start(context.Background(), componenttest.NewNopHost()))
 | |
| 
 | |
| 	for requestNum := 0; requestNum < requestCount; requestNum++ {
 | |
| 		ld := testdata.GenerateLogs(logsPerRequest)
 | |
| 		assert.NoError(t, batcher.ConsumeLogs(context.Background(), ld))
 | |
| 	}
 | |
| 
 | |
| 	require.NoError(t, batcher.Shutdown(context.Background()))
 | |
| 
 | |
| 	require.Equal(t, requestCount*logsPerRequest, sink.LogRecordCount())
 | |
| 	require.Equal(t, 1, len(sink.AllLogs()))
 | |
| }
 | |
| 
 | |
| func getTestLogSeverityText(requestNum, index int) string {
 | |
| 	return fmt.Sprintf("test-log-int-%d-%d", requestNum, index)
 | |
| }
 | |
| 
 | |
| func logsReceivedBySeverityText(lds []plog.Logs) map[string]plog.LogRecord {
 | |
| 	logsReceivedBySeverityText := map[string]plog.LogRecord{}
 | |
| 	for i := range lds {
 | |
| 		ld := lds[i]
 | |
| 		rms := ld.ResourceLogs()
 | |
| 		for i := 0; i < rms.Len(); i++ {
 | |
| 			ilms := rms.At(i).ScopeLogs()
 | |
| 			for j := 0; j < ilms.Len(); j++ {
 | |
| 				logs := ilms.At(j).LogRecords()
 | |
| 				for k := 0; k < logs.Len(); k++ {
 | |
| 					log := logs.At(k)
 | |
| 					logsReceivedBySeverityText[log.SeverityText()] = log
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 	return logsReceivedBySeverityText
 | |
| }
 | |
| 
 | |
| func TestShutdown(t *testing.T) {
 | |
| 	factory := NewFactory()
 | |
| 	componenttest.VerifyProcessorShutdown(t, factory, factory.CreateDefaultConfig())
 | |
| }
 |