diff --git a/internal/e2e/go.mod b/internal/e2e/go.mod index b0533e860ed0..7030aea1145d 100644 --- a/internal/e2e/go.mod +++ b/internal/e2e/go.mod @@ -3,6 +3,7 @@ module go.opentelemetry.io/collector/internal/e2e go 1.23.0 require ( + github.com/prometheus/common v0.65.0 github.com/stretchr/testify v1.10.0 go.opentelemetry.io/collector v0.131.0 go.opentelemetry.io/collector/component v1.37.0 @@ -18,19 +19,26 @@ require ( go.opentelemetry.io/collector/config/configtelemetry v0.131.0 go.opentelemetry.io/collector/config/configtls v1.37.0 go.opentelemetry.io/collector/confmap v1.37.0 + go.opentelemetry.io/collector/confmap/provider/envprovider v1.37.0 + go.opentelemetry.io/collector/confmap/provider/fileprovider v1.37.0 + go.opentelemetry.io/collector/confmap/provider/yamlprovider v1.37.0 go.opentelemetry.io/collector/connector v0.131.0 go.opentelemetry.io/collector/connector/connectortest v0.131.0 go.opentelemetry.io/collector/consumer v1.37.0 go.opentelemetry.io/collector/consumer/consumertest v0.131.0 go.opentelemetry.io/collector/exporter v0.131.0 + go.opentelemetry.io/collector/exporter/debugexporter v0.131.1 go.opentelemetry.io/collector/exporter/exportertest v0.131.0 go.opentelemetry.io/collector/exporter/otlpexporter v0.131.0 go.opentelemetry.io/collector/exporter/otlphttpexporter v0.131.0 go.opentelemetry.io/collector/extension v1.37.0 go.opentelemetry.io/collector/internal/sharedcomponent v0.131.0 + go.opentelemetry.io/collector/otelcol v0.131.0 go.opentelemetry.io/collector/pdata v1.37.0 go.opentelemetry.io/collector/pdata/testdata v0.131.0 go.opentelemetry.io/collector/pipeline v0.131.0 + go.opentelemetry.io/collector/processor v1.37.0 + go.opentelemetry.io/collector/processor/batchprocessor v0.131.1 go.opentelemetry.io/collector/receiver v1.37.0 go.opentelemetry.io/collector/receiver/otlpreceiver v0.131.0 go.opentelemetry.io/collector/receiver/receivertest v0.131.0 @@ -60,6 +68,7 @@ require ( github.com/google/uuid v1.6.0 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.1 // indirect github.com/hashicorp/go-version v1.7.0 // indirect + github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/klauspost/compress v1.18.0 // indirect github.com/knadh/koanf/maps v0.1.2 // indirect @@ -77,10 +86,11 @@ require ( github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect github.com/prometheus/client_golang v1.22.0 // indirect github.com/prometheus/client_model v0.6.2 // indirect - github.com/prometheus/common v0.65.0 // indirect github.com/prometheus/procfs v0.17.0 // indirect github.com/rs/cors v1.11.1 // indirect github.com/shirou/gopsutil/v4 v4.25.6 // indirect + github.com/spf13/cobra v1.9.1 // indirect + github.com/spf13/pflag v1.0.6 // indirect github.com/tklauser/go-sysconf v0.3.12 // indirect github.com/tklauser/numcpus v0.6.1 // indirect github.com/yusufpapurcu/wmi v1.2.4 // indirect @@ -88,6 +98,7 @@ require ( go.opentelemetry.io/collector/client v1.37.0 // indirect go.opentelemetry.io/collector/config/configcompression v1.37.0 // indirect go.opentelemetry.io/collector/config/configmiddleware v0.131.0 // indirect + go.opentelemetry.io/collector/confmap/xconfmap v0.131.0 // indirect go.opentelemetry.io/collector/connector/xconnector v0.131.0 // indirect go.opentelemetry.io/collector/consumer/consumererror v0.131.0 // indirect go.opentelemetry.io/collector/consumer/consumererror/xconsumererror v0.131.0 // indirect @@ -105,7 +116,6 @@ require ( go.opentelemetry.io/collector/pdata/pprofile v0.131.0 // indirect go.opentelemetry.io/collector/pdata/xpdata v0.131.0 // indirect go.opentelemetry.io/collector/pipeline/xpipeline v0.131.0 // indirect - go.opentelemetry.io/collector/processor v1.37.0 // indirect go.opentelemetry.io/collector/processor/processortest v0.131.0 // indirect go.opentelemetry.io/collector/processor/xprocessor v0.131.0 // indirect go.opentelemetry.io/collector/receiver/receiverhelper v0.131.0 // indirect @@ -138,6 +148,7 @@ require ( go.uber.org/multierr v1.11.0 // indirect go.yaml.in/yaml/v3 v3.0.4 // indirect golang.org/x/crypto v0.40.0 // indirect + golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 // indirect golang.org/x/net v0.42.0 // indirect golang.org/x/sys v0.34.0 // indirect golang.org/x/text v0.27.0 // indirect @@ -276,3 +287,9 @@ replace go.opentelemetry.io/collector/extension/extensionmiddleware => ../../ext replace go.opentelemetry.io/collector/config/configmiddleware => ../../config/configmiddleware replace go.opentelemetry.io/collector/pdata/xpdata => ../../pdata/xpdata + +replace go.opentelemetry.io/collector/exporter/debugexporter => ../../exporter/debugexporter + +replace go.opentelemetry.io/collector/processor/batchprocessor => ../../processor/batchprocessor + +replace go.opentelemetry.io/collector/confmap/provider/envprovider => ../../confmap/provider/envprovider diff --git a/internal/e2e/go.sum b/internal/e2e/go.sum index 27f37593eedb..e6deb28e99dc 100644 --- a/internal/e2e/go.sum +++ b/internal/e2e/go.sum @@ -4,6 +4,7 @@ github.com/cenkalti/backoff/v5 v5.0.3 h1:ZN+IMa753KfX5hd8vVaMixjnqRZ3y8CuJKRKj1x github.com/cenkalti/backoff/v5 v5.0.3/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -48,6 +49,8 @@ github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.1 h1:X5VWvz21y3gzm9Nw/kaUeku/1+u github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.1/go.mod h1:Zanoh4+gvIgluNqcfMVTJueD4wSS5hT7zTt4Mrutd90= github.com/hashicorp/go-version v1.7.0 h1:5tqGy27NaOTB8yJKUZELlFAS/LTKJkrmONwQKeRZfjY= github.com/hashicorp/go-version v1.7.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= +github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= @@ -100,8 +103,13 @@ github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= github.com/rs/cors v1.11.1 h1:eU3gRzXLRK57F5rKMGMZURNdIG4EoAmX8k94r9wXWHA= github.com/rs/cors v1.11.1/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/shirou/gopsutil/v4 v4.25.6 h1:kLysI2JsKorfaFPcYmcJqbzROzsBWEOAtw6A7dIfqXs= github.com/shirou/gopsutil/v4 v4.25.6/go.mod h1:PfybzyydfZcN+JMMjkF6Zb8Mq1A/VcogFFg7hj50W9c= +github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo= +github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0= +github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o= +github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= @@ -183,6 +191,8 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.40.0 h1:r4x+VvoG5Fm+eJcxMaY8CQM7Lb0l1lsmjGBQ6s8BfKM= golang.org/x/crypto v0.40.0/go.mod h1:Qr1vMER5WyS2dfPHAlsOj01wgLbsyWtFn/aY+5+ZdxY= +golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 h1:vr/HnozRka3pE4EsMEg1lgkXJkTFJCVUX+S/ZT6wYzM= +golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842/go.mod h1:XtvwrStGgqGPLc4cjQfWqZHG1YFdYs6swckp8vpsjnc= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= diff --git a/internal/e2e/metric_stability_test.go b/internal/e2e/metric_stability_test.go new file mode 100644 index 000000000000..e6b884c0a848 --- /dev/null +++ b/internal/e2e/metric_stability_test.go @@ -0,0 +1,366 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package e2e + +import ( + "bufio" + "bytes" + "context" + "fmt" + "net" + "net/http" + "path/filepath" + "strconv" + "testing" + "time" + + "github.com/prometheus/common/expfmt" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "go.opentelemetry.io/collector/component" + "go.opentelemetry.io/collector/confmap" + "go.opentelemetry.io/collector/confmap/provider/envprovider" + "go.opentelemetry.io/collector/confmap/provider/fileprovider" + "go.opentelemetry.io/collector/confmap/provider/yamlprovider" + "go.opentelemetry.io/collector/exporter" + "go.opentelemetry.io/collector/exporter/debugexporter" + "go.opentelemetry.io/collector/otelcol" + "go.opentelemetry.io/collector/pdata/pcommon" + "go.opentelemetry.io/collector/pdata/plog" + "go.opentelemetry.io/collector/pdata/pmetric" + "go.opentelemetry.io/collector/pdata/ptrace" + "go.opentelemetry.io/collector/processor" + "go.opentelemetry.io/collector/processor/batchprocessor" + "go.opentelemetry.io/collector/receiver" + "go.opentelemetry.io/collector/receiver/otlpreceiver" +) + +func assertMetrics(t *testing.T, metricsAddr string, expectedMetrics map[string]bool) bool { + client := &http.Client{} + resp, err := client.Get(fmt.Sprintf("http://%s/metrics", metricsAddr)) + if err != nil { + return false + } + + if resp.StatusCode != http.StatusOK { + return false + } + + defer resp.Body.Close() + + reader := bufio.NewReader(resp.Body) + var parser expfmt.TextParser + parsed, err := parser.TextToMetricFamilies(reader) + if err != nil { + return false + } + + for metricName, metricFamily := range parsed { + if _, ok := expectedMetrics[metricName]; ok { + expectedMetrics[metricName] = true + assert.GreaterOrEqual(t, len(metricFamily.Metric), 1, + "metric %s should have at least one data point", metricName) + } + } + + for metricName, found := range expectedMetrics { + if !found { + t.Logf("expected metric %s was not found", metricName) + return false + } + } + + return true +} + +func TestMetricStability(t *testing.T) { + tests := []struct { + name string + configFile string + expectedMetrics map[string]bool + otelPort string + metricsPort string + }{ + { + name: "No metric readers (default)", + configFile: "metric_stability_test_no_readers.yaml", + expectedMetrics: map[string]bool{ + // Process metrics + "otelcol_process_uptime": false, + "otelcol_process_cpu_seconds": false, + "otelcol_process_memory_rss": false, + "otelcol_process_runtime_heap_alloc_bytes": false, + "otelcol_process_runtime_total_alloc_bytes": false, + "otelcol_process_runtime_total_sys_memory_bytes": false, + + // Batch processor metrics + "otelcol_processor_batch_batch_send_size": false, + "otelcol_processor_batch_batch_send_size_bytes": false, + "otelcol_processor_batch_metadata_cardinality": false, + "otelcol_processor_batch_timeout_trigger_send": false, + + // HTTP server metrics + "http_server_request_body_size": false, + "http_server_request_duration": false, + "http_server_response_body_size": false, + + // Exporter metrics + "otelcol_exporter_sent_metric_points": false, + "otelcol_exporter_send_failed_metric_points": false, + "otelcol_exporter_sent_spans": false, + "otelcol_exporter_send_failed_spans": false, + "otelcol_exporter_sent_log_records": false, + "otelcol_exporter_send_failed_log_records": false, + + // Receiver metrics + "otelcol_receiver_accepted_metric_points": false, + "otelcol_receiver_refused_metric_points": false, + "otelcol_receiver_accepted_spans": false, + "otelcol_receiver_refused_spans": false, + "otelcol_receiver_accepted_log_records": false, + "otelcol_receiver_refused_log_records": false, + + // Other metrics + "promhttp_metric_handler_errors_total": false, + "target_info": false, + }, + otelPort: getFreePort(t), + metricsPort: "8888", // default metrics port + }, + { + name: "Metric readers", + configFile: "metric_stability_test_readers.yaml", + expectedMetrics: map[string]bool{ + // Process metrics + "otelcol_process_uptime_seconds_total": false, + "otelcol_process_cpu_seconds_total": false, + "otelcol_process_memory_rss_bytes": false, + "otelcol_process_runtime_heap_alloc_bytes": false, + "otelcol_process_runtime_total_alloc_bytes_total": false, + "otelcol_process_runtime_total_sys_memory_bytes": false, + + // Batch processor metrics + "otelcol_processor_batch_batch_send_size": false, + "otelcol_processor_batch_batch_send_size_bytes": false, + "otelcol_processor_batch_metadata_cardinality": false, + "otelcol_processor_batch_timeout_trigger_send_total": false, + + // HTTP server metrics + "http_server_request_body_size_bytes": false, + "http_server_request_duration_seconds": false, + "http_server_response_body_size_bytes": false, + + // Exporter metrics - Metrics + "otelcol_exporter_sent_metric_points_total": false, + "otelcol_exporter_send_failed_metric_points_total": false, + + // Exporter metrics - Traces + "otelcol_exporter_sent_spans_total": false, + "otelcol_exporter_send_failed_spans_total": false, + + // Exporter metrics - Logs + "otelcol_exporter_sent_log_records_total": false, + "otelcol_exporter_send_failed_log_records_total": false, + + // Receiver metrics + "otelcol_receiver_accepted_metric_points_total": false, + "otelcol_receiver_refused_metric_points_total": false, + + // Receiver metrics - Traces + "otelcol_receiver_accepted_spans_total": false, + "otelcol_receiver_refused_spans_total": false, + + // Receiver metrics - Logs + "otelcol_receiver_accepted_log_records_total": false, + "otelcol_receiver_refused_log_records_total": false, + + // Other metrics + "promhttp_metric_handler_errors_total": false, + "target_info": false, + }, + otelPort: getFreePort(t), + metricsPort: getFreePort(t), + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + testMetricStability(t, test.configFile, test.expectedMetrics, test.metricsPort, test.otelPort) + }) + } +} + +func testMetricStability(t *testing.T, configFile string, expectedMetrics map[string]bool, metricsPort, otelPort string) { + t.Setenv("METRICS_PORT", metricsPort) + t.Setenv("OTEL_PORT", otelPort) + + collector, err := otelcol.NewCollector(otelcol.CollectorSettings{ + BuildInfo: component.NewDefaultBuildInfo(), + Factories: func() (otelcol.Factories, error) { + return otelcol.Factories{ + Receivers: map[component.Type]receiver.Factory{otlpreceiver.NewFactory().Type(): otlpreceiver.NewFactory()}, + Processors: map[component.Type]processor.Factory{batchprocessor.NewFactory().Type(): batchprocessor.NewFactory()}, + Exporters: map[component.Type]exporter.Factory{debugexporter.NewFactory().Type(): debugexporter.NewFactory()}, + }, nil + }, + ConfigProviderSettings: otelcol.ConfigProviderSettings{ + ResolverSettings: confmap.ResolverSettings{ + URIs: []string{filepath.Join("testdata", configFile)}, + ProviderFactories: []confmap.ProviderFactory{ + fileprovider.NewFactory(), + yamlprovider.NewFactory(), + envprovider.NewFactory(), + }, + }, + }, + }) + require.NoError(t, err) + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + go func() { + err := collector.Run(ctx) + if err != nil { + t.Logf("Collector stopped with error: %v", err) + } + }() + + require.Eventually(t, func() bool { + resp, err := http.Get(fmt.Sprintf("http://localhost:%s/metrics", metricsPort)) + if err != nil { + return false + } + resp.Body.Close() + return resp.StatusCode == http.StatusOK + }, 5*time.Second, 100*time.Millisecond, "collector failed to start") + + for i := 0; i < 5; i++ { + sendTestData(t, otelPort) + } + + require.Eventually(t, func() bool { + return assertMetrics(t, "localhost:"+metricsPort, expectedMetrics) + }, 10*time.Second, 200*time.Millisecond, "failed to verify metrics") +} + +func sendTestData(t *testing.T, otelPort string) { + require.NoError(t, sendTestMetrics(otelPort)) + require.NoError(t, sendTestTraces(otelPort)) + require.NoError(t, sendTestLogs(otelPort)) +} + +func sendTestMetrics(otelPort string) error { + metrics := pmetric.NewMetrics() + rm := metrics.ResourceMetrics().AppendEmpty() + sm := rm.ScopeMetrics().AppendEmpty() + metric := sm.Metrics().AppendEmpty() + metric.SetName("test_metric") + metric.SetDescription("test metric") + metric.SetUnit("1") + dp := metric.SetEmptyGauge().DataPoints().AppendEmpty() + dp.SetTimestamp(pcommon.NewTimestampFromTime(time.Now())) + dp.SetDoubleValue(42.0) + + client := &http.Client{} + + metricsMarshaler := pmetric.ProtoMarshaler{} + metricsBytes, err := metricsMarshaler.MarshalMetrics(metrics) + if err != nil { + return fmt.Errorf("failed to marshal metrics: %w", err) + } + + req, err := http.NewRequest(http.MethodPost, fmt.Sprintf("http://localhost:%s/v1/metrics", otelPort), bytes.NewReader(metricsBytes)) + if err != nil { + return fmt.Errorf("failed to create metrics request: %w", err) + } + req.Header.Set("Content-Type", "application/x-protobuf") + + resp, err := client.Do(req) + if err != nil { + return fmt.Errorf("failed to send metrics: %w", err) + } + resp.Body.Close() + + return nil +} + +func sendTestTraces(otelPort string) error { + traces := ptrace.NewTraces() + rs := traces.ResourceSpans().AppendEmpty() + ss := rs.ScopeSpans().AppendEmpty() + span := ss.Spans().AppendEmpty() + span.SetName("test_span") + now := time.Now() + span.SetStartTimestamp(pcommon.NewTimestampFromTime(now)) + span.SetEndTimestamp(pcommon.NewTimestampFromTime(now)) + span.SetTraceID([16]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}) + span.SetSpanID([8]byte{1, 2, 3, 4, 5, 6, 7, 8}) + + client := &http.Client{} + + tracesMarshaler := ptrace.ProtoMarshaler{} + tracesBytes, err := tracesMarshaler.MarshalTraces(traces) + if err != nil { + return fmt.Errorf("failed to marshal traces: %w", err) + } + + req, err := http.NewRequest(http.MethodPost, fmt.Sprintf("http://localhost:%s/v1/traces", otelPort), bytes.NewReader(tracesBytes)) + if err != nil { + return fmt.Errorf("failed to create traces request: %w", err) + } + req.Header.Set("Content-Type", "application/x-protobuf") + + resp, err := client.Do(req) + if err != nil { + return fmt.Errorf("failed to send traces: %w", err) + } + resp.Body.Close() + + return nil +} + +func sendTestLogs(otelPort string) error { + logs := plog.NewLogs() + rl := logs.ResourceLogs().AppendEmpty() + sl := rl.ScopeLogs().AppendEmpty() + log := sl.LogRecords().AppendEmpty() + log.SetTimestamp(pcommon.NewTimestampFromTime(time.Now())) + log.SetSeverityText("INFO") + log.SetSeverityNumber(plog.SeverityNumberInfo) + log.Body().SetStr("test log message") + + client := &http.Client{} + + logsMarshaler := plog.ProtoMarshaler{} + logsBytes, err := logsMarshaler.MarshalLogs(logs) + if err != nil { + return fmt.Errorf("failed to marshal logs: %w", err) + } + + req, err := http.NewRequest(http.MethodPost, fmt.Sprintf("http://localhost:%s/v1/logs", otelPort), bytes.NewReader(logsBytes)) + if err != nil { + return fmt.Errorf("failed to create logs request: %w", err) + } + req.Header.Set("Content-Type", "application/x-protobuf") + + resp, err := client.Do(req) + if err != nil { + return fmt.Errorf("failed to send logs: %w", err) + } + resp.Body.Close() + + return nil +} + +func getFreePort(t *testing.T) string { + t.Helper() + l, err := net.Listen("tcp", "127.0.0.1:0") + if err != nil { + t.Fatalf("could not get free port: %v", err) + } + defer l.Close() + return strconv.Itoa(l.Addr().(*net.TCPAddr).Port) +} diff --git a/internal/e2e/testdata/metric_stability_test_no_readers.yaml b/internal/e2e/testdata/metric_stability_test_no_readers.yaml new file mode 100644 index 000000000000..381c89a08036 --- /dev/null +++ b/internal/e2e/testdata/metric_stability_test_no_readers.yaml @@ -0,0 +1,35 @@ +receivers: + otlp: + protocols: + http: + endpoint: localhost:${env:OTEL_PORT} + +processors: + batch: + timeout: 100ms + send_batch_size: 100 + +exporters: + debug: + verbosity: detailed + +service: + telemetry: + logs: + level: info + metrics: + level: detailed + + pipelines: + traces: + receivers: [otlp] + processors: [batch] + exporters: [debug] + metrics: + receivers: [otlp] + processors: [batch] + exporters: [debug] + logs: + receivers: [otlp] + processors: [batch] + exporters: [debug] \ No newline at end of file diff --git a/internal/e2e/testdata/metric_stability_test_readers.yaml b/internal/e2e/testdata/metric_stability_test_readers.yaml new file mode 100644 index 000000000000..85fa65562ba5 --- /dev/null +++ b/internal/e2e/testdata/metric_stability_test_readers.yaml @@ -0,0 +1,41 @@ +receivers: + otlp: + protocols: + http: + endpoint: localhost:${env:OTEL_PORT} + +processors: + batch: + timeout: 100ms + send_batch_size: 100 + +exporters: + debug: + verbosity: detailed + +service: + telemetry: + logs: + level: info + metrics: + level: detailed + readers: + - pull: + exporter: + prometheus: + host: localhost + port: ${env:METRICS_PORT} + + pipelines: + traces: + receivers: [otlp] + processors: [batch] + exporters: [debug] + metrics: + receivers: [otlp] + processors: [batch] + exporters: [debug] + logs: + receivers: [otlp] + processors: [batch] + exporters: [debug] \ No newline at end of file