From 3d9bb0bd3483c63ea6e5817a75100685cdb56db2 Mon Sep 17 00:00:00 2001 From: Yuna Verheyden Date: Thu, 19 Dec 2024 15:00:10 +0100 Subject: [PATCH] Distributor shim: add test verifying receiver works (including metrics) --- go.mod | 6 +- go.sum | 2 + modules/distributor/distributor.go | 2 +- .../distributor/receiver/metrics_provider.go | 304 +++----------- .../receiver/metrics_provider_test.go | 3 +- modules/distributor/receiver/shim.go | 5 +- modules/distributor/receiver/shim_test.go | 199 +++++++++ .../exporter/otlphttpexporter/LICENSE | 202 +++++++++ .../exporter/otlphttpexporter/Makefile | 1 + .../exporter/otlphttpexporter/README.md | 69 +++ .../exporter/otlphttpexporter/config.go | 73 ++++ .../exporter/otlphttpexporter/doc.go | 7 + .../exporter/otlphttpexporter/factory.go | 146 +++++++ .../internal/metadata/generated_status.go | 17 + .../exporter/otlphttpexporter/metadata.yaml | 13 + .../exporter/otlphttpexporter/otlp.go | 394 ++++++++++++++++++ .../collector/pdata/testdata/LICENSE | 202 +++++++++ .../collector/pdata/testdata/Makefile | 1 + .../collector/pdata/testdata/common.go | 30 ++ .../collector/pdata/testdata/log.go | 59 +++ .../collector/pdata/testdata/metric.go | 297 +++++++++++++ .../collector/pdata/testdata/resource.go | 10 + .../collector/pdata/testdata/trace.go | 68 +++ vendor/modules.txt | 7 + 24 files changed, 1856 insertions(+), 261 deletions(-) create mode 100644 vendor/go.opentelemetry.io/collector/exporter/otlphttpexporter/LICENSE create mode 100644 vendor/go.opentelemetry.io/collector/exporter/otlphttpexporter/Makefile create mode 100644 vendor/go.opentelemetry.io/collector/exporter/otlphttpexporter/README.md create mode 100644 vendor/go.opentelemetry.io/collector/exporter/otlphttpexporter/config.go create mode 100644 vendor/go.opentelemetry.io/collector/exporter/otlphttpexporter/doc.go create mode 100644 vendor/go.opentelemetry.io/collector/exporter/otlphttpexporter/factory.go create mode 100644 vendor/go.opentelemetry.io/collector/exporter/otlphttpexporter/internal/metadata/generated_status.go create mode 100644 vendor/go.opentelemetry.io/collector/exporter/otlphttpexporter/metadata.yaml create mode 100644 vendor/go.opentelemetry.io/collector/exporter/otlphttpexporter/otlp.go create mode 100644 vendor/go.opentelemetry.io/collector/pdata/testdata/LICENSE create mode 100644 vendor/go.opentelemetry.io/collector/pdata/testdata/Makefile create mode 100644 vendor/go.opentelemetry.io/collector/pdata/testdata/common.go create mode 100644 vendor/go.opentelemetry.io/collector/pdata/testdata/log.go create mode 100644 vendor/go.opentelemetry.io/collector/pdata/testdata/metric.go create mode 100644 vendor/go.opentelemetry.io/collector/pdata/testdata/resource.go create mode 100644 vendor/go.opentelemetry.io/collector/pdata/testdata/trace.go diff --git a/go.mod b/go.mod index 2aa2af50b7d..ba1b31ebe8d 100644 --- a/go.mod +++ b/go.mod @@ -104,8 +104,10 @@ require ( go.opentelemetry.io/collector/config/configtls v1.18.0 go.opentelemetry.io/collector/exporter v0.102.1 go.opentelemetry.io/collector/exporter/otlpexporter v0.102.1 + go.opentelemetry.io/collector/exporter/otlphttpexporter v0.102.1 go.opentelemetry.io/collector/extension v0.102.1 go.opentelemetry.io/collector/otelcol v0.102.1 + go.opentelemetry.io/collector/pdata/testdata v0.102.1 go.opentelemetry.io/collector/processor v0.102.1 go.opentelemetry.io/collector/receiver v0.102.1 go.opentelemetry.io/collector/receiver/otlpreceiver v0.102.1 @@ -113,6 +115,8 @@ require ( go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.52.0 go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.28.0 go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.28.0 + go.opentelemetry.io/otel/exporters/prometheus v0.50.0 + go.opentelemetry.io/otel/sdk/metric v1.28.0 go.opentelemetry.io/proto/otlp v1.3.1 golang.org/x/net v0.31.0 golang.org/x/oauth2 v0.21.0 @@ -310,13 +314,11 @@ require ( go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.28.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.28.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.28.0 // indirect - go.opentelemetry.io/otel/exporters/prometheus v0.50.0 // indirect go.opentelemetry.io/otel/exporters/stdout/stdoutlog v0.4.0 // indirect go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.28.0 // indirect go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.28.0 // indirect go.opentelemetry.io/otel/log v0.4.0 // indirect go.opentelemetry.io/otel/sdk/log v0.4.0 // indirect - go.opentelemetry.io/otel/sdk/metric v1.28.0 // indirect golang.org/x/crypto v0.31.0 // indirect golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 // indirect golang.org/x/mod v0.19.0 // indirect diff --git a/go.sum b/go.sum index 7cf747a353e..e9b4977c292 100644 --- a/go.sum +++ b/go.sum @@ -989,6 +989,8 @@ go.opentelemetry.io/collector/exporter v0.102.1 h1:4VURYgBNJscxfMhZWitzcwA1cig5a go.opentelemetry.io/collector/exporter v0.102.1/go.mod h1:1pmNxvrvvbWDW6PiGObICdj0eOSGV4Fzwpm5QA1GU54= go.opentelemetry.io/collector/exporter/otlpexporter v0.102.1 h1:bOXE7u1iy0SKwH2mnVyIMKkvFIR9bn9iIm1Cf/CJlZU= go.opentelemetry.io/collector/exporter/otlpexporter v0.102.1/go.mod h1:4ya6xaUYvcXq9MQW0TbsR4QWkOJI02d/2Vt8plwdozA= +go.opentelemetry.io/collector/exporter/otlphttpexporter v0.102.1 h1:9TaxHrkVtEdssDAHqV5yU9PARkFph7CvfLqC1wS6m+c= +go.opentelemetry.io/collector/exporter/otlphttpexporter v0.102.1/go.mod h1:auKlkLfuUriyZ2CmV2dudJaVGB7ycZ+tTpypy2JNFEc= go.opentelemetry.io/collector/extension v0.102.1 h1:gAvE3w15q+Vv0Tj100jzcDpeMTyc8dAiemHRtJbspLg= go.opentelemetry.io/collector/extension v0.102.1/go.mod h1:XBxUOXjZpwYLZYOK5u3GWlbBTOKmzStY5eU1R/aXkIo= go.opentelemetry.io/collector/extension/auth v0.102.1 h1:GP6oBmpFJjxuVruPb9X40bdf6PNu9779i8anxa+wW6U= diff --git a/modules/distributor/distributor.go b/modules/distributor/distributor.go index 24e94289f5d..d98dbe9797d 100644 --- a/modules/distributor/distributor.go +++ b/modules/distributor/distributor.go @@ -256,7 +256,7 @@ func New(cfg Config, clientCfg ingester_client.Config, ingestersRing ring.ReadRi cfgReceivers = defaultReceivers } - receivers, err := receiver.New(cfgReceivers, d, middleware, cfg.RetryAfterOnResourceExhausted, loggingLevel) + receivers, err := receiver.New(cfgReceivers, d, middleware, cfg.RetryAfterOnResourceExhausted, loggingLevel, reg) if err != nil { return nil, err } diff --git a/modules/distributor/receiver/metrics_provider.go b/modules/distributor/receiver/metrics_provider.go index 9abd360fffe..97c88b7c9bb 100644 --- a/modules/distributor/receiver/metrics_provider.go +++ b/modules/distributor/receiver/metrics_provider.go @@ -22,184 +22,82 @@ import ( "github.com/prometheus/client_golang/prometheus/promauto" "go.opentelemetry.io/otel/metric" "go.opentelemetry.io/otel/metric/embedded" + "go.opentelemetry.io/otel/metric/noop" ) var ( // Compile-time check this implements the OpenTelemetry API. - _ metric.MeterProvider = MeterProvider{} - _ metric.Meter = Meter{} - _ metric.Observer = Observer{} - _ metric.Registration = Registration{} - _ metric.Int64Counter = Int64Counter{} - _ metric.Float64Counter = Float64Counter{} - _ metric.Int64UpDownCounter = Int64UpDownCounter{} - _ metric.Float64UpDownCounter = Float64UpDownCounter{} - _ metric.Int64Histogram = Int64Histogram{} - _ metric.Float64Histogram = Float64Histogram{} - _ metric.Int64Gauge = Int64Gauge{} - _ metric.Float64Gauge = Float64Gauge{} - _ metric.Int64ObservableCounter = Int64ObservableCounter{} - _ metric.Float64ObservableCounter = Float64ObservableCounter{} - _ metric.Int64ObservableGauge = Int64ObservableGauge{} - _ metric.Float64ObservableGauge = Float64ObservableGauge{} - _ metric.Int64ObservableUpDownCounter = Int64ObservableUpDownCounter{} - _ metric.Float64ObservableUpDownCounter = Float64ObservableUpDownCounter{} - _ metric.Int64Observer = Int64Observer{} - _ metric.Float64Observer = Float64Observer{} + _ metric.MeterProvider = &MeterProvider{} + _ metric.Meter = &Meter{} + _ metric.Int64Counter = &Int64Counter{} ) -var ( - receiverAcceptedSpans = promauto.NewCounterVec(prometheus.CounterOpts{ - Namespace: "tempo", - Name: "receiver_accepted_spans", - Help: "Number of spans successfully pushed into the pipeline.", - }, []string{"receiver", "transport"}) - receiverRefusedSpans = promauto.NewCounterVec(prometheus.CounterOpts{ - Namespace: "tempo", - Name: "receiver_refused_spans", - Help: "Number of spans that could not be pushed into the pipeline.", - }, []string{"receiver", "transport"}) -) +type metrics struct { + receiverAcceptedSpans *prometheus.CounterVec + receiverRefusedSpans *prometheus.CounterVec +} + +func newMetrics(reg prometheus.Registerer) *metrics { + return &metrics{ + receiverAcceptedSpans: promauto.With(reg).NewCounterVec(prometheus.CounterOpts{ + Namespace: "tempo", + Name: "receiver_accepted_spans", + Help: "Number of spans successfully pushed into the pipeline.", + }, []string{"receiver", "transport"}), + receiverRefusedSpans: promauto.With(reg).NewCounterVec(prometheus.CounterOpts{ + Namespace: "tempo", + Name: "receiver_refused_spans", + Help: "Number of spans that could not be pushed into the pipeline.", + }, []string{"receiver", "transport"}), + } +} // MeterProvider is an OpenTelemetry No-Op MeterProvider. -type MeterProvider struct{ embedded.MeterProvider } +type MeterProvider struct { + embedded.MeterProvider + metrics *metrics +} // NewMeterProvider returns a MeterProvider that does not record any telemetry. -func NewMeterProvider() MeterProvider { - return MeterProvider{} +func NewMeterProvider(reg prometheus.Registerer) *MeterProvider { + return &MeterProvider{ + metrics: newMetrics(reg), + } } // Meter returns an OpenTelemetry Meter that does not record any telemetry. -func (MeterProvider) Meter(string, ...metric.MeterOption) metric.Meter { - return Meter{} +func (mp *MeterProvider) Meter(string, ...metric.MeterOption) metric.Meter { + return &Meter{ + metrics: mp.metrics, + } } // Meter is an OpenTelemetry No-Op Meter. -type Meter struct{ embedded.Meter } +type Meter struct { + // embed the noop Meter, this provides noop for all methods we don't implement ourselves + noop.Meter + metrics *metrics +} -// Int64Counter returns a Counter used to record int64 measurements that -// produces no telemetry. -func (Meter) Int64Counter(name string, _ ...metric.Int64CounterOption) (metric.Int64Counter, error) { +// Int64Counter returns a Counter used to record int64 measurements +func (m *Meter) Int64Counter(name string, _ ...metric.Int64CounterOption) (metric.Int64Counter, error) { switch name { case "receiver_accepted_spans", "receiver_refused_spans": - return Int64Counter{Name: name}, nil + return &Int64Counter{Name: name, metrics: m.metrics}, nil default: - return Int64Counter{}, nil + return noop.Int64Counter{}, nil } } -// Int64UpDownCounter returns an UpDownCounter used to record int64 -// measurements that produces no telemetry. -func (Meter) Int64UpDownCounter(string, ...metric.Int64UpDownCounterOption) (metric.Int64UpDownCounter, error) { - return Int64UpDownCounter{}, nil -} - -// Int64Histogram returns a Histogram used to record int64 measurements that -// produces no telemetry. -func (Meter) Int64Histogram(string, ...metric.Int64HistogramOption) (metric.Int64Histogram, error) { - return Int64Histogram{}, nil -} - -// Int64Gauge returns a Gauge used to record int64 measurements that -// produces no telemetry. -func (Meter) Int64Gauge(string, ...metric.Int64GaugeOption) (metric.Int64Gauge, error) { - return Int64Gauge{}, nil -} - -// Int64ObservableCounter returns an ObservableCounter used to record int64 -// measurements that produces no telemetry. -func (Meter) Int64ObservableCounter(string, ...metric.Int64ObservableCounterOption) (metric.Int64ObservableCounter, error) { - return Int64ObservableCounter{}, nil -} - -// Int64ObservableUpDownCounter returns an ObservableUpDownCounter used to -// record int64 measurements that produces no telemetry. -func (Meter) Int64ObservableUpDownCounter(string, ...metric.Int64ObservableUpDownCounterOption) (metric.Int64ObservableUpDownCounter, error) { - return Int64ObservableUpDownCounter{}, nil -} - -// Int64ObservableGauge returns an ObservableGauge used to record int64 -// measurements that produces no telemetry. -func (Meter) Int64ObservableGauge(string, ...metric.Int64ObservableGaugeOption) (metric.Int64ObservableGauge, error) { - return Int64ObservableGauge{}, nil -} - -// Float64Counter returns a Counter used to record int64 measurements that -// produces no telemetry. -func (Meter) Float64Counter(string, ...metric.Float64CounterOption) (metric.Float64Counter, error) { - return Float64Counter{}, nil -} - -// Float64UpDownCounter returns an UpDownCounter used to record int64 -// measurements that produces no telemetry. -func (Meter) Float64UpDownCounter(string, ...metric.Float64UpDownCounterOption) (metric.Float64UpDownCounter, error) { - return Float64UpDownCounter{}, nil -} - -// Float64Histogram returns a Histogram used to record int64 measurements that -// produces no telemetry. -func (Meter) Float64Histogram(string, ...metric.Float64HistogramOption) (metric.Float64Histogram, error) { - return Float64Histogram{}, nil -} - -// Float64Gauge returns a Gauge used to record float64 measurements that -// produces no telemetry. -func (Meter) Float64Gauge(string, ...metric.Float64GaugeOption) (metric.Float64Gauge, error) { - return Float64Gauge{}, nil -} - -// Float64ObservableCounter returns an ObservableCounter used to record int64 -// measurements that produces no telemetry. -func (Meter) Float64ObservableCounter(string, ...metric.Float64ObservableCounterOption) (metric.Float64ObservableCounter, error) { - return Float64ObservableCounter{}, nil -} - -// Float64ObservableUpDownCounter returns an ObservableUpDownCounter used to -// record int64 measurements that produces no telemetry. -func (Meter) Float64ObservableUpDownCounter(string, ...metric.Float64ObservableUpDownCounterOption) (metric.Float64ObservableUpDownCounter, error) { - return Float64ObservableUpDownCounter{}, nil -} - -// Float64ObservableGauge returns an ObservableGauge used to record int64 -// measurements that produces no telemetry. -func (Meter) Float64ObservableGauge(string, ...metric.Float64ObservableGaugeOption) (metric.Float64ObservableGauge, error) { - return Float64ObservableGauge{}, nil -} - -// RegisterCallback performs no operation. -func (Meter) RegisterCallback(metric.Callback, ...metric.Observable) (metric.Registration, error) { - return Registration{}, nil -} - -// Observer acts as a recorder of measurements for multiple instruments in a -// Callback, it performing no operation. -type Observer struct{ embedded.Observer } - -// ObserveFloat64 performs no operation. -func (Observer) ObserveFloat64(metric.Float64Observable, float64, ...metric.ObserveOption) { -} - -// ObserveInt64 performs no operation. -func (Observer) ObserveInt64(metric.Int64Observable, int64, ...metric.ObserveOption) { -} - -// Registration is the registration of a Callback with a No-Op Meter. -type Registration struct{ embedded.Registration } - -// Unregister unregisters the Callback the Registration represents with the -// No-Op Meter. This will always return nil because the No-Op Meter performs no -// operation, including hold any record of registrations. -func (Registration) Unregister() error { return nil } - // Int64Counter is an OpenTelemetry Counter used to record int64 measurements. -// It produces no telemetry. type Int64Counter struct { embedded.Int64Counter - Name string + Name string + metrics *metrics } -func (r Int64Counter) Add(_ context.Context, value int64, options ...metric.AddOption) { +func (r *Int64Counter) Add(_ context.Context, value int64, options ...metric.AddOption) { // don't do anything for metrics that we don't care if r.Name == "" { return @@ -221,112 +119,8 @@ func (r Int64Counter) Add(_ context.Context, value int64, options ...metric.AddO switch r.Name { case "receiver_accepted_spans": - receiverAcceptedSpans.WithLabelValues(receiver, transport).Add(float64(value)) + r.metrics.receiverAcceptedSpans.WithLabelValues(receiver, transport).Add(float64(value)) case "receiver_refused_spans": - receiverRefusedSpans.WithLabelValues(receiver, transport).Add(float64(value)) + r.metrics.receiverRefusedSpans.WithLabelValues(receiver, transport).Add(float64(value)) } } - -// Float64Counter is an OpenTelemetry Counter used to record float64 -// measurements. It produces no telemetry. -type Float64Counter struct{ embedded.Float64Counter } - -// Add performs no operation. -func (Float64Counter) Add(context.Context, float64, ...metric.AddOption) {} - -// Int64UpDownCounter is an OpenTelemetry UpDownCounter used to record int64 -// measurements. It produces no telemetry. -type Int64UpDownCounter struct{ embedded.Int64UpDownCounter } - -// Add performs no operation. -func (Int64UpDownCounter) Add(context.Context, int64, ...metric.AddOption) {} - -// Float64UpDownCounter is an OpenTelemetry UpDownCounter used to record -// float64 measurements. It produces no telemetry. -type Float64UpDownCounter struct{ embedded.Float64UpDownCounter } - -// Add performs no operation. -func (Float64UpDownCounter) Add(context.Context, float64, ...metric.AddOption) {} - -// Int64Histogram is an OpenTelemetry Histogram used to record int64 -// measurements. It produces no telemetry. -type Int64Histogram struct{ embedded.Int64Histogram } - -// Record performs no operation. -func (Int64Histogram) Record(context.Context, int64, ...metric.RecordOption) {} - -// Float64Histogram is an OpenTelemetry Histogram used to record float64 -// measurements. It produces no telemetry. -type Float64Histogram struct{ embedded.Float64Histogram } - -// Record performs no operation. -func (Float64Histogram) Record(context.Context, float64, ...metric.RecordOption) {} - -// Int64Gauge is an OpenTelemetry Gauge used to record instantaneous int64 -// measurements. It produces no telemetry. -type Int64Gauge struct{ embedded.Int64Gauge } - -// Record performs no operation. -func (Int64Gauge) Record(context.Context, int64, ...metric.RecordOption) {} - -// Float64Gauge is an OpenTelemetry Gauge used to record instantaneous float64 -// measurements. It produces no telemetry. -type Float64Gauge struct{ embedded.Float64Gauge } - -// Record performs no operation. -func (Float64Gauge) Record(context.Context, float64, ...metric.RecordOption) {} - -// Int64ObservableCounter is an OpenTelemetry ObservableCounter used to record -// int64 measurements. It produces no telemetry. -type Int64ObservableCounter struct { - metric.Int64Observable - embedded.Int64ObservableCounter -} - -// Float64ObservableCounter is an OpenTelemetry ObservableCounter used to record -// float64 measurements. It produces no telemetry. -type Float64ObservableCounter struct { - metric.Float64Observable - embedded.Float64ObservableCounter -} - -// Int64ObservableGauge is an OpenTelemetry ObservableGauge used to record -// int64 measurements. It produces no telemetry. -type Int64ObservableGauge struct { - metric.Int64Observable - embedded.Int64ObservableGauge -} - -// Float64ObservableGauge is an OpenTelemetry ObservableGauge used to record -// float64 measurements. It produces no telemetry. -type Float64ObservableGauge struct { - metric.Float64Observable - embedded.Float64ObservableGauge -} - -// Int64ObservableUpDownCounter is an OpenTelemetry ObservableUpDownCounter -// used to record int64 measurements. It produces no telemetry. -type Int64ObservableUpDownCounter struct { - metric.Int64Observable - embedded.Int64ObservableUpDownCounter -} - -// Float64ObservableUpDownCounter is an OpenTelemetry ObservableUpDownCounter -// used to record float64 measurements. It produces no telemetry. -type Float64ObservableUpDownCounter struct { - metric.Float64Observable - embedded.Float64ObservableUpDownCounter -} - -// Int64Observer is a recorder of int64 measurements that performs no operation. -type Int64Observer struct{ embedded.Int64Observer } - -// Observe performs no operation. -func (Int64Observer) Observe(int64, ...metric.ObserveOption) {} - -// Float64Observer is a recorder of float64 measurements that performs no -// operation. -type Float64Observer struct{ embedded.Float64Observer } - -// Observe performs no operation. -func (Float64Observer) Observe(float64, ...metric.ObserveOption) {} diff --git a/modules/distributor/receiver/metrics_provider_test.go b/modules/distributor/receiver/metrics_provider_test.go index 0c7ce7a2fa2..95da5882323 100644 --- a/modules/distributor/receiver/metrics_provider_test.go +++ b/modules/distributor/receiver/metrics_provider_test.go @@ -4,12 +4,13 @@ import ( "context" "testing" + "github.com/prometheus/client_golang/prometheus" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/metric" ) func BenchmarkMetricsProvider(b *testing.B) { - meterProvider := NewMeterProvider() + meterProvider := NewMeterProvider(prometheus.NewPedanticRegistry()) meter := meterProvider.Meter("test") acceptedSpans, _ := meter.Int64Counter("receiver_accepted_spans") c := context.Background() diff --git a/modules/distributor/receiver/shim.go b/modules/distributor/receiver/shim.go index 563f9b7af60..d43544f0c3a 100644 --- a/modules/distributor/receiver/shim.go +++ b/modules/distributor/receiver/shim.go @@ -14,6 +14,7 @@ import ( "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/kafkareceiver" "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/opencensusreceiver" "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/zipkinreceiver" + "github.com/prometheus/client_golang/prometheus" prom_client "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promauto" "go.opentelemetry.io/collector/component" @@ -142,7 +143,7 @@ func (m *mapProvider) Scheme() string { return "mock" } func (m *mapProvider) Shutdown(context.Context) error { return nil } -func New(receiverCfg map[string]interface{}, pusher TracesPusher, middleware Middleware, retryAfterDuration time.Duration, logLevel dslog.Level) (services.Service, error) { +func New(receiverCfg map[string]interface{}, pusher TracesPusher, middleware Middleware, retryAfterDuration time.Duration, logLevel dslog.Level, reg prometheus.Registerer) (services.Service, error) { shim := &receiversShim{ pusher: pusher, logger: log.NewRateLimitedLogger(logsPerSecond, level.Error(log.Logger)), @@ -235,7 +236,7 @@ func New(receiverCfg map[string]interface{}, pusher TracesPusher, middleware Mid nopType := component.MustNewType("tempo") traceProvider := tracenoop.NewTracerProvider() - meterProvider := NewMeterProvider() + meterProvider := NewMeterProvider(reg) // todo: propagate a real context? translate our log configuration into zap? ctx := context.Background() for componentID, cfg := range conf.Receivers { diff --git a/modules/distributor/receiver/shim_test.go b/modules/distributor/receiver/shim_test.go index 22076743a50..72cfed27596 100644 --- a/modules/distributor/receiver/shim_test.go +++ b/modules/distributor/receiver/shim_test.go @@ -1,17 +1,216 @@ package receiver import ( + "context" "errors" + "strings" "testing" "time" + dslog "github.com/grafana/dskit/log" + "github.com/grafana/dskit/services" + "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/client_golang/prometheus/testutil" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "go.opentelemetry.io/collector/component" + "go.opentelemetry.io/collector/config/configgrpc" + "go.opentelemetry.io/collector/config/confighttp" + "go.opentelemetry.io/collector/config/configtls" + "go.opentelemetry.io/collector/exporter" + "go.opentelemetry.io/collector/exporter/otlpexporter" + "go.opentelemetry.io/collector/exporter/otlphttpexporter" + "go.opentelemetry.io/collector/pdata/ptrace" + "go.opentelemetry.io/collector/pdata/testdata" + metricnoop "go.opentelemetry.io/otel/metric/noop" + tracenoop "go.opentelemetry.io/otel/trace/noop" + "go.uber.org/zap" "google.golang.org/genproto/googleapis/rpc/errdetails" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" "google.golang.org/protobuf/types/known/durationpb" + + "github.com/grafana/tempo/pkg/tempopb" ) +// These tests use the OpenTelemetry Collector Exporters to validate the different protocols +func TestShim_integration(t *testing.T) { + randomTraces := testdata.GenerateTraces(5) + + testCases := []struct { + name string + receiverCfg map[string]interface{} + factory exporter.Factory + exporterCfg component.Config + expectedTransport string + }{ + { + name: "otlpexporter", + receiverCfg: map[string]interface{}{ + "otlp": map[string]interface{}{ + "protocols": map[string]interface{}{ + "grpc": nil, + }, + }, + }, + factory: otlpexporter.NewFactory(), + exporterCfg: &otlpexporter.Config{ + ClientConfig: configgrpc.ClientConfig{ + Endpoint: "127.0.0.1:4317", + TLSSetting: configtls.ClientConfig{ + Insecure: true, + }, + }, + }, + expectedTransport: "grpc", + }, + { + name: "otlphttpexporter - JSON encoding", + receiverCfg: map[string]interface{}{ + "otlp": map[string]interface{}{ + "protocols": map[string]interface{}{ + "http": nil, + }, + }, + }, + factory: otlphttpexporter.NewFactory(), + exporterCfg: &otlphttpexporter.Config{ + ClientConfig: confighttp.ClientConfig{ + Endpoint: "http://127.0.0.1:4318", + }, + Encoding: otlphttpexporter.EncodingJSON, + }, + expectedTransport: "http", + }, + { + name: "otlphttpexporter - proto encoding", + receiverCfg: map[string]interface{}{ + "otlp": map[string]interface{}{ + "protocols": map[string]interface{}{ + "http": nil, + }, + }, + }, + factory: otlphttpexporter.NewFactory(), + exporterCfg: &otlphttpexporter.Config{ + ClientConfig: confighttp.ClientConfig{ + Endpoint: "http://127.0.0.1:4318", + }, + Encoding: otlphttpexporter.EncodingProto, + }, + expectedTransport: "http", + }, + } + for _, testCase := range testCases { + t.Run(testCase.name, func(t *testing.T) { + pusher := &capturingPusher{} + reg := prometheus.NewPedanticRegistry() + + stopShim := runReceiverShim(t, testCase.receiverCfg, pusher, reg) + defer stopShim() + + exporter, stopExporter := runOTelExporter(t, testCase.factory, testCase.exporterCfg) + defer stopExporter() + + err := exporter.ConsumeTraces(context.Background(), randomTraces) + assert.NoError(t, err) + + receivedTraces := pusher.GetAndClearTraces() + // We should only have received one push request + require.Len(t, receivedTraces, 1) + + assert.Equal(t, randomTraces, receivedTraces[0]) + + count, err := testutil.GatherAndCount(reg, "tempo_receiver_accepted_spans", "tempo_receiver_refused_spans") + assert.NoError(t, err) + assert.Equal(t, 2, count) + + expected := ` +# HELP tempo_receiver_accepted_spans Number of spans successfully pushed into the pipeline. +# TYPE tempo_receiver_accepted_spans counter +tempo_receiver_accepted_spans{receiver="tempo/otlp_receiver", transport=""} 5 +# HELP tempo_receiver_refused_spans Number of spans that could not be pushed into the pipeline. +# TYPE tempo_receiver_refused_spans counter +tempo_receiver_refused_spans{receiver="tempo/otlp_receiver", transport=""} 0 +` + expectedWithTransport := strings.ReplaceAll(expected, "", testCase.expectedTransport) + + err = testutil.GatherAndCompare(reg, strings.NewReader(expectedWithTransport), "tempo_receiver_accepted_spans", "tempo_receiver_refused_spans") + assert.NoError(t, err) + }) + } +} + +func runReceiverShim(t *testing.T, receiverCfg map[string]interface{}, pusher TracesPusher, reg prometheus.Registerer) func() { + level := dslog.Level{} + _ = level.Set("info") + + shim, err := New(receiverCfg, pusher, FakeTenantMiddleware(), 0, level, reg) + require.NoError(t, err) + + err = services.StartAndAwaitRunning(context.Background(), shim) + require.NoError(t, err) + + return func() { + err := services.StopAndAwaitTerminated(context.Background(), shim) + if errors.Is(err, context.Canceled) { + return + } + assert.NoError(t, err) + } +} + +func runOTelExporter(t *testing.T, factory exporter.Factory, cfg component.Config) (exporter.Traces, func()) { + exporter, err := factory.CreateTracesExporter( + context.Background(), + exporter.CreateSettings{ + ID: component.MustNewID("test"), + TelemetrySettings: component.TelemetrySettings{ + Logger: zap.NewNop(), + TracerProvider: tracenoop.NewTracerProvider(), + MeterProvider: metricnoop.NewMeterProvider(), + }, + }, + cfg, + ) + require.NoError(t, err) + + err = exporter.Start(context.Background(), &mockHost{}) + require.NoError(t, err) + + return exporter, func() { + err = exporter.Shutdown(context.Background()) + assert.NoError(t, err, "traces exporter shutting down failed") + } +} + +type mockHost struct{} + +var _ component.Host = (*mockHost)(nil) + +func (m *mockHost) GetFactory(component.Kind, component.Type) component.Factory { + panic("implement me") +} + +func (m *mockHost) GetExtensions() map[component.ID]component.Component { + panic("implement me") +} + +type capturingPusher struct { + traces []ptrace.Traces +} + +func (p *capturingPusher) GetAndClearTraces() []ptrace.Traces { + traces := p.traces + p.traces = nil + return traces +} + +func (p *capturingPusher) PushTraces(_ context.Context, t ptrace.Traces) (*tempopb.PushResponse, error) { + p.traces = append(p.traces, t) + return &tempopb.PushResponse{}, nil +} + // TestWrapRetryableError confirms that errors are wrapped as expected func TestWrapRetryableError(t *testing.T) { // no wrapping b/c not a grpc error diff --git a/vendor/go.opentelemetry.io/collector/exporter/otlphttpexporter/LICENSE b/vendor/go.opentelemetry.io/collector/exporter/otlphttpexporter/LICENSE new file mode 100644 index 00000000000..d6456956733 --- /dev/null +++ b/vendor/go.opentelemetry.io/collector/exporter/otlphttpexporter/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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. diff --git a/vendor/go.opentelemetry.io/collector/exporter/otlphttpexporter/Makefile b/vendor/go.opentelemetry.io/collector/exporter/otlphttpexporter/Makefile new file mode 100644 index 00000000000..ded7a36092d --- /dev/null +++ b/vendor/go.opentelemetry.io/collector/exporter/otlphttpexporter/Makefile @@ -0,0 +1 @@ +include ../../Makefile.Common diff --git a/vendor/go.opentelemetry.io/collector/exporter/otlphttpexporter/README.md b/vendor/go.opentelemetry.io/collector/exporter/otlphttpexporter/README.md new file mode 100644 index 00000000000..fb15c0dab7a --- /dev/null +++ b/vendor/go.opentelemetry.io/collector/exporter/otlphttpexporter/README.md @@ -0,0 +1,69 @@ +# OTLP/HTTP Exporter + + +| Status | | +| ------------- |-----------| +| Stability | [beta]: logs | +| | [stable]: traces, metrics | +| Distributions | [core], [contrib] | +| Issues | [![Open issues](https://img.shields.io/github/issues-search/open-telemetry/opentelemetry-collector-contrib?query=is%3Aissue%20is%3Aopen%20label%3Aexporter%2Fotlphttp%20&label=open&color=orange&logo=opentelemetry)](https://github.com/open-telemetry/opentelemetry-collector-contrib/issues?q=is%3Aopen+is%3Aissue+label%3Aexporter%2Fotlphttp) [![Closed issues](https://img.shields.io/github/issues-search/open-telemetry/opentelemetry-collector-contrib?query=is%3Aissue%20is%3Aclosed%20label%3Aexporter%2Fotlphttp%20&label=closed&color=blue&logo=opentelemetry)](https://github.com/open-telemetry/opentelemetry-collector-contrib/issues?q=is%3Aclosed+is%3Aissue+label%3Aexporter%2Fotlphttp) | + +[beta]: https://github.com/open-telemetry/opentelemetry-collector#beta +[stable]: https://github.com/open-telemetry/opentelemetry-collector#stable +[core]: https://github.com/open-telemetry/opentelemetry-collector-releases/tree/main/distributions/otelcol +[contrib]: https://github.com/open-telemetry/opentelemetry-collector-releases/tree/main/distributions/otelcol-contrib + + +Export traces and/or metrics via HTTP using [OTLP]( +https://github.com/open-telemetry/opentelemetry-proto/blob/main/docs/specification.md) +format. + +The following settings are required: + +- `endpoint` (no default): The target base URL to send data to (e.g.: https://example.com:4318). + To send each signal a corresponding path will be added to this base URL, i.e. for traces + "/v1/traces" will appended, for metrics "/v1/metrics" will be appended, for logs + "/v1/logs" will be appended. + +The following settings can be optionally configured: + +- `traces_endpoint` (no default): The target URL to send trace data to (e.g.: https://example.com:4318/v1/traces). + If this setting is present the `endpoint` setting is ignored for traces. +- `metrics_endpoint` (no default): The target URL to send metric data to (e.g.: https://example.com:4318/v1/metrics). + If this setting is present the `endpoint` setting is ignored for metrics. +- `logs_endpoint` (no default): The target URL to send log data to (e.g.: https://example.com:4318/v1/logs). + If this setting is present the `endpoint` setting is ignored logs. +- `tls`: see [TLS Configuration Settings](../../config/configtls/README.md) for the full set of available options. +- `timeout` (default = 30s): HTTP request time limit. For details see https://golang.org/pkg/net/http/#Client +- `read_buffer_size` (default = 0): ReadBufferSize for HTTP client. +- `write_buffer_size` (default = 512 * 1024): WriteBufferSize for HTTP client. +- `encoding` (default = proto): The encoding to use for the messages (valid options: `proto`, `json`) + +Example: + +```yaml +exporters: + otlphttp: + endpoint: https://example.com:4318 +``` + +By default `gzip` compression is enabled. See [compression comparison](../../config/configgrpc/README.md#compression-comparison) for details benchmark information. To disable, configure as follows: + +```yaml +exporters: + otlphttp: + ... + compression: none +``` + +By default `proto` encoding is used, to change the content encoding of the message configure it as follows: + +```yaml +exporters: + otlphttp: + ... + encoding: json +``` + +The full list of settings exposed for this exporter are documented [here](./config.go) +with detailed sample configurations [here](./testdata/config.yaml). diff --git a/vendor/go.opentelemetry.io/collector/exporter/otlphttpexporter/config.go b/vendor/go.opentelemetry.io/collector/exporter/otlphttpexporter/config.go new file mode 100644 index 00000000000..ef59fc324a0 --- /dev/null +++ b/vendor/go.opentelemetry.io/collector/exporter/otlphttpexporter/config.go @@ -0,0 +1,73 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package otlphttpexporter // import "go.opentelemetry.io/collector/exporter/otlphttpexporter" + +import ( + "encoding" + "errors" + "fmt" + + "go.opentelemetry.io/collector/component" + "go.opentelemetry.io/collector/config/confighttp" + "go.opentelemetry.io/collector/config/configretry" + "go.opentelemetry.io/collector/exporter/exporterhelper" +) + +// EncodingType defines the type for content encoding +type EncodingType string + +const ( + EncodingProto EncodingType = "proto" + EncodingJSON EncodingType = "json" +) + +var _ encoding.TextUnmarshaler = (*EncodingType)(nil) + +// UnmarshalText unmarshalls text to an EncodingType. +func (e *EncodingType) UnmarshalText(text []byte) error { + if e == nil { + return errors.New("cannot unmarshal to a nil *EncodingType") + } + + str := string(text) + switch str { + case string(EncodingProto): + *e = EncodingProto + case string(EncodingJSON): + *e = EncodingJSON + default: + return fmt.Errorf("invalid encoding type: %s", str) + } + + return nil +} + +// Config defines configuration for OTLP/HTTP exporter. +type Config struct { + confighttp.ClientConfig `mapstructure:",squash"` // squash ensures fields are correctly decoded in embedded struct. + QueueConfig exporterhelper.QueueSettings `mapstructure:"sending_queue"` + RetryConfig configretry.BackOffConfig `mapstructure:"retry_on_failure"` + + // The URL to send traces to. If omitted the Endpoint + "/v1/traces" will be used. + TracesEndpoint string `mapstructure:"traces_endpoint"` + + // The URL to send metrics to. If omitted the Endpoint + "/v1/metrics" will be used. + MetricsEndpoint string `mapstructure:"metrics_endpoint"` + + // The URL to send logs to. If omitted the Endpoint + "/v1/logs" will be used. + LogsEndpoint string `mapstructure:"logs_endpoint"` + + // The encoding to export telemetry (default: "proto") + Encoding EncodingType `mapstructure:"encoding"` +} + +var _ component.Config = (*Config)(nil) + +// Validate checks if the exporter configuration is valid +func (cfg *Config) Validate() error { + if cfg.Endpoint == "" && cfg.TracesEndpoint == "" && cfg.MetricsEndpoint == "" && cfg.LogsEndpoint == "" { + return errors.New("at least one endpoint must be specified") + } + return nil +} diff --git a/vendor/go.opentelemetry.io/collector/exporter/otlphttpexporter/doc.go b/vendor/go.opentelemetry.io/collector/exporter/otlphttpexporter/doc.go new file mode 100644 index 00000000000..16eccd7cbd4 --- /dev/null +++ b/vendor/go.opentelemetry.io/collector/exporter/otlphttpexporter/doc.go @@ -0,0 +1,7 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +//go:generate mdatagen metadata.yaml + +// Package otlphttpexporter exports data by using the OTLP format to an HTTP endpoint. +package otlphttpexporter // import "go.opentelemetry.io/collector/exporter/otlphttpexporter" diff --git a/vendor/go.opentelemetry.io/collector/exporter/otlphttpexporter/factory.go b/vendor/go.opentelemetry.io/collector/exporter/otlphttpexporter/factory.go new file mode 100644 index 00000000000..9ebcc01fba1 --- /dev/null +++ b/vendor/go.opentelemetry.io/collector/exporter/otlphttpexporter/factory.go @@ -0,0 +1,146 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package otlphttpexporter // import "go.opentelemetry.io/collector/exporter/otlphttpexporter" + +import ( + "context" + "fmt" + "net/url" + "strings" + "time" + + "go.opentelemetry.io/collector/component" + "go.opentelemetry.io/collector/config/configcompression" + "go.opentelemetry.io/collector/config/confighttp" + "go.opentelemetry.io/collector/config/configopaque" + "go.opentelemetry.io/collector/config/configretry" + "go.opentelemetry.io/collector/consumer" + "go.opentelemetry.io/collector/exporter" + "go.opentelemetry.io/collector/exporter/exporterhelper" + "go.opentelemetry.io/collector/exporter/otlphttpexporter/internal/metadata" +) + +// NewFactory creates a factory for OTLP exporter. +func NewFactory() exporter.Factory { + return exporter.NewFactory( + metadata.Type, + createDefaultConfig, + exporter.WithTraces(createTracesExporter, metadata.TracesStability), + exporter.WithMetrics(createMetricsExporter, metadata.MetricsStability), + exporter.WithLogs(createLogsExporter, metadata.LogsStability), + ) +} + +func createDefaultConfig() component.Config { + return &Config{ + RetryConfig: configretry.NewDefaultBackOffConfig(), + QueueConfig: exporterhelper.NewDefaultQueueSettings(), + Encoding: EncodingProto, + ClientConfig: confighttp.ClientConfig{ + Endpoint: "", + Timeout: 30 * time.Second, + Headers: map[string]configopaque.String{}, + // Default to gzip compression + Compression: configcompression.TypeGzip, + // We almost read 0 bytes, so no need to tune ReadBufferSize. + WriteBufferSize: 512 * 1024, + }, + } +} + +func composeSignalURL(oCfg *Config, signalOverrideURL string, signalName string) (string, error) { + switch { + case signalOverrideURL != "": + _, err := url.Parse(signalOverrideURL) + if err != nil { + return "", fmt.Errorf("%s_endpoint must be a valid URL", signalName) + } + return signalOverrideURL, nil + case oCfg.Endpoint == "": + return "", fmt.Errorf("either endpoint or %s_endpoint must be specified", signalName) + default: + if strings.HasSuffix(oCfg.Endpoint, "/") { + return oCfg.Endpoint + "v1/" + signalName, nil + } + return oCfg.Endpoint + "/v1/" + signalName, nil + } +} + +func createTracesExporter( + ctx context.Context, + set exporter.CreateSettings, + cfg component.Config, +) (exporter.Traces, error) { + oce, err := newExporter(cfg, set) + if err != nil { + return nil, err + } + oCfg := cfg.(*Config) + + oce.tracesURL, err = composeSignalURL(oCfg, oCfg.TracesEndpoint, "traces") + if err != nil { + return nil, err + } + + return exporterhelper.NewTracesExporter(ctx, set, cfg, + oce.pushTraces, + exporterhelper.WithStart(oce.start), + exporterhelper.WithCapabilities(consumer.Capabilities{MutatesData: false}), + // explicitly disable since we rely on http.Client timeout logic. + exporterhelper.WithTimeout(exporterhelper.TimeoutSettings{Timeout: 0}), + exporterhelper.WithRetry(oCfg.RetryConfig), + exporterhelper.WithQueue(oCfg.QueueConfig)) +} + +func createMetricsExporter( + ctx context.Context, + set exporter.CreateSettings, + cfg component.Config, +) (exporter.Metrics, error) { + oce, err := newExporter(cfg, set) + if err != nil { + return nil, err + } + oCfg := cfg.(*Config) + + oce.metricsURL, err = composeSignalURL(oCfg, oCfg.MetricsEndpoint, "metrics") + if err != nil { + return nil, err + } + + return exporterhelper.NewMetricsExporter(ctx, set, cfg, + oce.pushMetrics, + exporterhelper.WithStart(oce.start), + exporterhelper.WithCapabilities(consumer.Capabilities{MutatesData: false}), + // explicitly disable since we rely on http.Client timeout logic. + exporterhelper.WithTimeout(exporterhelper.TimeoutSettings{Timeout: 0}), + exporterhelper.WithRetry(oCfg.RetryConfig), + exporterhelper.WithQueue(oCfg.QueueConfig)) +} + +func createLogsExporter( + ctx context.Context, + set exporter.CreateSettings, + cfg component.Config, +) (exporter.Logs, error) { + oce, err := newExporter(cfg, set) + if err != nil { + return nil, err + } + oCfg := cfg.(*Config) + + oce.logsURL, err = composeSignalURL(oCfg, oCfg.LogsEndpoint, "logs") + if err != nil { + return nil, err + } + + return exporterhelper.NewLogsExporter(ctx, set, cfg, + oce.pushLogs, + exporterhelper.WithStart(oce.start), + exporterhelper.WithCapabilities(consumer.Capabilities{MutatesData: false}), + // explicitly disable since we rely on http.Client timeout logic. + exporterhelper.WithTimeout(exporterhelper.TimeoutSettings{Timeout: 0}), + exporterhelper.WithRetry(oCfg.RetryConfig), + exporterhelper.WithQueue(oCfg.QueueConfig)) +} diff --git a/vendor/go.opentelemetry.io/collector/exporter/otlphttpexporter/internal/metadata/generated_status.go b/vendor/go.opentelemetry.io/collector/exporter/otlphttpexporter/internal/metadata/generated_status.go new file mode 100644 index 00000000000..8af2905a456 --- /dev/null +++ b/vendor/go.opentelemetry.io/collector/exporter/otlphttpexporter/internal/metadata/generated_status.go @@ -0,0 +1,17 @@ +// Code generated by mdatagen. DO NOT EDIT. + +package metadata + +import ( + "go.opentelemetry.io/collector/component" +) + +var ( + Type = component.MustNewType("otlphttp") +) + +const ( + LogsStability = component.StabilityLevelBeta + TracesStability = component.StabilityLevelStable + MetricsStability = component.StabilityLevelStable +) diff --git a/vendor/go.opentelemetry.io/collector/exporter/otlphttpexporter/metadata.yaml b/vendor/go.opentelemetry.io/collector/exporter/otlphttpexporter/metadata.yaml new file mode 100644 index 00000000000..5e1c41d3243 --- /dev/null +++ b/vendor/go.opentelemetry.io/collector/exporter/otlphttpexporter/metadata.yaml @@ -0,0 +1,13 @@ +type: otlphttp + +status: + class: exporter + stability: + stable: [traces, metrics] + beta: [logs] + distributions: [core, contrib] + +tests: + config: + endpoint: "https://1.2.3.4:1234" + diff --git a/vendor/go.opentelemetry.io/collector/exporter/otlphttpexporter/otlp.go b/vendor/go.opentelemetry.io/collector/exporter/otlphttpexporter/otlp.go new file mode 100644 index 00000000000..ea02be512f4 --- /dev/null +++ b/vendor/go.opentelemetry.io/collector/exporter/otlphttpexporter/otlp.go @@ -0,0 +1,394 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package otlphttpexporter // import "go.opentelemetry.io/collector/exporter/otlphttpexporter" + +import ( + "bytes" + "context" + "errors" + "fmt" + "io" + "net/http" + "net/url" + "runtime" + "strconv" + "time" + + "go.uber.org/zap" + "google.golang.org/genproto/googleapis/rpc/status" + "google.golang.org/protobuf/proto" + + "go.opentelemetry.io/collector/component" + "go.opentelemetry.io/collector/consumer/consumererror" + "go.opentelemetry.io/collector/exporter" + "go.opentelemetry.io/collector/exporter/exporterhelper" + "go.opentelemetry.io/collector/internal/httphelper" + "go.opentelemetry.io/collector/pdata/plog" + "go.opentelemetry.io/collector/pdata/plog/plogotlp" + "go.opentelemetry.io/collector/pdata/pmetric" + "go.opentelemetry.io/collector/pdata/pmetric/pmetricotlp" + "go.opentelemetry.io/collector/pdata/ptrace" + "go.opentelemetry.io/collector/pdata/ptrace/ptraceotlp" +) + +type baseExporter struct { + // Input configuration. + config *Config + client *http.Client + tracesURL string + metricsURL string + logsURL string + logger *zap.Logger + settings component.TelemetrySettings + // Default user-agent header. + userAgent string +} + +const ( + headerRetryAfter = "Retry-After" + maxHTTPResponseReadBytes = 64 * 1024 + + jsonContentType = "application/json" + protobufContentType = "application/x-protobuf" +) + +// Create new exporter. +func newExporter(cfg component.Config, set exporter.CreateSettings) (*baseExporter, error) { + oCfg := cfg.(*Config) + + if oCfg.Endpoint != "" { + _, err := url.Parse(oCfg.Endpoint) + if err != nil { + return nil, errors.New("endpoint must be a valid URL") + } + } + + userAgent := fmt.Sprintf("%s/%s (%s/%s)", + set.BuildInfo.Description, set.BuildInfo.Version, runtime.GOOS, runtime.GOARCH) + + // client construction is deferred to start + return &baseExporter{ + config: oCfg, + logger: set.Logger, + userAgent: userAgent, + settings: set.TelemetrySettings, + }, nil +} + +// start actually creates the HTTP client. The client construction is deferred till this point as this +// is the only place we get hold of Extensions which are required to construct auth round tripper. +func (e *baseExporter) start(ctx context.Context, host component.Host) error { + client, err := e.config.ClientConfig.ToClient(ctx, host, e.settings) + if err != nil { + return err + } + e.client = client + return nil +} + +func (e *baseExporter) pushTraces(ctx context.Context, td ptrace.Traces) error { + tr := ptraceotlp.NewExportRequestFromTraces(td) + + var err error + var request []byte + switch e.config.Encoding { + case EncodingJSON: + request, err = tr.MarshalJSON() + case EncodingProto: + request, err = tr.MarshalProto() + default: + err = fmt.Errorf("invalid encoding: %s", e.config.Encoding) + } + + if err != nil { + return consumererror.NewPermanent(err) + } + + return e.export(ctx, e.tracesURL, request, e.tracesPartialSuccessHandler) +} + +func (e *baseExporter) pushMetrics(ctx context.Context, md pmetric.Metrics) error { + tr := pmetricotlp.NewExportRequestFromMetrics(md) + + var err error + var request []byte + switch e.config.Encoding { + case EncodingJSON: + request, err = tr.MarshalJSON() + case EncodingProto: + request, err = tr.MarshalProto() + default: + err = fmt.Errorf("invalid encoding: %s", e.config.Encoding) + } + + if err != nil { + return consumererror.NewPermanent(err) + } + return e.export(ctx, e.metricsURL, request, e.metricsPartialSuccessHandler) +} + +func (e *baseExporter) pushLogs(ctx context.Context, ld plog.Logs) error { + tr := plogotlp.NewExportRequestFromLogs(ld) + + var err error + var request []byte + switch e.config.Encoding { + case EncodingJSON: + request, err = tr.MarshalJSON() + case EncodingProto: + request, err = tr.MarshalProto() + default: + err = fmt.Errorf("invalid encoding: %s", e.config.Encoding) + } + + if err != nil { + return consumererror.NewPermanent(err) + } + + return e.export(ctx, e.logsURL, request, e.logsPartialSuccessHandler) +} + +func (e *baseExporter) export(ctx context.Context, url string, request []byte, partialSuccessHandler partialSuccessHandler) error { + e.logger.Debug("Preparing to make HTTP request", zap.String("url", url)) + req, err := http.NewRequestWithContext(ctx, http.MethodPost, url, bytes.NewReader(request)) + if err != nil { + return consumererror.NewPermanent(err) + } + + switch e.config.Encoding { + case EncodingJSON: + req.Header.Set("Content-Type", jsonContentType) + case EncodingProto: + req.Header.Set("Content-Type", protobufContentType) + default: + return fmt.Errorf("invalid encoding: %s", e.config.Encoding) + } + + req.Header.Set("User-Agent", e.userAgent) + + resp, err := e.client.Do(req) + if err != nil { + return fmt.Errorf("failed to make an HTTP request: %w", err) + } + + defer func() { + // Discard any remaining response body when we are done reading. + io.CopyN(io.Discard, resp.Body, maxHTTPResponseReadBytes) // nolint:errcheck + resp.Body.Close() + }() + + if resp.StatusCode >= 200 && resp.StatusCode <= 299 { + return handlePartialSuccessResponse(resp, partialSuccessHandler) + } + + respStatus := readResponseStatus(resp) + + // Format the error message. Use the status if it is present in the response. + var errString string + var formattedErr error + if respStatus != nil { + errString = fmt.Sprintf( + "error exporting items, request to %s responded with HTTP Status Code %d, Message=%s, Details=%v", + url, resp.StatusCode, respStatus.Message, respStatus.Details) + } else { + errString = fmt.Sprintf( + "error exporting items, request to %s responded with HTTP Status Code %d", + url, resp.StatusCode) + } + formattedErr = httphelper.NewStatusFromMsgAndHTTPCode(errString, resp.StatusCode).Err() + + if isRetryableStatusCode(resp.StatusCode) { + // A retry duration of 0 seconds will trigger the default backoff policy + // of our caller (retry handler). + retryAfter := 0 + + // Check if the server is overwhelmed. + // See spec https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/protocol/otlp.md#otlphttp-throttling + isThrottleError := resp.StatusCode == http.StatusTooManyRequests || resp.StatusCode == http.StatusServiceUnavailable + if val := resp.Header.Get(headerRetryAfter); isThrottleError && val != "" { + if seconds, err2 := strconv.Atoi(val); err2 == nil { + retryAfter = seconds + } + } + + return exporterhelper.NewThrottleRetry(formattedErr, time.Duration(retryAfter)*time.Second) + } + + return consumererror.NewPermanent(formattedErr) +} + +// Determine if the status code is retryable according to the specification. +// For more, see https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/protocol/otlp.md#failures-1 +func isRetryableStatusCode(code int) bool { + switch code { + case http.StatusTooManyRequests: + return true + case http.StatusBadGateway: + return true + case http.StatusServiceUnavailable: + return true + case http.StatusGatewayTimeout: + return true + default: + return false + } +} + +func readResponseBody(resp *http.Response) ([]byte, error) { + if resp.ContentLength == 0 { + return nil, nil + } + + maxRead := resp.ContentLength + + // if maxRead == -1, the ContentLength header has not been sent, so read up to + // the maximum permitted body size. If it is larger than the permitted body + // size, still try to read from the body in case the value is an error. If the + // body is larger than the maximum size, proto unmarshaling will likely fail. + if maxRead == -1 || maxRead > maxHTTPResponseReadBytes { + maxRead = maxHTTPResponseReadBytes + } + protoBytes := make([]byte, maxRead) + n, err := io.ReadFull(resp.Body, protoBytes) + + // No bytes read and an EOF error indicates there is no body to read. + if n == 0 && (err == nil || errors.Is(err, io.EOF)) { + return nil, nil + } + + // io.ReadFull will return io.ErrorUnexpectedEOF if the Content-Length header + // wasn't set, since we will try to read past the length of the body. If this + // is the case, the body will still have the full message in it, so we want to + // ignore the error and parse the message. + if err != nil && !errors.Is(err, io.ErrUnexpectedEOF) { + return nil, err + } + + return protoBytes[:n], nil +} + +// Read the response and decode the status.Status from the body. +// Returns nil if the response is empty or cannot be decoded. +func readResponseStatus(resp *http.Response) *status.Status { + var respStatus *status.Status + if resp.StatusCode >= 400 && resp.StatusCode <= 599 { + // Request failed. Read the body. OTLP spec says: + // "Response body for all HTTP 4xx and HTTP 5xx responses MUST be a + // Protobuf-encoded Status message that describes the problem." + respBytes, err := readResponseBody(resp) + if err != nil { + return nil + } + + // Decode it as Status struct. See https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/protocol/otlp.md#failures + respStatus = &status.Status{} + err = proto.Unmarshal(respBytes, respStatus) + if err != nil { + return nil + } + } + + return respStatus +} + +func handlePartialSuccessResponse(resp *http.Response, partialSuccessHandler partialSuccessHandler) error { + bodyBytes, err := readResponseBody(resp) + if err != nil { + return err + } + + return partialSuccessHandler(bodyBytes, resp.Header.Get("Content-Type")) +} + +type partialSuccessHandler func(bytes []byte, contentType string) error + +func (e *baseExporter) tracesPartialSuccessHandler(protoBytes []byte, contentType string) error { + if protoBytes == nil { + return nil + } + exportResponse := ptraceotlp.NewExportResponse() + switch contentType { + case protobufContentType: + err := exportResponse.UnmarshalProto(protoBytes) + if err != nil { + return fmt.Errorf("error parsing protobuf response: %w", err) + } + case jsonContentType: + err := exportResponse.UnmarshalJSON(protoBytes) + if err != nil { + return fmt.Errorf("error parsing json response: %w", err) + } + default: + return nil + } + + partialSuccess := exportResponse.PartialSuccess() + if !(partialSuccess.ErrorMessage() == "" && partialSuccess.RejectedSpans() == 0) { + e.logger.Warn("Partial success response", + zap.String("message", exportResponse.PartialSuccess().ErrorMessage()), + zap.Int64("dropped_spans", exportResponse.PartialSuccess().RejectedSpans()), + ) + } + return nil +} + +func (e *baseExporter) metricsPartialSuccessHandler(protoBytes []byte, contentType string) error { + if protoBytes == nil { + return nil + } + exportResponse := pmetricotlp.NewExportResponse() + switch contentType { + case protobufContentType: + err := exportResponse.UnmarshalProto(protoBytes) + if err != nil { + return fmt.Errorf("error parsing protobuf response: %w", err) + } + case jsonContentType: + err := exportResponse.UnmarshalJSON(protoBytes) + if err != nil { + return fmt.Errorf("error parsing json response: %w", err) + } + default: + return nil + } + + partialSuccess := exportResponse.PartialSuccess() + if !(partialSuccess.ErrorMessage() == "" && partialSuccess.RejectedDataPoints() == 0) { + e.logger.Warn("Partial success response", + zap.String("message", exportResponse.PartialSuccess().ErrorMessage()), + zap.Int64("dropped_data_points", exportResponse.PartialSuccess().RejectedDataPoints()), + ) + } + return nil +} + +func (e *baseExporter) logsPartialSuccessHandler(protoBytes []byte, contentType string) error { + if protoBytes == nil { + return nil + } + exportResponse := plogotlp.NewExportResponse() + switch contentType { + case protobufContentType: + err := exportResponse.UnmarshalProto(protoBytes) + if err != nil { + return fmt.Errorf("error parsing protobuf response: %w", err) + } + case jsonContentType: + err := exportResponse.UnmarshalJSON(protoBytes) + if err != nil { + return fmt.Errorf("error parsing json response: %w", err) + } + default: + return nil + } + + partialSuccess := exportResponse.PartialSuccess() + if !(partialSuccess.ErrorMessage() == "" && partialSuccess.RejectedLogRecords() == 0) { + e.logger.Warn("Partial success response", + zap.String("message", exportResponse.PartialSuccess().ErrorMessage()), + zap.Int64("dropped_log_records", exportResponse.PartialSuccess().RejectedLogRecords()), + ) + } + return nil +} diff --git a/vendor/go.opentelemetry.io/collector/pdata/testdata/LICENSE b/vendor/go.opentelemetry.io/collector/pdata/testdata/LICENSE new file mode 100644 index 00000000000..d6456956733 --- /dev/null +++ b/vendor/go.opentelemetry.io/collector/pdata/testdata/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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. diff --git a/vendor/go.opentelemetry.io/collector/pdata/testdata/Makefile b/vendor/go.opentelemetry.io/collector/pdata/testdata/Makefile new file mode 100644 index 00000000000..ded7a36092d --- /dev/null +++ b/vendor/go.opentelemetry.io/collector/pdata/testdata/Makefile @@ -0,0 +1 @@ +include ../../Makefile.Common diff --git a/vendor/go.opentelemetry.io/collector/pdata/testdata/common.go b/vendor/go.opentelemetry.io/collector/pdata/testdata/common.go new file mode 100644 index 00000000000..b62cf24724d --- /dev/null +++ b/vendor/go.opentelemetry.io/collector/pdata/testdata/common.go @@ -0,0 +1,30 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package testdata + +import ( + "go.opentelemetry.io/collector/pdata/pcommon" +) + +func initMetricExemplarAttributes(dest pcommon.Map) { + dest.PutStr("exemplar-attachment", "exemplar-attachment-value") +} + +func initMetricAttributes1(dest pcommon.Map) { + dest.PutStr("label-1", "label-value-1") +} + +func initMetricAttributes2(dest pcommon.Map) { + dest.PutStr("label-2", "label-value-2") +} + +func initMetricAttributes12(dest pcommon.Map) { + initMetricAttributes1(dest) + initMetricAttributes2(dest) +} + +func initMetricAttributes13(dest pcommon.Map) { + initMetricAttributes1(dest) + dest.PutStr("label-3", "label-value-3") +} diff --git a/vendor/go.opentelemetry.io/collector/pdata/testdata/log.go b/vendor/go.opentelemetry.io/collector/pdata/testdata/log.go new file mode 100644 index 00000000000..5d2a0a194ff --- /dev/null +++ b/vendor/go.opentelemetry.io/collector/pdata/testdata/log.go @@ -0,0 +1,59 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package testdata + +import ( + "time" + + "go.opentelemetry.io/collector/pdata/pcommon" + "go.opentelemetry.io/collector/pdata/plog" +) + +var ( + logTimestamp = pcommon.NewTimestampFromTime(time.Date(2020, 2, 11, 20, 26, 13, 789, time.UTC)) +) + +func GenerateLogs(count int) plog.Logs { + ld := plog.NewLogs() + initResource(ld.ResourceLogs().AppendEmpty().Resource()) + logs := ld.ResourceLogs().At(0).ScopeLogs().AppendEmpty().LogRecords() + logs.EnsureCapacity(count) + for i := 0; i < count; i++ { + switch i % 2 { + case 0: + fillLogOne(logs.AppendEmpty()) + case 1: + fillLogTwo(logs.AppendEmpty()) + } + } + return ld +} + +func fillLogOne(log plog.LogRecord) { + log.SetTimestamp(logTimestamp) + log.SetDroppedAttributesCount(1) + log.SetSeverityNumber(plog.SeverityNumberInfo) + log.SetSeverityText("Info") + log.SetSpanID([8]byte{0x01, 0x02, 0x04, 0x08}) + log.SetTraceID([16]byte{0x08, 0x04, 0x02, 0x01}) + + attrs := log.Attributes() + attrs.PutStr("app", "server") + attrs.PutInt("instance_num", 1) + + log.Body().SetStr("This is a log message") +} + +func fillLogTwo(log plog.LogRecord) { + log.SetTimestamp(logTimestamp) + log.SetDroppedAttributesCount(1) + log.SetSeverityNumber(plog.SeverityNumberInfo) + log.SetSeverityText("Info") + + attrs := log.Attributes() + attrs.PutStr("customer", "acme") + attrs.PutStr("env", "dev") + + log.Body().SetStr("something happened") +} diff --git a/vendor/go.opentelemetry.io/collector/pdata/testdata/metric.go b/vendor/go.opentelemetry.io/collector/pdata/testdata/metric.go new file mode 100644 index 00000000000..4ccb091a398 --- /dev/null +++ b/vendor/go.opentelemetry.io/collector/pdata/testdata/metric.go @@ -0,0 +1,297 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package testdata + +import ( + "time" + + "go.opentelemetry.io/collector/pdata/pcommon" + "go.opentelemetry.io/collector/pdata/pmetric" +) + +var ( + metricStartTimestamp = pcommon.NewTimestampFromTime(time.Date(2020, 2, 11, 20, 26, 12, 321, time.UTC)) + metricExemplarTimestamp = pcommon.NewTimestampFromTime(time.Date(2020, 2, 11, 20, 26, 13, 123, time.UTC)) + metricTimestamp = pcommon.NewTimestampFromTime(time.Date(2020, 2, 11, 20, 26, 13, 789, time.UTC)) +) + +const ( + TestGaugeDoubleMetricName = "gauge-double" + TestGaugeIntMetricName = "gauge-int" + TestSumDoubleMetricName = "sum-double" + TestSumIntMetricName = "sum-int" + TestHistogramMetricName = "histogram" + TestExponentialHistogramMetricName = "exponential-histogram" + TestSummaryMetricName = "summary" +) + +func generateMetricsOneEmptyInstrumentationScope() pmetric.Metrics { + md := pmetric.NewMetrics() + initResource(md.ResourceMetrics().AppendEmpty().Resource()) + md.ResourceMetrics().At(0).ScopeMetrics().AppendEmpty() + return md +} + +func GenerateMetricsAllTypesEmpty() pmetric.Metrics { + md := generateMetricsOneEmptyInstrumentationScope() + ms := md.ResourceMetrics().At(0).ScopeMetrics().At(0).Metrics() + + doubleGauge := ms.AppendEmpty() + initMetric(doubleGauge, TestGaugeDoubleMetricName, pmetric.MetricTypeGauge) + doubleGauge.Gauge().DataPoints().AppendEmpty() + intGauge := ms.AppendEmpty() + initMetric(intGauge, TestGaugeIntMetricName, pmetric.MetricTypeGauge) + intGauge.Gauge().DataPoints().AppendEmpty() + doubleSum := ms.AppendEmpty() + initMetric(doubleSum, TestSumDoubleMetricName, pmetric.MetricTypeSum) + doubleSum.Sum().DataPoints().AppendEmpty() + intSum := ms.AppendEmpty() + initMetric(intSum, TestSumIntMetricName, pmetric.MetricTypeSum) + intSum.Sum().DataPoints().AppendEmpty() + histogram := ms.AppendEmpty() + initMetric(histogram, TestHistogramMetricName, pmetric.MetricTypeHistogram) + histogram.Histogram().DataPoints().AppendEmpty() + summary := ms.AppendEmpty() + initMetric(summary, TestSummaryMetricName, pmetric.MetricTypeSummary) + summary.Summary().DataPoints().AppendEmpty() + return md +} + +func GenerateMetricsMetricTypeInvalid() pmetric.Metrics { + md := generateMetricsOneEmptyInstrumentationScope() + initMetric(md.ResourceMetrics().At(0).ScopeMetrics().At(0).Metrics().AppendEmpty(), TestSumIntMetricName, pmetric.MetricTypeEmpty) + return md +} + +func GenerateMetricsAllTypes() pmetric.Metrics { + md := generateMetricsOneEmptyInstrumentationScope() + ms := md.ResourceMetrics().At(0).ScopeMetrics().At(0).Metrics() + initGaugeIntMetric(ms.AppendEmpty()) + initGaugeDoubleMetric(ms.AppendEmpty()) + initSumIntMetric(ms.AppendEmpty()) + initSumDoubleMetric(ms.AppendEmpty()) + initHistogramMetric(ms.AppendEmpty()) + initExponentialHistogramMetric(ms.AppendEmpty()) + initSummaryMetric(ms.AppendEmpty()) + return md +} + +func GenerateMetrics(count int) pmetric.Metrics { + md := generateMetricsOneEmptyInstrumentationScope() + ms := md.ResourceMetrics().At(0).ScopeMetrics().At(0).Metrics() + ms.EnsureCapacity(count) + for i := 0; i < count; i++ { + switch i % 7 { + case 0: + initGaugeIntMetric(ms.AppendEmpty()) + case 1: + initGaugeDoubleMetric(ms.AppendEmpty()) + case 2: + initSumIntMetric(ms.AppendEmpty()) + case 3: + initSumDoubleMetric(ms.AppendEmpty()) + case 4: + initHistogramMetric(ms.AppendEmpty()) + case 5: + initExponentialHistogramMetric(ms.AppendEmpty()) + case 6: + initSummaryMetric(ms.AppendEmpty()) + } + } + return md +} + +func initGaugeIntMetric(im pmetric.Metric) { + initMetric(im, TestGaugeIntMetricName, pmetric.MetricTypeGauge) + + idps := im.Gauge().DataPoints() + idp0 := idps.AppendEmpty() + initMetricAttributes1(idp0.Attributes()) + idp0.SetStartTimestamp(metricStartTimestamp) + idp0.SetTimestamp(metricTimestamp) + idp0.SetIntValue(123) + idp1 := idps.AppendEmpty() + initMetricAttributes2(idp1.Attributes()) + idp1.SetStartTimestamp(metricStartTimestamp) + idp1.SetTimestamp(metricTimestamp) + idp1.SetIntValue(456) +} + +func initGaugeDoubleMetric(im pmetric.Metric) { + initMetric(im, TestGaugeDoubleMetricName, pmetric.MetricTypeGauge) + + idps := im.Gauge().DataPoints() + idp0 := idps.AppendEmpty() + initMetricAttributes12(idp0.Attributes()) + idp0.SetStartTimestamp(metricStartTimestamp) + idp0.SetTimestamp(metricTimestamp) + idp0.SetDoubleValue(1.23) + idp1 := idps.AppendEmpty() + initMetricAttributes13(idp1.Attributes()) + idp1.SetStartTimestamp(metricStartTimestamp) + idp1.SetTimestamp(metricTimestamp) + idp1.SetDoubleValue(4.56) +} + +func initSumIntMetric(im pmetric.Metric) { + initMetric(im, TestSumIntMetricName, pmetric.MetricTypeSum) + + idps := im.Sum().DataPoints() + idp0 := idps.AppendEmpty() + initMetricAttributes1(idp0.Attributes()) + idp0.SetStartTimestamp(metricStartTimestamp) + idp0.SetTimestamp(metricTimestamp) + idp0.SetIntValue(123) + idp1 := idps.AppendEmpty() + initMetricAttributes2(idp1.Attributes()) + idp1.SetStartTimestamp(metricStartTimestamp) + idp1.SetTimestamp(metricTimestamp) + idp1.SetIntValue(456) +} + +func initSumDoubleMetric(dm pmetric.Metric) { + initMetric(dm, TestSumDoubleMetricName, pmetric.MetricTypeSum) + + ddps := dm.Sum().DataPoints() + ddp0 := ddps.AppendEmpty() + initMetricAttributes12(ddp0.Attributes()) + ddp0.SetStartTimestamp(metricStartTimestamp) + ddp0.SetTimestamp(metricTimestamp) + ddp0.SetDoubleValue(1.23) + + ddp1 := ddps.AppendEmpty() + initMetricAttributes13(ddp1.Attributes()) + ddp1.SetStartTimestamp(metricStartTimestamp) + ddp1.SetTimestamp(metricTimestamp) + ddp1.SetDoubleValue(4.56) +} + +func initHistogramMetric(hm pmetric.Metric) { + initMetric(hm, TestHistogramMetricName, pmetric.MetricTypeHistogram) + + hdps := hm.Histogram().DataPoints() + hdp0 := hdps.AppendEmpty() + initMetricAttributes13(hdp0.Attributes()) + hdp0.SetStartTimestamp(metricStartTimestamp) + hdp0.SetTimestamp(metricTimestamp) + hdp0.SetCount(1) + hdp0.SetSum(15) + + hdp1 := hdps.AppendEmpty() + initMetricAttributes2(hdp1.Attributes()) + hdp1.SetStartTimestamp(metricStartTimestamp) + hdp1.SetTimestamp(metricTimestamp) + hdp1.SetCount(1) + hdp1.SetSum(15) + hdp1.SetMin(15) + hdp1.SetMax(15) + hdp1.BucketCounts().FromRaw([]uint64{0, 1}) + exemplar := hdp1.Exemplars().AppendEmpty() + exemplar.SetTraceID([16]byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10}) + exemplar.SetSpanID([8]byte{0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18}) + exemplar.SetTimestamp(metricExemplarTimestamp) + exemplar.SetDoubleValue(15) + initMetricExemplarAttributes(exemplar.FilteredAttributes()) + hdp1.ExplicitBounds().FromRaw([]float64{1}) +} + +func initExponentialHistogramMetric(hm pmetric.Metric) { + initMetric(hm, TestExponentialHistogramMetricName, pmetric.MetricTypeExponentialHistogram) + + hdps := hm.ExponentialHistogram().DataPoints() + hdp0 := hdps.AppendEmpty() + initMetricAttributes13(hdp0.Attributes()) + hdp0.SetStartTimestamp(metricStartTimestamp) + hdp0.SetTimestamp(metricTimestamp) + hdp0.SetCount(5) + hdp0.SetSum(0.15) + hdp0.SetZeroCount(1) + hdp0.SetScale(1) + + // positive index 1 and 2 are values sqrt(2), 2 at scale 1 + hdp0.Positive().SetOffset(1) + hdp0.Positive().BucketCounts().FromRaw([]uint64{1, 1}) + // negative index -1 and 0 are values -1/sqrt(2), -1 at scale 1 + hdp0.Negative().SetOffset(-1) + hdp0.Negative().BucketCounts().FromRaw([]uint64{1, 1}) + + // The above will print: + // Bucket (-1.414214, -1.000000], Count: 1 + // Bucket (-1.000000, -0.707107], Count: 1 + // Bucket [0, 0], Count: 1 + // Bucket [0.707107, 1.000000), Count: 1 + // Bucket [1.000000, 1.414214), Count: 1 + + hdp1 := hdps.AppendEmpty() + initMetricAttributes2(hdp1.Attributes()) + hdp1.SetStartTimestamp(metricStartTimestamp) + hdp1.SetTimestamp(metricTimestamp) + hdp1.SetCount(3) + hdp1.SetSum(1.25) + hdp1.SetMin(0) + hdp1.SetMax(1) + hdp1.SetZeroCount(1) + hdp1.SetScale(-1) + + // index -1 and 0 are values 0.25, 1 at scale -1 + hdp1.Positive().SetOffset(-1) + hdp1.Positive().BucketCounts().FromRaw([]uint64{1, 1}) + + // The above will print: + // Bucket [0, 0], Count: 1 + // Bucket [0.250000, 1.000000), Count: 1 + // Bucket [1.000000, 4.000000), Count: 1 + + exemplar := hdp1.Exemplars().AppendEmpty() + exemplar.SetTraceID([16]byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10}) + exemplar.SetSpanID([8]byte{0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18}) + exemplar.SetTimestamp(metricExemplarTimestamp) + exemplar.SetIntValue(15) + initMetricExemplarAttributes(exemplar.FilteredAttributes()) +} + +func initSummaryMetric(sm pmetric.Metric) { + initMetric(sm, TestSummaryMetricName, pmetric.MetricTypeSummary) + + sdps := sm.Summary().DataPoints() + sdp0 := sdps.AppendEmpty() + initMetricAttributes13(sdp0.Attributes()) + sdp0.SetStartTimestamp(metricStartTimestamp) + sdp0.SetTimestamp(metricTimestamp) + sdp0.SetCount(1) + sdp0.SetSum(15) + + sdp1 := sdps.AppendEmpty() + initMetricAttributes2(sdp1.Attributes()) + sdp1.SetStartTimestamp(metricStartTimestamp) + sdp1.SetTimestamp(metricTimestamp) + sdp1.SetCount(1) + sdp1.SetSum(15) + + quantile := sdp1.QuantileValues().AppendEmpty() + quantile.SetQuantile(0.01) + quantile.SetValue(15) +} + +func initMetric(m pmetric.Metric, name string, ty pmetric.MetricType) { + m.SetName(name) + m.SetDescription("") + m.SetUnit("1") + switch ty { + case pmetric.MetricTypeGauge: + m.SetEmptyGauge() + case pmetric.MetricTypeSum: + sum := m.SetEmptySum() + sum.SetIsMonotonic(true) + sum.SetAggregationTemporality(pmetric.AggregationTemporalityCumulative) + case pmetric.MetricTypeHistogram: + histo := m.SetEmptyHistogram() + histo.SetAggregationTemporality(pmetric.AggregationTemporalityCumulative) + case pmetric.MetricTypeExponentialHistogram: + histo := m.SetEmptyExponentialHistogram() + histo.SetAggregationTemporality(pmetric.AggregationTemporalityDelta) + case pmetric.MetricTypeSummary: + m.SetEmptySummary() + } +} diff --git a/vendor/go.opentelemetry.io/collector/pdata/testdata/resource.go b/vendor/go.opentelemetry.io/collector/pdata/testdata/resource.go new file mode 100644 index 00000000000..cac7a404602 --- /dev/null +++ b/vendor/go.opentelemetry.io/collector/pdata/testdata/resource.go @@ -0,0 +1,10 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package testdata + +import "go.opentelemetry.io/collector/pdata/pcommon" + +func initResource(r pcommon.Resource) { + r.Attributes().PutStr("resource-attr", "resource-attr-val-1") +} diff --git a/vendor/go.opentelemetry.io/collector/pdata/testdata/trace.go b/vendor/go.opentelemetry.io/collector/pdata/testdata/trace.go new file mode 100644 index 00000000000..3d69ed3a425 --- /dev/null +++ b/vendor/go.opentelemetry.io/collector/pdata/testdata/trace.go @@ -0,0 +1,68 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package testdata + +import ( + "time" + + "go.opentelemetry.io/collector/pdata/pcommon" + "go.opentelemetry.io/collector/pdata/ptrace" +) + +var ( + spanStartTimestamp = pcommon.NewTimestampFromTime(time.Date(2020, 2, 11, 20, 26, 12, 321, time.UTC)) + spanEventTimestamp = pcommon.NewTimestampFromTime(time.Date(2020, 2, 11, 20, 26, 13, 123, time.UTC)) + spanEndTimestamp = pcommon.NewTimestampFromTime(time.Date(2020, 2, 11, 20, 26, 13, 789, time.UTC)) +) + +func GenerateTraces(spanCount int) ptrace.Traces { + td := ptrace.NewTraces() + initResource(td.ResourceSpans().AppendEmpty().Resource()) + ss := td.ResourceSpans().At(0).ScopeSpans().AppendEmpty().Spans() + ss.EnsureCapacity(spanCount) + for i := 0; i < spanCount; i++ { + switch i % 2 { + case 0: + fillSpanOne(ss.AppendEmpty()) + case 1: + fillSpanTwo(ss.AppendEmpty()) + } + } + return td +} + +func fillSpanOne(span ptrace.Span) { + span.SetName("operationA") + span.SetStartTimestamp(spanStartTimestamp) + span.SetEndTimestamp(spanEndTimestamp) + span.SetDroppedAttributesCount(1) + span.SetTraceID([16]byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10}) + span.SetSpanID([8]byte{0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18}) + evs := span.Events() + ev0 := evs.AppendEmpty() + ev0.SetTimestamp(spanEventTimestamp) + ev0.SetName("event-with-attr") + ev0.Attributes().PutStr("span-event-attr", "span-event-attr-val") + ev0.SetDroppedAttributesCount(2) + ev1 := evs.AppendEmpty() + ev1.SetTimestamp(spanEventTimestamp) + ev1.SetName("event") + ev1.SetDroppedAttributesCount(2) + span.SetDroppedEventsCount(1) + status := span.Status() + status.SetCode(ptrace.StatusCodeError) + status.SetMessage("status-cancelled") +} + +func fillSpanTwo(span ptrace.Span) { + span.SetName("operationB") + span.SetStartTimestamp(spanStartTimestamp) + span.SetEndTimestamp(spanEndTimestamp) + link0 := span.Links().AppendEmpty() + link0.Attributes().PutStr("span-link-attr", "span-link-attr-val") + link0.SetDroppedAttributesCount(4) + link1 := span.Links().AppendEmpty() + link1.SetDroppedAttributesCount(4) + span.SetDroppedLinksCount(3) +} diff --git a/vendor/modules.txt b/vendor/modules.txt index 0a96d010cc5..bfb0ae7df5b 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -1429,6 +1429,10 @@ go.opentelemetry.io/collector/exporter/internal/queue ## explicit; go 1.21.0 go.opentelemetry.io/collector/exporter/otlpexporter go.opentelemetry.io/collector/exporter/otlpexporter/internal/metadata +# go.opentelemetry.io/collector/exporter/otlphttpexporter v0.102.1 +## explicit; go 1.21.0 +go.opentelemetry.io/collector/exporter/otlphttpexporter +go.opentelemetry.io/collector/exporter/otlphttpexporter/internal/metadata # go.opentelemetry.io/collector/extension v0.102.1 ## explicit; go 1.21.0 go.opentelemetry.io/collector/extension @@ -1467,6 +1471,9 @@ go.opentelemetry.io/collector/pdata/pmetric go.opentelemetry.io/collector/pdata/pmetric/pmetricotlp go.opentelemetry.io/collector/pdata/ptrace go.opentelemetry.io/collector/pdata/ptrace/ptraceotlp +# go.opentelemetry.io/collector/pdata/testdata v0.102.1 +## explicit; go 1.21.0 +go.opentelemetry.io/collector/pdata/testdata # go.opentelemetry.io/collector/processor v0.102.1 ## explicit; go 1.21.0 go.opentelemetry.io/collector/processor