diff --git a/.github/dependabot.yml b/.github/dependabot.yml index de7125cd5..8d43af4ca 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -136,16 +136,6 @@ updates: schedule: day: sunday interval: weekly - - - package-ecosystem: gomod - directory: /exporters/otlp - labels: - - dependencies - - go - - "Skip Changelog" - schedule: - day: sunday - interval: weekly - package-ecosystem: gomod directory: /exporters/stdout diff --git a/CHANGELOG.md b/CHANGELOG.md index a97fa6792..5ecb1e69c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -106,6 +106,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm Handling of baggage is now done using the added `Baggage` type and related context functions (`ContextWithBaggage`, `ContextWithoutBaggage`, and `FromContext`) in that package. (TBD) - The `InstallNewPipeline` and `NewExportPipeline` creation functions in all the exporters (prometheus, otlp, stdout, jaeger, and zipkin) have been removed. These functions were deemed premature attempts to provide convenience that did not achieve this aim. (#1985) +- The `go.opentelemetry.io/otel/exporters/otlp` exporter has been removed. Use `go.opentelemetry.io/otel/exporters/otlp/otlptrace` instead. (#1990) ### Fixed diff --git a/Makefile b/Makefile index 356912c86..16ec7be02 100644 --- a/Makefile +++ b/Makefile @@ -150,7 +150,7 @@ lint-modules: | $(CROSSLINK) .PHONY: license-check license-check: - @licRes=$$(for f in $$(find . -type f \( -iname '*.go' -o -iname '*.sh' \) ! -path '**/third_party/*' ! -path './exporters/otlp/internal/opentelemetry-proto/*') ; do \ + @licRes=$$(for f in $$(find . -type f \( -iname '*.go' -o -iname '*.sh' \) ! -path '**/third_party/*') ; do \ awk '/Copyright The OpenTelemetry Authors|generated|GENERATED/ && NR<=3 { found=1; next } END { if (!found) print FILENAME }' $$f; \ done); \ if [ -n "$${licRes}" ]; then \ diff --git a/bridge/opencensus/go.mod b/bridge/opencensus/go.mod index 585221411..a607dc0c2 100644 --- a/bridge/opencensus/go.mod +++ b/bridge/opencensus/go.mod @@ -34,8 +34,6 @@ replace go.opentelemetry.io/otel/example/zipkin => ../../example/zipkin replace go.opentelemetry.io/otel/exporters/metric/prometheus => ../../exporters/metric/prometheus -replace go.opentelemetry.io/otel/exporters/otlp => ../../exporters/otlp - replace go.opentelemetry.io/otel/exporters/stdout => ../../exporters/stdout replace go.opentelemetry.io/otel/exporters/trace/jaeger => ../../exporters/trace/jaeger diff --git a/bridge/opentracing/go.mod b/bridge/opentracing/go.mod index 1e3e825a7..1cabea113 100644 --- a/bridge/opentracing/go.mod +++ b/bridge/opentracing/go.mod @@ -30,8 +30,6 @@ replace go.opentelemetry.io/otel/example/zipkin => ../../example/zipkin replace go.opentelemetry.io/otel/exporters/metric/prometheus => ../../exporters/metric/prometheus -replace go.opentelemetry.io/otel/exporters/otlp => ../../exporters/otlp - replace go.opentelemetry.io/otel/exporters/stdout => ../../exporters/stdout replace go.opentelemetry.io/otel/exporters/trace/jaeger => ../../exporters/trace/jaeger diff --git a/example/jaeger/go.mod b/example/jaeger/go.mod index ff9d694e0..6d4a8310a 100644 --- a/example/jaeger/go.mod +++ b/example/jaeger/go.mod @@ -34,8 +34,6 @@ replace go.opentelemetry.io/otel/example/zipkin => ../zipkin replace go.opentelemetry.io/otel/exporters/metric/prometheus => ../../exporters/metric/prometheus -replace go.opentelemetry.io/otel/exporters/otlp => ../../exporters/otlp - replace go.opentelemetry.io/otel/exporters/stdout => ../../exporters/stdout replace go.opentelemetry.io/otel/exporters/trace/zipkin => ../../exporters/trace/zipkin diff --git a/example/namedtracer/go.mod b/example/namedtracer/go.mod index 35245f151..9889d491b 100644 --- a/example/namedtracer/go.mod +++ b/example/namedtracer/go.mod @@ -35,8 +35,6 @@ replace go.opentelemetry.io/otel/example/zipkin => ../zipkin replace go.opentelemetry.io/otel/exporters/metric/prometheus => ../../exporters/metric/prometheus -replace go.opentelemetry.io/otel/exporters/otlp => ../../exporters/otlp - replace go.opentelemetry.io/otel/exporters/trace/jaeger => ../../exporters/trace/jaeger replace go.opentelemetry.io/otel/exporters/trace/zipkin => ../../exporters/trace/zipkin diff --git a/example/opencensus/go.mod b/example/opencensus/go.mod index c3193709d..bec3351a3 100644 --- a/example/opencensus/go.mod +++ b/example/opencensus/go.mod @@ -36,8 +36,6 @@ replace go.opentelemetry.io/otel/example/zipkin => ../zipkin replace go.opentelemetry.io/otel/exporters/metric/prometheus => ../../exporters/metric/prometheus -replace go.opentelemetry.io/otel/exporters/otlp => ../../exporters/otlp - replace go.opentelemetry.io/otel/exporters/trace/jaeger => ../../exporters/trace/jaeger replace go.opentelemetry.io/otel/exporters/trace/zipkin => ../../exporters/trace/zipkin diff --git a/example/otel-collector/go.mod b/example/otel-collector/go.mod index f8f54302e..94e889614 100644 --- a/example/otel-collector/go.mod +++ b/example/otel-collector/go.mod @@ -4,16 +4,13 @@ go 1.15 replace ( go.opentelemetry.io/otel => ../.. - go.opentelemetry.io/otel/exporters/otlp => ../../exporters/otlp go.opentelemetry.io/otel/sdk => ../../sdk ) require ( go.opentelemetry.io/otel v0.20.0 - go.opentelemetry.io/otel/exporters/otlp v0.20.0 - go.opentelemetry.io/otel/metric v0.20.0 + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v0.20.0 go.opentelemetry.io/otel/sdk v0.20.0 - go.opentelemetry.io/otel/sdk/metric v0.20.0 go.opentelemetry.io/otel/trace v0.20.0 google.golang.org/grpc v1.38.0 ) diff --git a/example/otel-collector/go.sum b/example/otel-collector/go.sum index e9bd67196..a40bb40cf 100644 --- a/example/otel-collector/go.sum +++ b/example/otel-collector/go.sum @@ -2,8 +2,6 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMT cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= -github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= -github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/cenkalti/backoff/v4 v4.1.1 h1:G2HAfAmvm/GcKan2oOQpBXOd2tT2G57ZnZGWa1PxPBQ= github.com/cenkalti/backoff/v4 v4.1.1/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= diff --git a/example/otel-collector/main.go b/example/otel-collector/main.go index 9998f3adc..042f04db4 100644 --- a/example/otel-collector/main.go +++ b/example/otel-collector/main.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -// Example using the OTLP exporter + collector + third-party backends. For +// Example using OTLP exporters + collector + third-party backends. For // information about using the exporter, see: // https://pkg.go.dev/go.opentelemetry.io/otel/exporters/otlp?tab=doc#example-package-Insecure package main @@ -27,14 +27,8 @@ import ( "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/attribute" - "go.opentelemetry.io/otel/exporters/otlp" - "go.opentelemetry.io/otel/exporters/otlp/otlpgrpc" - "go.opentelemetry.io/otel/metric" - "go.opentelemetry.io/otel/metric/global" + "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc" "go.opentelemetry.io/otel/propagation" - controller "go.opentelemetry.io/otel/sdk/metric/controller/basic" - processor "go.opentelemetry.io/otel/sdk/metric/processor/basic" - "go.opentelemetry.io/otel/sdk/metric/selector/simple" "go.opentelemetry.io/otel/sdk/resource" sdktrace "go.opentelemetry.io/otel/sdk/trace" semconv "go.opentelemetry.io/otel/semconv/v1.4.0" @@ -46,19 +40,6 @@ import ( func initProvider() func() { ctx := context.Background() - // If the OpenTelemetry Collector is running on a local cluster (minikube or - // microk8s), it should be accessible through the NodePort service at the - // `localhost:30080` endpoint. Otherwise, replace `localhost` with the - // endpoint of your cluster. If you run the app inside k8s, then you can - // probably connect directly to the service through dns - driver := otlpgrpc.NewDriver( - otlpgrpc.WithInsecure(), - otlpgrpc.WithEndpoint("localhost:30080"), - otlpgrpc.WithDialOption(grpc.WithBlock()), // useful for testing - ) - exp, err := otlp.New(ctx, driver) - handleErr(err, "failed to create exporter") - res, err := resource.New(ctx, resource.WithAttributes( // the service name used to display traces in backends @@ -67,32 +48,34 @@ func initProvider() func() { ) handleErr(err, "failed to create resource") - bsp := sdktrace.NewBatchSpanProcessor(exp) + // If the OpenTelemetry Collector is running on a local cluster (minikube or + // microk8s), it should be accessible through the NodePort service at the + // `localhost:30080` endpoint. Otherwise, replace `localhost` with the + // endpoint of your cluster. If you run the app inside k8s, then you can + // probably connect directly to the service through dns + + // Set up a trace exporter + traceExporter, err := otlptracegrpc.New(ctx, + otlptracegrpc.WithInsecure(), + otlptracegrpc.WithEndpoint("localhost:30080"), + otlptracegrpc.WithDialOption(grpc.WithBlock()), + ) + handleErr(err, "failed to create trace exporter") + + // Register the trace exporter with a TracerProvider, using a batch + // span processor to aggregate spans before export. + bsp := sdktrace.NewBatchSpanProcessor(traceExporter) tracerProvider := sdktrace.NewTracerProvider( sdktrace.WithSampler(sdktrace.AlwaysSample()), sdktrace.WithResource(res), sdktrace.WithSpanProcessor(bsp), ) - - cont := controller.New( - processor.New( - simple.NewWithExactDistribution(), - exp, - ), - controller.WithExporter(exp), - controller.WithCollectPeriod(2*time.Second), - ) + otel.SetTracerProvider(tracerProvider) // set global propagator to tracecontext (the default is no-op). otel.SetTextMapPropagator(propagation.TraceContext{}) - otel.SetTracerProvider(tracerProvider) - global.SetMeterProvider(cont.MeterProvider()) - handleErr(cont.Start(context.Background()), "failed to start controller") return func() { - // Push any last metric events to the exporter. - handleErr(cont.Stop(context.Background()), "failed to stop controller") - // Shutdown will flush any remaining spans and shut down the exporter. handleErr(tracerProvider.Shutdown(ctx), "failed to shutdown TracerProvider") } @@ -105,7 +88,6 @@ func main() { defer shutdown() tracer := otel.Tracer("test-tracer") - meter := global.Meter("test-meter") // labels represent additional key-value descriptors that can be bound to a // metric observer or recorder. @@ -115,14 +97,6 @@ func main() { attribute.String("labelC", "vanilla"), } - // Recorder metric example - valuerecorder := metric.Must(meter). - NewFloat64Counter( - "an_important_metric", - metric.WithDescription("Measures the cumulative epicness of the app"), - ).Bind(commonLabels...) - defer valuerecorder.Unbind() - // work begins ctx, span := tracer.Start( context.Background(), @@ -132,7 +106,6 @@ func main() { for i := 0; i < 10; i++ { _, iSpan := tracer.Start(ctx, fmt.Sprintf("Sample-%d", i)) log.Printf("Doing really hard work (%d / 10)\n", i+1) - valuerecorder.Add(ctx, 1.0) <-time.After(time.Second) iSpan.End() diff --git a/example/passthrough/go.mod b/example/passthrough/go.mod index 7c6da39c3..bd29551bf 100644 --- a/example/passthrough/go.mod +++ b/example/passthrough/go.mod @@ -38,8 +38,6 @@ replace go.opentelemetry.io/otel/example/zipkin => ../zipkin replace go.opentelemetry.io/otel/exporters/metric/prometheus => ../../exporters/metric/prometheus -replace go.opentelemetry.io/otel/exporters/otlp => ../../exporters/otlp - replace go.opentelemetry.io/otel/exporters/trace/jaeger => ../../exporters/trace/jaeger replace go.opentelemetry.io/otel/exporters/trace/zipkin => ../../exporters/trace/zipkin diff --git a/example/prom-collector/README.md b/example/prom-collector/README.md deleted file mode 100644 index 78b3a904f..000000000 --- a/example/prom-collector/README.md +++ /dev/null @@ -1,5 +0,0 @@ -# Prometheus Collector Example - -This example demonstrates a metrics export pipeline that supports -Prometheus (pull) and simultaneously exports OTLP to an OpenTelemetry -endpoint (push). diff --git a/example/prom-collector/go.mod b/example/prom-collector/go.mod deleted file mode 100644 index 717c031a8..000000000 --- a/example/prom-collector/go.mod +++ /dev/null @@ -1,66 +0,0 @@ -module go.opentelemetry.io/otel/example/prom-collector - -go 1.15 - -replace ( - go.opentelemetry.io/otel => ../.. - go.opentelemetry.io/otel/exporters/metric/prometheus => ../../exporters/metric/prometheus - go.opentelemetry.io/otel/exporters/otlp => ../../exporters/otlp - go.opentelemetry.io/otel/sdk => ../../sdk -) - -require ( - go.opentelemetry.io/otel v0.20.0 - go.opentelemetry.io/otel/exporters/metric/prometheus v0.20.0 - go.opentelemetry.io/otel/exporters/otlp v0.20.0 - go.opentelemetry.io/otel/metric v0.20.0 - go.opentelemetry.io/otel/sdk v0.20.0 - go.opentelemetry.io/otel/sdk/metric v0.20.0 - google.golang.org/grpc v1.38.0 -) - -replace go.opentelemetry.io/otel/bridge/opencensus => ../../bridge/opencensus - -replace go.opentelemetry.io/otel/bridge/opentracing => ../../bridge/opentracing - -replace go.opentelemetry.io/otel/example/jaeger => ../jaeger - -replace go.opentelemetry.io/otel/example/namedtracer => ../namedtracer - -replace go.opentelemetry.io/otel/example/opencensus => ../opencensus - -replace go.opentelemetry.io/otel/example/otel-collector => ../otel-collector - -replace go.opentelemetry.io/otel/example/prom-collector => ./ - -replace go.opentelemetry.io/otel/example/prometheus => ../prometheus - -replace go.opentelemetry.io/otel/example/zipkin => ../zipkin - -replace go.opentelemetry.io/otel/exporters/stdout => ../../exporters/stdout - -replace go.opentelemetry.io/otel/exporters/trace/jaeger => ../../exporters/trace/jaeger - -replace go.opentelemetry.io/otel/exporters/trace/zipkin => ../../exporters/trace/zipkin - -replace go.opentelemetry.io/otel/internal/tools => ../../internal/tools - -replace go.opentelemetry.io/otel/metric => ../../metric - -replace go.opentelemetry.io/otel/oteltest => ../../oteltest - -replace go.opentelemetry.io/otel/sdk/export/metric => ../../sdk/export/metric - -replace go.opentelemetry.io/otel/sdk/metric => ../../sdk/metric - -replace go.opentelemetry.io/otel/trace => ../../trace - -replace go.opentelemetry.io/otel/example/passthrough => ../passthrough - -replace go.opentelemetry.io/otel/exporters/otlp/otlptrace => ../../exporters/otlp/otlptrace - -replace go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc => ../../exporters/otlp/otlptrace/otlptracegrpc - -replace go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp => ../../exporters/otlp/otlptrace/otlptracehttp - -replace go.opentelemetry.io/otel/internal/metric => ../../internal/metric diff --git a/example/prom-collector/go.sum b/example/prom-collector/go.sum deleted file mode 100644 index 2988119b9..000000000 --- a/example/prom-collector/go.sum +++ /dev/null @@ -1,222 +0,0 @@ -cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= -github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= -github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= -github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= -github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= -github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= -github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= -github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= -github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= -github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= -github.com/cenkalti/backoff/v4 v4.1.1 h1:G2HAfAmvm/GcKan2oOQpBXOd2tT2G57ZnZGWa1PxPBQ= -github.com/cenkalti/backoff/v4 v4.1.1/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= -github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY= -github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= -github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= -github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= -github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= -github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= -github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= -github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= -github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= -github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= -github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= -github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= -github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= -github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= -github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= -github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= -github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ= -github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo= -github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= -github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= -github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= -github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= -github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= -github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= -github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= -github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= -github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= -github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= -github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= -github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= -github.com/prometheus/client_golang v1.11.0 h1:HNkLOAEQMIDv/K+04rukrLx6ch7msSRwf3/SASFAGtQ= -github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= -github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= -github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M= -github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= -github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= -github.com/prometheus/common v0.26.0 h1:iMAkS2TDoNWnKM+Kopnx/8tnEStIfpYA0ur0xQzzhMQ= -github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= -github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= -github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= -github.com/prometheus/procfs v0.6.0 h1:mxy4L2jP6qMonqmq+aTtOx1ifVWUgG/TAmntgbh3xv4= -github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= -github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= -github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= -github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= -github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= -github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= -github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -go.opentelemetry.io/proto/otlp v0.9.0 h1:C0g6TWmQYvjKRnljRULLWUVJGy8Uvu0NEL/5frY2/t4= -go.opentelemetry.io/proto/otlp v0.9.0/go.mod h1:1vKfU9rv61e9EVGthD1zNvUbiwPcimSsOPU9brfSHJg= -golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= -golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200822124328-c89045814202 h1:VvcQYSHwXgi7W+TpUR6A9g6Up98WAHf3f/ulnJ62IyA= -golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40 h1:JWgyZ1qgdTaF3N3oxC+MdTV7qvEEgHo3otj+HB5CM7Q= -golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= -golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= -golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 h1:+kGHl1aib/qcwaRi1CbqBZ1rk19r85MNUf8HaBghugY= -google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= -google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= -google.golang.org/grpc v1.37.1/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/grpc v1.38.0 h1:/9BgsAsa5nWe26HqOlvlgJnqBuktYOLCgjCPqsa56W0= -google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= -google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= -google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= -google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= -google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= -google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= -google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk= -google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= -gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/example/prom-collector/main.go b/example/prom-collector/main.go deleted file mode 100644 index 570f4b50f..000000000 --- a/example/prom-collector/main.go +++ /dev/null @@ -1,126 +0,0 @@ -// Copyright The OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package main - -import ( - "context" - "log" - "math/rand" - "net/http" - "time" - - "google.golang.org/grpc" - - "go.opentelemetry.io/otel/attribute" - "go.opentelemetry.io/otel/exporters/metric/prometheus" - "go.opentelemetry.io/otel/exporters/otlp" - "go.opentelemetry.io/otel/exporters/otlp/otlpgrpc" - "go.opentelemetry.io/otel/metric" - "go.opentelemetry.io/otel/metric/global" - "go.opentelemetry.io/otel/sdk/metric/aggregator/histogram" - controller "go.opentelemetry.io/otel/sdk/metric/controller/basic" - processor "go.opentelemetry.io/otel/sdk/metric/processor/basic" - "go.opentelemetry.io/otel/sdk/metric/selector/simple" - "go.opentelemetry.io/otel/sdk/resource" -) - -func initMeter() { - ctx := context.Background() - - res, err := resource.New( - ctx, - resource.WithAttributes(attribute.String("R", "V")), - ) - if err != nil { - log.Fatal("could not initialize resource:", err) - } - - driver := otlpgrpc.NewDriver( - otlpgrpc.WithInsecure(), - otlpgrpc.WithEndpoint("localhost:30080"), - otlpgrpc.WithDialOption(grpc.WithBlock()), // useful for testing - ) - otlpExporter, err := otlp.New(ctx, driver) - - if err != nil { - log.Fatal("could not initialize OTLP:", err) - } - - cont := controller.New( - processor.New( - simple.NewWithHistogramDistribution( - histogram.WithExplicitBoundaries([]float64{ - 0.001, 0.01, 0.1, 1, 10, 100, 1000, - }), - ), - otlpExporter, // otlpExporter is an ExportKindSelector - processor.WithMemory(true), - ), - controller.WithResource(res), - controller.WithExporter(otlpExporter), - ) - - if err := cont.Start(context.Background()); err != nil { - log.Fatal("could not start controller:", err) - } - - promExporter, err := prometheus.New(prometheus.Config{}, cont) - if err != nil { - log.Fatal("could not initialize prometheus:", err) - } - http.HandleFunc("/", promExporter.ServeHTTP) - go func() { - log.Fatal(http.ListenAndServe(":17000", nil)) - }() - - global.SetMeterProvider(cont.MeterProvider()) - - log.Println("Prometheus server running on :17000") - log.Println("Exporting OTLP to :30080") -} - -func main() { - initMeter() - - labels := []attribute.KeyValue{ - attribute.String("label1", "value1"), - } - - meter := global.Meter("ex.com/prom-collector") - _ = metric.Must(meter).NewFloat64ValueObserver( - "randval", - func(_ context.Context, result metric.Float64ObserverResult) { - result.Observe( - rand.Float64(), - labels..., - ) - }, - metric.WithDescription("A random value"), - ) - - temperature := metric.Must(meter).NewFloat64ValueRecorder("temperature") - interrupts := metric.Must(meter).NewInt64Counter("interrupts") - - ctx := context.Background() - - log.Println("Example is running, please visit :17000") - - for { - temperature.Record(ctx, 100+10*rand.NormFloat64(), labels...) - interrupts.Add(ctx, int64(rand.Intn(100)), labels...) - - time.Sleep(time.Second * time.Duration(rand.Intn(10))) - } -} diff --git a/example/prometheus/go.mod b/example/prometheus/go.mod index 26629cb3f..390418348 100644 --- a/example/prometheus/go.mod +++ b/example/prometheus/go.mod @@ -34,8 +34,6 @@ replace go.opentelemetry.io/otel/example/prometheus => ./ replace go.opentelemetry.io/otel/example/zipkin => ../zipkin -replace go.opentelemetry.io/otel/exporters/otlp => ../../exporters/otlp - replace go.opentelemetry.io/otel/exporters/stdout => ../../exporters/stdout replace go.opentelemetry.io/otel/exporters/trace/jaeger => ../../exporters/trace/jaeger diff --git a/example/zipkin/go.mod b/example/zipkin/go.mod index c57da075a..ba8cfb79d 100644 --- a/example/zipkin/go.mod +++ b/example/zipkin/go.mod @@ -35,8 +35,6 @@ replace go.opentelemetry.io/otel/example/zipkin => ./ replace go.opentelemetry.io/otel/exporters/metric/prometheus => ../../exporters/metric/prometheus -replace go.opentelemetry.io/otel/exporters/otlp => ../../exporters/otlp - replace go.opentelemetry.io/otel/exporters/stdout => ../../exporters/stdout replace go.opentelemetry.io/otel/exporters/trace/jaeger => ../../exporters/trace/jaeger diff --git a/exporters/metric/prometheus/go.mod b/exporters/metric/prometheus/go.mod index 78802ce87..f9d65458c 100644 --- a/exporters/metric/prometheus/go.mod +++ b/exporters/metric/prometheus/go.mod @@ -37,8 +37,6 @@ replace go.opentelemetry.io/otel/example/zipkin => ../../../example/zipkin replace go.opentelemetry.io/otel/exporters/metric/prometheus => ./ -replace go.opentelemetry.io/otel/exporters/otlp => ../../otlp - replace go.opentelemetry.io/otel/exporters/stdout => ../../stdout replace go.opentelemetry.io/otel/exporters/trace/jaeger => ../../trace/jaeger diff --git a/exporters/otlp/README.md b/exporters/otlp/README.md deleted file mode 100644 index 4895e19b6..000000000 --- a/exporters/otlp/README.md +++ /dev/null @@ -1,15 +0,0 @@ -# OpenTelemetry Collector Go Exporter - -[![PkgGoDev](https://pkg.go.dev/badge/go.opentelemetry.io/otel/exporters/otlp)](https://pkg.go.dev/go.opentelemetry.io/otel/exporters/otlp) - -This exporter exports OpenTelemetry spans and metrics to the OpenTelemetry Collector. - -## Installation and Setup - -The exporter can be installed using standard `go` functionality. - -```bash -go get -u go.opentelemetry.io/otel/exporters/otlp -``` - -A new exporter can be created using the `New` function. diff --git a/exporters/otlp/doc.go b/exporters/otlp/doc.go deleted file mode 100644 index d0897bc8c..000000000 --- a/exporters/otlp/doc.go +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright The OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// Package otlp contains an exporter for the OpenTelemetry protocol buffers. -// -// This package is currently in a pre-GA phase. Backwards incompatible changes -// may be introduced in subsequent minor version releases as we work to track -// the evolving OpenTelemetry specification and user feedback. -package otlp // import "go.opentelemetry.io/otel/exporters/otlp" diff --git a/exporters/otlp/example_test.go b/exporters/otlp/example_test.go deleted file mode 100644 index e4f3e998e..000000000 --- a/exporters/otlp/example_test.go +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright The OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package otlp_test - -import ( - "context" - "log" - - "go.opentelemetry.io/otel/exporters/otlp" - - "go.opentelemetry.io/otel" - "go.opentelemetry.io/otel/exporters/otlp/otlpgrpc" - sdktrace "go.opentelemetry.io/otel/sdk/trace" -) - -func ExampleNew() { - ctx := context.Background() - - // Set different endpoints for the metrics and traces collectors - metricsDriver := otlpgrpc.NewDriver( - // Configure metrics driver here - ) - tracesDriver := otlpgrpc.NewDriver( - // Configure traces driver here - ) - driver := otlp.NewSplitDriver(otlp.WithMetricDriver(metricsDriver), otlp.WithTraceDriver(tracesDriver)) - exporter, err := otlp.New(ctx, driver) // Configure as needed. - if err != nil { - log.Fatalf("failed to create exporter: %v", err) - } - defer func() { - err := exporter.Shutdown(ctx) - if err != nil { - log.Fatalf("failed to stop exporter: %v", err) - } - }() - - tracerProvider := sdktrace.NewTracerProvider(sdktrace.WithBatcher(exporter)) - otel.SetTracerProvider(tracerProvider) -} diff --git a/exporters/otlp/go.mod b/exporters/otlp/go.mod deleted file mode 100644 index acdf29f12..000000000 --- a/exporters/otlp/go.mod +++ /dev/null @@ -1,75 +0,0 @@ -module go.opentelemetry.io/otel/exporters/otlp - -go 1.15 - -replace ( - go.opentelemetry.io/otel => ../.. - go.opentelemetry.io/otel/sdk => ../../sdk -) - -require ( - github.com/cenkalti/backoff/v4 v4.1.1 - github.com/google/go-cmp v0.5.6 - github.com/stretchr/testify v1.7.0 - go.opentelemetry.io/otel v0.20.0 - go.opentelemetry.io/otel/metric v0.20.0 - go.opentelemetry.io/otel/oteltest v0.20.0 - go.opentelemetry.io/otel/sdk v0.20.0 - go.opentelemetry.io/otel/sdk/export/metric v0.20.0 - go.opentelemetry.io/otel/sdk/metric v0.20.0 - go.opentelemetry.io/otel/trace v0.20.0 - go.opentelemetry.io/proto/otlp v0.9.0 - google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 - google.golang.org/grpc v1.38.0 - google.golang.org/protobuf v1.26.0 -) - -replace go.opentelemetry.io/otel/bridge/opencensus => ../../bridge/opencensus - -replace go.opentelemetry.io/otel/bridge/opentracing => ../../bridge/opentracing - -replace go.opentelemetry.io/otel/example/jaeger => ../../example/jaeger - -replace go.opentelemetry.io/otel/example/namedtracer => ../../example/namedtracer - -replace go.opentelemetry.io/otel/example/opencensus => ../../example/opencensus - -replace go.opentelemetry.io/otel/example/otel-collector => ../../example/otel-collector - -replace go.opentelemetry.io/otel/example/prom-collector => ../../example/prom-collector - -replace go.opentelemetry.io/otel/example/prometheus => ../../example/prometheus - -replace go.opentelemetry.io/otel/example/zipkin => ../../example/zipkin - -replace go.opentelemetry.io/otel/exporters/metric/prometheus => ../metric/prometheus - -replace go.opentelemetry.io/otel/exporters/otlp => ./ - -replace go.opentelemetry.io/otel/exporters/stdout => ../stdout - -replace go.opentelemetry.io/otel/exporters/trace/jaeger => ../trace/jaeger - -replace go.opentelemetry.io/otel/exporters/trace/zipkin => ../trace/zipkin - -replace go.opentelemetry.io/otel/internal/tools => ../../internal/tools - -replace go.opentelemetry.io/otel/metric => ../../metric - -replace go.opentelemetry.io/otel/oteltest => ../../oteltest - -replace go.opentelemetry.io/otel/sdk/export/metric => ../../sdk/export/metric - -replace go.opentelemetry.io/otel/sdk/metric => ../../sdk/metric - -replace go.opentelemetry.io/otel/trace => ../../trace - -replace go.opentelemetry.io/otel/exporters/otlp/otlptrace => ./otlptrace - -replace go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc => ./otlptrace/otlptracegrpc - -replace go.opentelemetry.io/otel/example/passthrough => ../../example/passthrough - -replace go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp => ./otlptrace/otlptracehttp - -replace go.opentelemetry.io/otel/internal/metric => ../../internal/metric diff --git a/exporters/otlp/go.sum b/exporters/otlp/go.sum deleted file mode 100644 index e9bd67196..000000000 --- a/exporters/otlp/go.sum +++ /dev/null @@ -1,125 +0,0 @@ -cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= -github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= -github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= -github.com/cenkalti/backoff/v4 v4.1.1 h1:G2HAfAmvm/GcKan2oOQpBXOd2tT2G57ZnZGWa1PxPBQ= -github.com/cenkalti/backoff/v4 v4.1.1/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= -github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= -github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= -github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= -github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= -github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= -github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= -github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= -github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= -github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= -github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= -github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= -github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= -github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ= -github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo= -github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= -github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= -github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -go.opentelemetry.io/proto/otlp v0.9.0 h1:C0g6TWmQYvjKRnljRULLWUVJGy8Uvu0NEL/5frY2/t4= -go.opentelemetry.io/proto/otlp v0.9.0/go.mod h1:1vKfU9rv61e9EVGthD1zNvUbiwPcimSsOPU9brfSHJg= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= -golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20200822124328-c89045814202 h1:VvcQYSHwXgi7W+TpUR6A9g6Up98WAHf3f/ulnJ62IyA= -golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd h1:xhmwyvizuTgC2qz7ZlMluP20uW+C3Rm0FD/WLDX8884= -golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= -golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 h1:+kGHl1aib/qcwaRi1CbqBZ1rk19r85MNUf8HaBghugY= -google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= -google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= -google.golang.org/grpc v1.37.1/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/grpc v1.38.0 h1:/9BgsAsa5nWe26HqOlvlgJnqBuktYOLCgjCPqsa56W0= -google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= -google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= -google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= -google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= -google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= -google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= -google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk= -google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/exporters/otlp/internal/otlpconfig/envconfig.go b/exporters/otlp/internal/otlpconfig/envconfig.go deleted file mode 100644 index e17f2f1d1..000000000 --- a/exporters/otlp/internal/otlpconfig/envconfig.go +++ /dev/null @@ -1,225 +0,0 @@ -// Copyright The OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package otlpconfig - -import ( - "crypto/tls" - "fmt" - "io/ioutil" - "net/url" - "os" - "regexp" - "strconv" - "strings" - "time" - - "go.opentelemetry.io/otel/exporters/otlp" - - "go.opentelemetry.io/otel" -) - -var httpSchemeRegexp = regexp.MustCompile(`(?i)^http://|https://`) - -func ApplyGRPCEnvConfigs(cfg *Config) { - e := EnvOptionsReader{ - GetEnv: os.Getenv, - ReadFile: ioutil.ReadFile, - } - - e.ApplyGRPCEnvConfigs(cfg) -} - -func ApplyHTTPEnvConfigs(cfg *Config) { - e := EnvOptionsReader{ - GetEnv: os.Getenv, - ReadFile: ioutil.ReadFile, - } - - e.ApplyHTTPEnvConfigs(cfg) -} - -type EnvOptionsReader struct { - GetEnv func(string) string - ReadFile func(filename string) ([]byte, error) -} - -func (e *EnvOptionsReader) ApplyHTTPEnvConfigs(cfg *Config) { - opts := e.GetOptionsFromEnv() - for _, opt := range opts { - opt.ApplyHTTPOption(cfg) - } -} - -func (e *EnvOptionsReader) ApplyGRPCEnvConfigs(cfg *Config) { - opts := e.GetOptionsFromEnv() - for _, opt := range opts { - opt.ApplyGRPCOption(cfg) - } -} - -func (e *EnvOptionsReader) GetOptionsFromEnv() []GenericOption { - var opts []GenericOption - - // Endpoint - if v, ok := e.getEnvValue("ENDPOINT"); ok { - if isInsecureEndpoint(v) { - opts = append(opts, WithInsecure()) - } else { - opts = append(opts, WithSecure()) - } - - opts = append(opts, WithEndpoint(trimSchema(v))) - } - if v, ok := e.getEnvValue("TRACES_ENDPOINT"); ok { - if isInsecureEndpoint(v) { - opts = append(opts, WithInsecureTraces()) - } else { - opts = append(opts, WithSecureTraces()) - } - - opts = append(opts, WithTracesEndpoint(trimSchema(v))) - } - if v, ok := e.getEnvValue("METRICS_ENDPOINT"); ok { - if isInsecureEndpoint(v) { - opts = append(opts, WithInsecureMetrics()) - } else { - opts = append(opts, WithSecureMetrics()) - } - - opts = append(opts, WithMetricsEndpoint(trimSchema(v))) - } - - // Certificate File - if path, ok := e.getEnvValue("CERTIFICATE"); ok { - if tls, err := e.readTLSConfig(path); err == nil { - opts = append(opts, WithTLSClientConfig(tls)) - } else { - otel.Handle(fmt.Errorf("failed to configure otlp exporter certificate '%s': %w", path, err)) - } - } - if path, ok := e.getEnvValue("TRACES_CERTIFICATE"); ok { - if tls, err := e.readTLSConfig(path); err == nil { - opts = append(opts, WithTracesTLSClientConfig(tls)) - } else { - otel.Handle(fmt.Errorf("failed to configure otlp traces exporter certificate '%s': %w", path, err)) - } - } - if path, ok := e.getEnvValue("METRICS_CERTIFICATE"); ok { - if tls, err := e.readTLSConfig(path); err == nil { - opts = append(opts, WithMetricsTLSClientConfig(tls)) - } else { - otel.Handle(fmt.Errorf("failed to configure otlp metrics exporter certificate '%s': %w", path, err)) - } - } - - // Headers - if h, ok := e.getEnvValue("HEADERS"); ok { - opts = append(opts, WithHeaders(stringToHeader(h))) - } - if h, ok := e.getEnvValue("TRACES_HEADERS"); ok { - opts = append(opts, WithTracesHeaders(stringToHeader(h))) - } - if h, ok := e.getEnvValue("METRICS_HEADERS"); ok { - opts = append(opts, WithMetricsHeaders(stringToHeader(h))) - } - - // Compression - if c, ok := e.getEnvValue("COMPRESSION"); ok { - opts = append(opts, WithCompression(stringToCompression(c))) - } - if c, ok := e.getEnvValue("TRACES_COMPRESSION"); ok { - opts = append(opts, WithTracesCompression(stringToCompression(c))) - } - if c, ok := e.getEnvValue("METRICS_COMPRESSION"); ok { - opts = append(opts, WithMetricsCompression(stringToCompression(c))) - } - - // Timeout - if t, ok := e.getEnvValue("TIMEOUT"); ok { - if d, err := strconv.Atoi(t); err == nil { - opts = append(opts, WithTimeout(time.Duration(d)*time.Millisecond)) - } - } - if t, ok := e.getEnvValue("TRACES_TIMEOUT"); ok { - if d, err := strconv.Atoi(t); err == nil { - opts = append(opts, WithTracesTimeout(time.Duration(d)*time.Millisecond)) - } - } - if t, ok := e.getEnvValue("METRICS_TIMEOUT"); ok { - if d, err := strconv.Atoi(t); err == nil { - opts = append(opts, WithMetricsTimeout(time.Duration(d)*time.Millisecond)) - } - } - - return opts -} - -func isInsecureEndpoint(endpoint string) bool { - return strings.HasPrefix(strings.ToLower(endpoint), "http://") -} - -func trimSchema(endpoint string) string { - return httpSchemeRegexp.ReplaceAllString(endpoint, "") -} - -// getEnvValue gets an OTLP environment variable value of the specified key using the GetEnv function. -// This function already prepends the OTLP prefix to all key lookup. -func (e *EnvOptionsReader) getEnvValue(key string) (string, bool) { - v := strings.TrimSpace(e.GetEnv(fmt.Sprintf("OTEL_EXPORTER_OTLP_%s", key))) - return v, v != "" -} - -func (e *EnvOptionsReader) readTLSConfig(path string) (*tls.Config, error) { - b, err := e.ReadFile(path) - if err != nil { - return nil, err - } - return CreateTLSConfig(b) -} - -func stringToCompression(value string) otlp.Compression { - switch value { - case "gzip": - return otlp.GzipCompression - } - - return otlp.NoCompression -} - -func stringToHeader(value string) map[string]string { - headersPairs := strings.Split(value, ",") - headers := make(map[string]string) - - for _, header := range headersPairs { - nameValue := strings.SplitN(header, "=", 2) - if len(nameValue) < 2 { - continue - } - name, err := url.QueryUnescape(nameValue[0]) - if err != nil { - continue - } - trimmedName := strings.TrimSpace(name) - value, err := url.QueryUnescape(nameValue[1]) - if err != nil { - continue - } - trimmedValue := strings.TrimSpace(value) - - headers[trimmedName] = trimmedValue - } - - return headers -} diff --git a/exporters/otlp/internal/otlpconfig/envconfig_test.go b/exporters/otlp/internal/otlpconfig/envconfig_test.go deleted file mode 100644 index 7a6316a2d..000000000 --- a/exporters/otlp/internal/otlpconfig/envconfig_test.go +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright The OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package otlpconfig - -import ( - "reflect" - "testing" -) - -func TestStringToHeader(t *testing.T) { - tests := []struct { - name string - value string - want map[string]string - }{ - { - name: "simple test", - value: "userId=alice", - want: map[string]string{"userId": "alice"}, - }, - { - name: "simple test with spaces", - value: " userId = alice ", - want: map[string]string{"userId": "alice"}, - }, - { - name: "multiples headers encoded", - value: "userId=alice,serverNode=DF%3A28,isProduction=false", - want: map[string]string{ - "userId": "alice", - "serverNode": "DF:28", - "isProduction": "false", - }, - }, - { - name: "invalid headers format", - value: "userId:alice", - want: map[string]string{}, - }, - { - name: "invalid key", - value: "%XX=missing,userId=alice", - want: map[string]string{ - "userId": "alice", - }, - }, - { - name: "invalid value", - value: "missing=%XX,userId=alice", - want: map[string]string{ - "userId": "alice", - }, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - if got := stringToHeader(tt.value); !reflect.DeepEqual(got, tt.want) { - t.Errorf("stringToHeader() = %v, want %v", got, tt.want) - } - }) - } -} diff --git a/exporters/otlp/internal/otlpconfig/options.go b/exporters/otlp/internal/otlpconfig/options.go deleted file mode 100644 index 4f4387a1a..000000000 --- a/exporters/otlp/internal/otlpconfig/options.go +++ /dev/null @@ -1,385 +0,0 @@ -// Copyright The OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package otlpconfig // import "go.opentelemetry.io/otel/exporters/otlp/internal/otlpconfig" - -import ( - "crypto/tls" - "fmt" - "time" - - "google.golang.org/grpc" - "google.golang.org/grpc/credentials" - - "go.opentelemetry.io/otel/exporters/otlp" -) - -const ( - // DefaultMaxAttempts describes how many times the driver - // should retry the sending of the payload in case of a - // retryable error. - DefaultMaxAttempts int = 5 - // DefaultTracesPath is a default URL path for endpoint that - // receives spans. - DefaultTracesPath string = "/v1/traces" - // DefaultMetricsPath is a default URL path for endpoint that - // receives metrics. - DefaultMetricsPath string = "/v1/metrics" - // DefaultBackoff is a default base backoff time used in the - // exponential backoff strategy. - DefaultBackoff time.Duration = 300 * time.Millisecond - // DefaultTimeout is a default max waiting time for the backend to process - // each span or metrics batch. - DefaultTimeout time.Duration = 10 * time.Second -) - -var ( - // defaultRetrySettings is a default settings for the retry policy. - defaultRetrySettings = otlp.RetrySettings{ - Enabled: true, - InitialInterval: 5 * time.Second, - MaxInterval: 30 * time.Second, - MaxElapsedTime: time.Minute, - } -) - -type ( - SignalConfig struct { - Endpoint string - Insecure bool - TLSCfg *tls.Config - Headers map[string]string - Compression otlp.Compression - Timeout time.Duration - URLPath string - - // gRPC configurations - GRPCCredentials credentials.TransportCredentials - } - - Config struct { - // Signal specific configurations - Metrics SignalConfig - Traces SignalConfig - - // HTTP configurations - Marshaler otlp.Marshaler - MaxAttempts int - Backoff time.Duration - - // gRPC configurations - ReconnectionPeriod time.Duration - ServiceConfig string - DialOptions []grpc.DialOption - RetrySettings otlp.RetrySettings - } -) - -func NewDefaultConfig() Config { - c := Config{ - Traces: SignalConfig{ - Endpoint: fmt.Sprintf("%s:%d", otlp.DefaultCollectorHost, otlp.DefaultCollectorPort), - URLPath: DefaultTracesPath, - Compression: otlp.NoCompression, - Timeout: DefaultTimeout, - }, - Metrics: SignalConfig{ - Endpoint: fmt.Sprintf("%s:%d", otlp.DefaultCollectorHost, otlp.DefaultCollectorPort), - URLPath: DefaultMetricsPath, - Compression: otlp.NoCompression, - Timeout: DefaultTimeout, - }, - MaxAttempts: DefaultMaxAttempts, - Backoff: DefaultBackoff, - RetrySettings: defaultRetrySettings, - } - - return c -} - -type ( - // GenericOption applies an option to the HTTP or gRPC driver. - GenericOption interface { - ApplyHTTPOption(*Config) - ApplyGRPCOption(*Config) - - // A private method to prevent users implementing the - // interface and so future additions to it will not - // violate compatibility. - private() - } - - // HTTPOption applies an option to the HTTP driver. - HTTPOption interface { - ApplyHTTPOption(*Config) - - // A private method to prevent users implementing the - // interface and so future additions to it will not - // violate compatibility. - private() - } - - // GRPCOption applies an option to the gRPC driver. - GRPCOption interface { - ApplyGRPCOption(*Config) - - // A private method to prevent users implementing the - // interface and so future additions to it will not - // violate compatibility. - private() - } -) - -// genericOption is an option that applies the same logic -// for both gRPC and HTTP. -type genericOption struct { - fn func(*Config) -} - -func (g *genericOption) ApplyGRPCOption(cfg *Config) { - g.fn(cfg) -} - -func (g *genericOption) ApplyHTTPOption(cfg *Config) { - g.fn(cfg) -} - -func (genericOption) private() {} - -func newGenericOption(fn func(cfg *Config)) GenericOption { - return &genericOption{fn: fn} -} - -// splitOption is an option that applies different logics -// for gRPC and HTTP. -type splitOption struct { - httpFn func(*Config) - grpcFn func(*Config) -} - -func (g *splitOption) ApplyGRPCOption(cfg *Config) { - g.grpcFn(cfg) -} - -func (g *splitOption) ApplyHTTPOption(cfg *Config) { - g.httpFn(cfg) -} - -func (splitOption) private() {} - -func newSplitOption(httpFn func(cfg *Config), grpcFn func(cfg *Config)) GenericOption { - return &splitOption{httpFn: httpFn, grpcFn: grpcFn} -} - -// httpOption is an option that is only applied to the HTTP driver. -type httpOption struct { - fn func(*Config) -} - -func (h *httpOption) ApplyHTTPOption(cfg *Config) { - h.fn(cfg) -} - -func (httpOption) private() {} - -func NewHTTPOption(fn func(cfg *Config)) HTTPOption { - return &httpOption{fn: fn} -} - -// grpcOption is an option that is only applied to the gRPC driver. -type grpcOption struct { - fn func(*Config) -} - -func (h *grpcOption) ApplyGRPCOption(cfg *Config) { - h.fn(cfg) -} - -func (grpcOption) private() {} - -func NewGRPCOption(fn func(cfg *Config)) GRPCOption { - return &grpcOption{fn: fn} -} - -// Generic Options - -func WithEndpoint(endpoint string) GenericOption { - return newGenericOption(func(cfg *Config) { - cfg.Traces.Endpoint = endpoint - cfg.Metrics.Endpoint = endpoint - }) -} - -func WithTracesEndpoint(endpoint string) GenericOption { - return newGenericOption(func(cfg *Config) { - cfg.Traces.Endpoint = endpoint - }) -} - -func WithMetricsEndpoint(endpoint string) GenericOption { - return newGenericOption(func(cfg *Config) { - cfg.Metrics.Endpoint = endpoint - }) -} - -func WithCompression(compression otlp.Compression) GenericOption { - return newGenericOption(func(cfg *Config) { - cfg.Traces.Compression = compression - cfg.Metrics.Compression = compression - }) -} - -func WithTracesCompression(compression otlp.Compression) GenericOption { - return newGenericOption(func(cfg *Config) { - cfg.Traces.Compression = compression - }) -} - -func WithMetricsCompression(compression otlp.Compression) GenericOption { - return newGenericOption(func(cfg *Config) { - cfg.Metrics.Compression = compression - }) -} - -func WithTracesURLPath(urlPath string) GenericOption { - return newGenericOption(func(cfg *Config) { - cfg.Traces.URLPath = urlPath - }) -} - -func WithMetricsURLPath(urlPath string) GenericOption { - return newGenericOption(func(cfg *Config) { - cfg.Metrics.URLPath = urlPath - }) -} - -func WithRetry(settings otlp.RetrySettings) GenericOption { - return newGenericOption(func(cfg *Config) { - cfg.RetrySettings = settings - }) -} - -func WithTLSClientConfig(tlsCfg *tls.Config) GenericOption { - return newSplitOption(func(cfg *Config) { - cfg.Traces.TLSCfg = tlsCfg.Clone() - cfg.Metrics.TLSCfg = tlsCfg.Clone() - }, func(cfg *Config) { - cfg.Traces.GRPCCredentials = credentials.NewTLS(tlsCfg) - cfg.Metrics.GRPCCredentials = credentials.NewTLS(tlsCfg) - }) -} - -func WithTracesTLSClientConfig(tlsCfg *tls.Config) GenericOption { - return newSplitOption(func(cfg *Config) { - cfg.Traces.TLSCfg = tlsCfg.Clone() - }, func(cfg *Config) { - cfg.Traces.GRPCCredentials = credentials.NewTLS(tlsCfg) - }) -} - -func WithMetricsTLSClientConfig(tlsCfg *tls.Config) GenericOption { - return newSplitOption(func(cfg *Config) { - cfg.Metrics.TLSCfg = tlsCfg.Clone() - }, func(cfg *Config) { - cfg.Metrics.GRPCCredentials = credentials.NewTLS(tlsCfg) - }) -} - -func WithInsecure() GenericOption { - return newGenericOption(func(cfg *Config) { - cfg.Traces.Insecure = true - cfg.Metrics.Insecure = true - }) -} - -func WithSecure() GenericOption { - return newGenericOption(func(cfg *Config) { - cfg.Traces.Insecure = false - cfg.Metrics.Insecure = false - }) -} - -func WithInsecureTraces() GenericOption { - return newGenericOption(func(cfg *Config) { - cfg.Traces.Insecure = true - }) -} - -func WithSecureTraces() GenericOption { - return newGenericOption(func(cfg *Config) { - cfg.Traces.Insecure = false - }) -} - -func WithInsecureMetrics() GenericOption { - return newGenericOption(func(cfg *Config) { - cfg.Metrics.Insecure = true - }) -} - -func WithSecureMetrics() GenericOption { - return newGenericOption(func(cfg *Config) { - cfg.Metrics.Insecure = false - }) -} - -func WithHeaders(headers map[string]string) GenericOption { - return newGenericOption(func(cfg *Config) { - cfg.Traces.Headers = headers - cfg.Metrics.Headers = headers - }) -} - -func WithTracesHeaders(headers map[string]string) GenericOption { - return newGenericOption(func(cfg *Config) { - cfg.Traces.Headers = headers - }) -} - -func WithMetricsHeaders(headers map[string]string) GenericOption { - return newGenericOption(func(cfg *Config) { - cfg.Metrics.Headers = headers - }) -} - -func WithTimeout(duration time.Duration) GenericOption { - return newGenericOption(func(cfg *Config) { - cfg.Traces.Timeout = duration - cfg.Metrics.Timeout = duration - }) -} - -func WithTracesTimeout(duration time.Duration) GenericOption { - return newGenericOption(func(cfg *Config) { - cfg.Traces.Timeout = duration - }) -} - -func WithMetricsTimeout(duration time.Duration) GenericOption { - return newGenericOption(func(cfg *Config) { - cfg.Metrics.Timeout = duration - }) -} - -func WithMaxAttempts(maxAttempts int) GenericOption { - return newGenericOption(func(cfg *Config) { - cfg.MaxAttempts = maxAttempts - }) -} - -func WithBackoff(duration time.Duration) GenericOption { - return newGenericOption(func(cfg *Config) { - cfg.Backoff = duration - }) -} diff --git a/exporters/otlp/internal/otlpconfig/options_test.go b/exporters/otlp/internal/otlpconfig/options_test.go deleted file mode 100644 index eba7e2b1e..000000000 --- a/exporters/otlp/internal/otlpconfig/options_test.go +++ /dev/null @@ -1,503 +0,0 @@ -// Copyright The OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package otlpconfig - -import ( - "crypto/tls" - "crypto/x509" - "errors" - "testing" - "time" - - "go.opentelemetry.io/otel/exporters/otlp" - - "github.com/stretchr/testify/assert" -) - -type env map[string]string - -func (e *env) getEnv(env string) string { - return (*e)[env] -} - -type fileReader map[string][]byte - -func (f *fileReader) readFile(filename string) ([]byte, error) { - if b, ok := (*f)[filename]; ok { - return b, nil - } - return nil, errors.New("File not found") -} - -func TestConfigs(t *testing.T) { - tlsCert, err := CreateTLSConfig([]byte(WeakCertificate)) - assert.NoError(t, err) - - tests := []struct { - name string - opts []GenericOption - env env - fileReader fileReader - asserts func(t *testing.T, c *Config, grpcOption bool) - }{ - { - name: "Test default configs", - asserts: func(t *testing.T, c *Config, grpcOption bool) { - assert.Equal(t, "localhost:4317", c.Traces.Endpoint) - assert.Equal(t, "localhost:4317", c.Metrics.Endpoint) - assert.Equal(t, otlp.NoCompression, c.Traces.Compression) - assert.Equal(t, otlp.NoCompression, c.Metrics.Compression) - assert.Equal(t, map[string]string(nil), c.Traces.Headers) - assert.Equal(t, map[string]string(nil), c.Metrics.Headers) - assert.Equal(t, 10*time.Second, c.Traces.Timeout) - assert.Equal(t, 10*time.Second, c.Metrics.Timeout) - }, - }, - - // Endpoint Tests - { - name: "Test With Endpoint", - opts: []GenericOption{ - WithEndpoint("someendpoint"), - }, - asserts: func(t *testing.T, c *Config, grpcOption bool) { - assert.Equal(t, "someendpoint", c.Traces.Endpoint) - assert.Equal(t, "someendpoint", c.Metrics.Endpoint) - }, - }, - { - name: "Test With Signal Specific Endpoint", - opts: []GenericOption{ - WithEndpoint("overrode_by_signal_specific"), - WithTracesEndpoint("traces_endpoint"), - WithMetricsEndpoint("metrics_endpoint"), - }, - asserts: func(t *testing.T, c *Config, grpcOption bool) { - assert.Equal(t, "traces_endpoint", c.Traces.Endpoint) - assert.Equal(t, "metrics_endpoint", c.Metrics.Endpoint) - }, - }, - { - name: "Test Environment Endpoint", - env: map[string]string{ - "OTEL_EXPORTER_OTLP_ENDPOINT": "env_endpoint", - }, - asserts: func(t *testing.T, c *Config, grpcOption bool) { - assert.Equal(t, "env_endpoint", c.Traces.Endpoint) - assert.Equal(t, "env_endpoint", c.Metrics.Endpoint) - }, - }, - { - name: "Test Environment Signal Specific Endpoint", - env: map[string]string{ - "OTEL_EXPORTER_OTLP_ENDPOINT": "overrode_by_signal_specific", - "OTEL_EXPORTER_OTLP_TRACES_ENDPOINT": "env_traces_endpoint", - "OTEL_EXPORTER_OTLP_METRICS_ENDPOINT": "env_metrics_endpoint", - }, - asserts: func(t *testing.T, c *Config, grpcOption bool) { - assert.Equal(t, "env_traces_endpoint", c.Traces.Endpoint) - assert.Equal(t, "env_metrics_endpoint", c.Metrics.Endpoint) - }, - }, - { - name: "Test Mixed Environment and With Endpoint", - opts: []GenericOption{ - WithTracesEndpoint("traces_endpoint"), - }, - env: map[string]string{ - "OTEL_EXPORTER_OTLP_ENDPOINT": "env_endpoint", - }, - asserts: func(t *testing.T, c *Config, grpcOption bool) { - assert.Equal(t, "traces_endpoint", c.Traces.Endpoint) - assert.Equal(t, "env_endpoint", c.Metrics.Endpoint) - }, - }, - { - name: "Test Environment Endpoint with HTTP scheme", - env: map[string]string{ - "OTEL_EXPORTER_OTLP_ENDPOINT": "http://env_endpoint", - }, - asserts: func(t *testing.T, c *Config, grpcOption bool) { - assert.Equal(t, "env_endpoint", c.Traces.Endpoint) - assert.Equal(t, "env_endpoint", c.Metrics.Endpoint) - assert.Equal(t, true, c.Traces.Insecure) - assert.Equal(t, true, c.Metrics.Insecure) - }, - }, - { - name: "Test Environment Endpoint with HTTP scheme and leading & trailingspaces", - env: map[string]string{ - "OTEL_EXPORTER_OTLP_ENDPOINT": " http://env_endpoint ", - }, - asserts: func(t *testing.T, c *Config, grpcOption bool) { - assert.Equal(t, "env_endpoint", c.Traces.Endpoint) - assert.Equal(t, "env_endpoint", c.Metrics.Endpoint) - assert.Equal(t, true, c.Traces.Insecure) - assert.Equal(t, true, c.Metrics.Insecure) - }, - }, - { - name: "Test Environment Endpoint with HTTPS scheme", - env: map[string]string{ - "OTEL_EXPORTER_OTLP_ENDPOINT": "https://env_endpoint", - }, - asserts: func(t *testing.T, c *Config, grpcOption bool) { - assert.Equal(t, "env_endpoint", c.Traces.Endpoint) - assert.Equal(t, "env_endpoint", c.Metrics.Endpoint) - assert.Equal(t, false, c.Traces.Insecure) - assert.Equal(t, false, c.Metrics.Insecure) - }, - }, - { - name: "Test Environment Signal Specific Endpoint", - env: map[string]string{ - "OTEL_EXPORTER_OTLP_ENDPOINT": "http://overrode_by_signal_specific", - "OTEL_EXPORTER_OTLP_TRACES_ENDPOINT": "http://env_traces_endpoint", - "OTEL_EXPORTER_OTLP_METRICS_ENDPOINT": "https://env_metrics_endpoint", - }, - asserts: func(t *testing.T, c *Config, grpcOption bool) { - assert.Equal(t, "env_traces_endpoint", c.Traces.Endpoint) - assert.Equal(t, "env_metrics_endpoint", c.Metrics.Endpoint) - assert.Equal(t, true, c.Traces.Insecure) - assert.Equal(t, false, c.Metrics.Insecure) - }, - }, - { - name: "Test Environment Signal Specific Endpoint #2", - env: map[string]string{ - "OTEL_EXPORTER_OTLP_ENDPOINT": "http://overrode_by_signal_specific", - "OTEL_EXPORTER_OTLP_TRACES_ENDPOINT": "http://env_traces_endpoint", - "OTEL_EXPORTER_OTLP_METRICS_ENDPOINT": "env_metrics_endpoint", - }, - asserts: func(t *testing.T, c *Config, grpcOption bool) { - assert.Equal(t, "env_traces_endpoint", c.Traces.Endpoint) - assert.Equal(t, "env_metrics_endpoint", c.Metrics.Endpoint) - assert.Equal(t, true, c.Traces.Insecure) - assert.Equal(t, false, c.Metrics.Insecure) - }, - }, - { - name: "Test Environment Signal Specific Endpoint with uppercase scheme", - env: map[string]string{ - "OTEL_EXPORTER_OTLP_ENDPOINT": "HTTP://overrode_by_signal_specific", - "OTEL_EXPORTER_OTLP_TRACES_ENDPOINT": "HtTp://env_traces_endpoint", - "OTEL_EXPORTER_OTLP_METRICS_ENDPOINT": "env_metrics_endpoint", - }, - asserts: func(t *testing.T, c *Config, grpcOption bool) { - assert.Equal(t, "env_traces_endpoint", c.Traces.Endpoint) - assert.Equal(t, "env_metrics_endpoint", c.Metrics.Endpoint) - assert.Equal(t, true, c.Traces.Insecure) - assert.Equal(t, false, c.Metrics.Insecure) - }, - }, - - // Certificate tests - { - name: "Test With Certificate", - opts: []GenericOption{ - WithTLSClientConfig(tlsCert), - }, - asserts: func(t *testing.T, c *Config, grpcOption bool) { - if grpcOption { - //TODO: make sure gRPC's credentials actually works - assert.NotNil(t, c.Traces.GRPCCredentials) - assert.NotNil(t, c.Metrics.GRPCCredentials) - } else { - assert.Equal(t, tlsCert.RootCAs.Subjects(), c.Traces.TLSCfg.RootCAs.Subjects()) - assert.Equal(t, tlsCert.RootCAs.Subjects(), c.Metrics.TLSCfg.RootCAs.Subjects()) - } - }, - }, - { - name: "Test With Signal Specific Certificate", - opts: []GenericOption{ - WithTLSClientConfig(&tls.Config{}), - WithTracesTLSClientConfig(tlsCert), - WithMetricsTLSClientConfig(&tls.Config{RootCAs: x509.NewCertPool()}), - }, - asserts: func(t *testing.T, c *Config, grpcOption bool) { - - if grpcOption { - assert.NotNil(t, c.Traces.GRPCCredentials) - assert.NotNil(t, c.Metrics.GRPCCredentials) - } else { - assert.Equal(t, tlsCert.RootCAs.Subjects(), c.Traces.TLSCfg.RootCAs.Subjects()) - assert.Equal(t, 0, len(c.Metrics.TLSCfg.RootCAs.Subjects())) - } - }, - }, - { - name: "Test Environment Certificate", - env: map[string]string{ - "OTEL_EXPORTER_OTLP_CERTIFICATE": "cert_path", - }, - fileReader: fileReader{ - "cert_path": []byte(WeakCertificate), - }, - asserts: func(t *testing.T, c *Config, grpcOption bool) { - if grpcOption { - assert.NotNil(t, c.Traces.GRPCCredentials) - assert.NotNil(t, c.Metrics.GRPCCredentials) - } else { - assert.Equal(t, tlsCert.RootCAs.Subjects(), c.Traces.TLSCfg.RootCAs.Subjects()) - assert.Equal(t, tlsCert.RootCAs.Subjects(), c.Metrics.TLSCfg.RootCAs.Subjects()) - } - }, - }, - { - name: "Test Environment Signal Specific Certificate", - env: map[string]string{ - "OTEL_EXPORTER_OTLP_CERTIFICATE": "overrode_by_signal_specific", - "OTEL_EXPORTER_OTLP_TRACES_CERTIFICATE": "cert_path", - "OTEL_EXPORTER_OTLP_METRICS_CERTIFICATE": "invalid_cert", - }, - fileReader: fileReader{ - "cert_path": []byte(WeakCertificate), - "invalid_cert": []byte("invalid certificate file."), - }, - asserts: func(t *testing.T, c *Config, grpcOption bool) { - if grpcOption { - assert.NotNil(t, c.Traces.GRPCCredentials) - assert.Nil(t, c.Metrics.GRPCCredentials) - } else { - assert.Equal(t, tlsCert.RootCAs.Subjects(), c.Traces.TLSCfg.RootCAs.Subjects()) - assert.Equal(t, (*tls.Config)(nil), c.Metrics.TLSCfg) - } - }, - }, - { - name: "Test Mixed Environment and With Certificate", - opts: []GenericOption{ - WithMetricsTLSClientConfig(&tls.Config{RootCAs: x509.NewCertPool()}), - }, - env: map[string]string{ - "OTEL_EXPORTER_OTLP_CERTIFICATE": "cert_path", - }, - fileReader: fileReader{ - "cert_path": []byte(WeakCertificate), - }, - asserts: func(t *testing.T, c *Config, grpcOption bool) { - if grpcOption { - assert.NotNil(t, c.Traces.GRPCCredentials) - assert.NotNil(t, c.Metrics.GRPCCredentials) - } else { - assert.Equal(t, tlsCert.RootCAs.Subjects(), c.Traces.TLSCfg.RootCAs.Subjects()) - assert.Equal(t, 0, len(c.Metrics.TLSCfg.RootCAs.Subjects())) - } - }, - }, - - // Headers tests - { - name: "Test With Headers", - opts: []GenericOption{ - WithHeaders(map[string]string{"h1": "v1"}), - }, - asserts: func(t *testing.T, c *Config, grpcOption bool) { - assert.Equal(t, map[string]string{"h1": "v1"}, c.Metrics.Headers) - assert.Equal(t, map[string]string{"h1": "v1"}, c.Traces.Headers) - }, - }, - { - name: "Test With Signal Specific Headers", - opts: []GenericOption{ - WithHeaders(map[string]string{"overrode": "by_signal_specific"}), - WithMetricsHeaders(map[string]string{"m1": "mv1"}), - WithTracesHeaders(map[string]string{"t1": "tv1"}), - }, - asserts: func(t *testing.T, c *Config, grpcOption bool) { - assert.Equal(t, map[string]string{"m1": "mv1"}, c.Metrics.Headers) - assert.Equal(t, map[string]string{"t1": "tv1"}, c.Traces.Headers) - }, - }, - { - name: "Test Environment Headers", - env: map[string]string{"OTEL_EXPORTER_OTLP_HEADERS": "h1=v1,h2=v2"}, - asserts: func(t *testing.T, c *Config, grpcOption bool) { - assert.Equal(t, map[string]string{"h1": "v1", "h2": "v2"}, c.Metrics.Headers) - assert.Equal(t, map[string]string{"h1": "v1", "h2": "v2"}, c.Traces.Headers) - }, - }, - { - name: "Test Environment Signal Specific Headers", - env: map[string]string{ - "OTEL_EXPORTER_OTLP_HEADERS": "overrode_by_signal_specific", - "OTEL_EXPORTER_OTLP_TRACES_HEADERS": "h1=v1,h2=v2", - "OTEL_EXPORTER_OTLP_METRICS_HEADERS": "h1=v1,h2=v2", - }, - asserts: func(t *testing.T, c *Config, grpcOption bool) { - assert.Equal(t, map[string]string{"h1": "v1", "h2": "v2"}, c.Metrics.Headers) - assert.Equal(t, map[string]string{"h1": "v1", "h2": "v2"}, c.Traces.Headers) - }, - }, - { - name: "Test Mixed Environment and With Headers", - env: map[string]string{"OTEL_EXPORTER_OTLP_HEADERS": "h1=v1,h2=v2"}, - opts: []GenericOption{ - WithMetricsHeaders(map[string]string{"m1": "mv1"}), - }, - asserts: func(t *testing.T, c *Config, grpcOption bool) { - assert.Equal(t, map[string]string{"m1": "mv1"}, c.Metrics.Headers) - assert.Equal(t, map[string]string{"h1": "v1", "h2": "v2"}, c.Traces.Headers) - }, - }, - - // Compression Tests - { - name: "Test With Compression", - opts: []GenericOption{ - WithCompression(otlp.GzipCompression), - }, - asserts: func(t *testing.T, c *Config, grpcOption bool) { - assert.Equal(t, otlp.GzipCompression, c.Traces.Compression) - assert.Equal(t, otlp.GzipCompression, c.Metrics.Compression) - }, - }, - { - name: "Test With Signal Specific Compression", - opts: []GenericOption{ - WithCompression(otlp.NoCompression), // overrode by signal specific configs - WithTracesCompression(otlp.GzipCompression), - WithMetricsCompression(otlp.GzipCompression), - }, - asserts: func(t *testing.T, c *Config, grpcOption bool) { - assert.Equal(t, otlp.GzipCompression, c.Traces.Compression) - assert.Equal(t, otlp.GzipCompression, c.Metrics.Compression) - }, - }, - { - name: "Test Environment Compression", - env: map[string]string{ - "OTEL_EXPORTER_OTLP_COMPRESSION": "gzip", - }, - asserts: func(t *testing.T, c *Config, grpcOption bool) { - assert.Equal(t, otlp.GzipCompression, c.Traces.Compression) - assert.Equal(t, otlp.GzipCompression, c.Metrics.Compression) - }, - }, - { - name: "Test Environment Signal Specific Compression", - env: map[string]string{ - "OTEL_EXPORTER_OTLP_TRACES_COMPRESSION": "gzip", - "OTEL_EXPORTER_OTLP_METRICS_COMPRESSION": "gzip", - }, - asserts: func(t *testing.T, c *Config, grpcOption bool) { - assert.Equal(t, otlp.GzipCompression, c.Traces.Compression) - assert.Equal(t, otlp.GzipCompression, c.Metrics.Compression) - }, - }, - { - name: "Test Mixed Environment and With Compression", - opts: []GenericOption{ - WithTracesCompression(otlp.NoCompression), - }, - env: map[string]string{ - "OTEL_EXPORTER_OTLP_TRACES_COMPRESSION": "gzip", - "OTEL_EXPORTER_OTLP_METRICS_COMPRESSION": "gzip", - }, - asserts: func(t *testing.T, c *Config, grpcOption bool) { - assert.Equal(t, otlp.NoCompression, c.Traces.Compression) - assert.Equal(t, otlp.GzipCompression, c.Metrics.Compression) - }, - }, - - // Timeout Tests - { - name: "Test With Timeout", - opts: []GenericOption{ - WithTimeout(time.Duration(5 * time.Second)), - }, - asserts: func(t *testing.T, c *Config, grpcOption bool) { - assert.Equal(t, 5*time.Second, c.Traces.Timeout) - assert.Equal(t, 5*time.Second, c.Metrics.Timeout) - }, - }, - { - name: "Test With Signal Specific Timeout", - opts: []GenericOption{ - WithTimeout(time.Duration(5 * time.Second)), - WithTracesTimeout(time.Duration(13 * time.Second)), - WithMetricsTimeout(time.Duration(14 * time.Second)), - }, - asserts: func(t *testing.T, c *Config, grpcOption bool) { - assert.Equal(t, 13*time.Second, c.Traces.Timeout) - assert.Equal(t, 14*time.Second, c.Metrics.Timeout) - }, - }, - { - name: "Test Environment Timeout", - env: map[string]string{ - "OTEL_EXPORTER_OTLP_TIMEOUT": "15000", - }, - asserts: func(t *testing.T, c *Config, grpcOption bool) { - assert.Equal(t, c.Metrics.Timeout, 15*time.Second) - assert.Equal(t, c.Traces.Timeout, 15*time.Second) - }, - }, - { - name: "Test Environment Signal Specific Timeout", - env: map[string]string{ - "OTEL_EXPORTER_OTLP_TIMEOUT": "15000", - "OTEL_EXPORTER_OTLP_TRACES_TIMEOUT": "27000", - "OTEL_EXPORTER_OTLP_METRICS_TIMEOUT": "28000", - }, - asserts: func(t *testing.T, c *Config, grpcOption bool) { - assert.Equal(t, c.Traces.Timeout, 27*time.Second) - assert.Equal(t, c.Metrics.Timeout, 28*time.Second) - }, - }, - { - name: "Test Mixed Environment and With Timeout", - env: map[string]string{ - "OTEL_EXPORTER_OTLP_TIMEOUT": "15000", - "OTEL_EXPORTER_OTLP_TRACES_TIMEOUT": "27000", - "OTEL_EXPORTER_OTLP_METRICS_TIMEOUT": "28000", - }, - opts: []GenericOption{ - WithTracesTimeout(5 * time.Second), - }, - asserts: func(t *testing.T, c *Config, grpcOption bool) { - assert.Equal(t, c.Traces.Timeout, 5*time.Second) - assert.Equal(t, c.Metrics.Timeout, 28*time.Second) - }, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - - e := EnvOptionsReader{ - GetEnv: tt.env.getEnv, - ReadFile: tt.fileReader.readFile, - } - - // Tests Generic options as HTTP Options - cfg := NewDefaultConfig() - e.ApplyHTTPEnvConfigs(&cfg) - for _, opt := range tt.opts { - opt.ApplyHTTPOption(&cfg) - } - tt.asserts(t, &cfg, false) - - // Tests Generic options as gRPC Options - cfg = NewDefaultConfig() - e.ApplyGRPCEnvConfigs(&cfg) - for _, opt := range tt.opts { - opt.ApplyGRPCOption(&cfg) - } - tt.asserts(t, &cfg, true) - }) - } -} diff --git a/exporters/otlp/internal/otlpconfig/tls.go b/exporters/otlp/internal/otlpconfig/tls.go deleted file mode 100644 index cea3b3148..000000000 --- a/exporters/otlp/internal/otlpconfig/tls.go +++ /dev/null @@ -1,70 +0,0 @@ -// Copyright The OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package otlpconfig - -import ( - "crypto/tls" - "crypto/x509" - "errors" - "io/ioutil" -) - -//nolint:revive // ignoring missing comments for unexported constants in an internal package. -const ( - WeakCertificate = ` ------BEGIN CERTIFICATE----- -MIIBhzCCASygAwIBAgIRANHpHgAWeTnLZpTSxCKs0ggwCgYIKoZIzj0EAwIwEjEQ -MA4GA1UEChMHb3RlbC1nbzAeFw0yMTA0MDExMzU5MDNaFw0yMTA0MDExNDU5MDNa -MBIxEDAOBgNVBAoTB290ZWwtZ28wWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAAS9 -nWSkmPCxShxnp43F+PrOtbGV7sNfkbQ/kxzi9Ego0ZJdiXxkmv/C05QFddCW7Y0Z -sJCLHGogQsYnWJBXUZOVo2MwYTAOBgNVHQ8BAf8EBAMCB4AwEwYDVR0lBAwwCgYI -KwYBBQUHAwEwDAYDVR0TAQH/BAIwADAsBgNVHREEJTAjgglsb2NhbGhvc3SHEAAA -AAAAAAAAAAAAAAAAAAGHBH8AAAEwCgYIKoZIzj0EAwIDSQAwRgIhANwZVVKvfvQ/ -1HXsTvgH+xTQswOwSSKYJ1cVHQhqK7ZbAiEAus8NxpTRnp5DiTMuyVmhVNPB+bVH -Lhnm4N/QDk5rek0= ------END CERTIFICATE----- -` - WeakPrivateKey = ` ------BEGIN PRIVATE KEY----- -MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgN8HEXiXhvByrJ1zK -SFT6Y2l2KqDWwWzKf+t4CyWrNKehRANCAAS9nWSkmPCxShxnp43F+PrOtbGV7sNf -kbQ/kxzi9Ego0ZJdiXxkmv/C05QFddCW7Y0ZsJCLHGogQsYnWJBXUZOV ------END PRIVATE KEY----- -` -) - -// ReadTLSConfigFromFile reads a PEM certificate file and creates -// a tls.Config that will use this certifate to verify a server certificate. -func ReadTLSConfigFromFile(path string) (*tls.Config, error) { - b, err := ioutil.ReadFile(path) - if err != nil { - return nil, err - } - - return CreateTLSConfig(b) -} - -// CreateTLSConfig creates a tls.Config from a raw certificate bytes -// to verify a server certificate. -func CreateTLSConfig(certBytes []byte) (*tls.Config, error) { - cp := x509.NewCertPool() - if ok := cp.AppendCertsFromPEM(certBytes); !ok { - return nil, errors.New("failed to append certificate to the cert pool") - } - - return &tls.Config{ - RootCAs: cp, - }, nil -} diff --git a/exporters/otlp/internal/otlptest/collector.go b/exporters/otlp/internal/otlptest/collector.go deleted file mode 100644 index d2fcbc0e6..000000000 --- a/exporters/otlp/internal/otlptest/collector.go +++ /dev/null @@ -1,137 +0,0 @@ -// Copyright The OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package otlptest - -import ( - "sort" - - collectormetricpb "go.opentelemetry.io/proto/otlp/collector/metrics/v1" - collectortracepb "go.opentelemetry.io/proto/otlp/collector/trace/v1" - commonpb "go.opentelemetry.io/proto/otlp/common/v1" - metricpb "go.opentelemetry.io/proto/otlp/metrics/v1" - resourcepb "go.opentelemetry.io/proto/otlp/resource/v1" - tracepb "go.opentelemetry.io/proto/otlp/trace/v1" -) - -// Collector is an interface that mock collectors should implements, -// so they can be used for the end-to-end testing. -type Collector interface { - Stop() error - GetResourceSpans() []*tracepb.ResourceSpans - GetMetrics() []*metricpb.Metric -} - -// SpansStorage stores the spans. Mock collectors could use it to -// store spans they have received. -type SpansStorage struct { - rsm map[string]*tracepb.ResourceSpans - spanCount int -} - -// MetricsStorage stores the metrics. Mock collectors could use it to -// store metrics they have received. -type MetricsStorage struct { - metrics []*metricpb.Metric -} - -// NewSpansStorage creates a new spans storage. -func NewSpansStorage() SpansStorage { - return SpansStorage{ - rsm: make(map[string]*tracepb.ResourceSpans), - } -} - -// AddSpans adds spans to the spans storage. -func (s *SpansStorage) AddSpans(request *collectortracepb.ExportTraceServiceRequest) { - for _, rs := range request.GetResourceSpans() { - rstr := resourceString(rs.Resource) - if existingRs, ok := s.rsm[rstr]; !ok { - s.rsm[rstr] = rs - // TODO (rghetia): Add support for library Info. - if len(rs.InstrumentationLibrarySpans) == 0 { - rs.InstrumentationLibrarySpans = []*tracepb.InstrumentationLibrarySpans{ - { - Spans: []*tracepb.Span{}, - }, - } - } - s.spanCount += len(rs.InstrumentationLibrarySpans[0].Spans) - } else { - if len(rs.InstrumentationLibrarySpans) > 0 { - newSpans := rs.InstrumentationLibrarySpans[0].GetSpans() - existingRs.InstrumentationLibrarySpans[0].Spans = - append(existingRs.InstrumentationLibrarySpans[0].Spans, - newSpans...) - s.spanCount += len(newSpans) - } - } - } -} - -// GetSpans returns the stored spans. -func (s *SpansStorage) GetSpans() []*tracepb.Span { - spans := make([]*tracepb.Span, 0, s.spanCount) - for _, rs := range s.rsm { - spans = append(spans, rs.InstrumentationLibrarySpans[0].Spans...) - } - return spans -} - -// GetResourceSpans returns the stored resource spans. -func (s *SpansStorage) GetResourceSpans() []*tracepb.ResourceSpans { - rss := make([]*tracepb.ResourceSpans, 0, len(s.rsm)) - for _, rs := range s.rsm { - rss = append(rss, rs) - } - return rss -} - -// NewMetricsStorage creates a new metrics storage. -func NewMetricsStorage() MetricsStorage { - return MetricsStorage{} -} - -// AddMetrics adds metrics to the metrics storage. -func (s *MetricsStorage) AddMetrics(request *collectormetricpb.ExportMetricsServiceRequest) { - for _, rm := range request.GetResourceMetrics() { - // TODO (rghetia) handle multiple resource and library info. - if len(rm.InstrumentationLibraryMetrics) > 0 { - s.metrics = append(s.metrics, rm.InstrumentationLibraryMetrics[0].Metrics...) - } - } -} - -// GetMetrics returns the stored metrics. -func (s *MetricsStorage) GetMetrics() []*metricpb.Metric { - // copy in order to not change. - m := make([]*metricpb.Metric, 0, len(s.metrics)) - return append(m, s.metrics...) -} - -func resourceString(res *resourcepb.Resource) string { - sAttrs := sortedAttributes(res.GetAttributes()) - rstr := "" - for _, attr := range sAttrs { - rstr = rstr + attr.String() - } - return rstr -} - -func sortedAttributes(attrs []*commonpb.KeyValue) []*commonpb.KeyValue { - sort.Slice(attrs[:], func(i, j int) bool { - return attrs[i].Key < attrs[j].Key - }) - return attrs -} diff --git a/exporters/otlp/internal/otlptest/data.go b/exporters/otlp/internal/otlptest/data.go deleted file mode 100644 index 9d8c04557..000000000 --- a/exporters/otlp/internal/otlptest/data.go +++ /dev/null @@ -1,142 +0,0 @@ -// Copyright The OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package otlptest - -import ( - "context" - "fmt" - "time" - - "go.opentelemetry.io/otel/attribute" - "go.opentelemetry.io/otel/codes" - "go.opentelemetry.io/otel/metric" - "go.opentelemetry.io/otel/metric/number" - exportmetric "go.opentelemetry.io/otel/sdk/export/metric" - "go.opentelemetry.io/otel/sdk/instrumentation" - "go.opentelemetry.io/otel/sdk/metric/aggregator/sum" - "go.opentelemetry.io/otel/sdk/resource" - tracesdk "go.opentelemetry.io/otel/sdk/trace" - "go.opentelemetry.io/otel/sdk/trace/tracetest" - "go.opentelemetry.io/otel/trace" -) - -// Used to avoid implementing locking functions for test -// checkpointsets. -type noopLocker struct{} - -// Lock implements sync.Locker, which is needed for -// exportmetric.CheckpointSet. -func (noopLocker) Lock() {} - -// Unlock implements sync.Locker, which is needed for -// exportmetric.CheckpointSet. -func (noopLocker) Unlock() {} - -// RLock implements exportmetric.CheckpointSet. -func (noopLocker) RLock() {} - -// RUnlock implements exportmetric.CheckpointSet. -func (noopLocker) RUnlock() {} - -// OneRecordCheckpointSet is a CheckpointSet that returns just one -// filled record. It may be useful for testing driver's metrics -// export. -type OneRecordCheckpointSet struct { - noopLocker -} - -var _ exportmetric.CheckpointSet = OneRecordCheckpointSet{} - -// ForEach implements exportmetric.CheckpointSet. It always invokes -// the callback once with always the same record. -func (OneRecordCheckpointSet) ForEach(kindSelector exportmetric.ExportKindSelector, recordFunc func(exportmetric.Record) error) error { - desc := metric.NewDescriptor( - "foo", - metric.CounterInstrumentKind, - number.Int64Kind, - ) - res := resource.NewSchemaless(attribute.String("a", "b")) - agg := sum.New(1) - if err := agg[0].Update(context.Background(), number.NewInt64Number(42), &desc); err != nil { - return err - } - start := time.Date(2020, time.December, 8, 19, 15, 0, 0, time.UTC) - end := time.Date(2020, time.December, 8, 19, 16, 0, 0, time.UTC) - labels := attribute.NewSet(attribute.String("abc", "def"), attribute.Int64("one", 1)) - rec := exportmetric.NewRecord(&desc, &labels, res, agg[0].Aggregation(), start, end) - return recordFunc(rec) -} - -// SingleReadOnlySpan returns a one-element slice with a read-only span. It -// may be useful for testing driver's trace export. -func SingleReadOnlySpan() []tracesdk.ReadOnlySpan { - return tracetest.SpanStubs{ - { - SpanContext: trace.NewSpanContext(trace.SpanContextConfig{ - TraceID: trace.TraceID{2, 3, 4, 5, 6, 7, 8, 9, 2, 3, 4, 5, 6, 7, 8, 9}, - SpanID: trace.SpanID{3, 4, 5, 6, 7, 8, 9, 0}, - TraceFlags: trace.FlagsSampled, - }), - Parent: trace.NewSpanContext(trace.SpanContextConfig{ - TraceID: trace.TraceID{2, 3, 4, 5, 6, 7, 8, 9, 2, 3, 4, 5, 6, 7, 8, 9}, - SpanID: trace.SpanID{1, 2, 3, 4, 5, 6, 7, 8}, - TraceFlags: trace.FlagsSampled, - }), - SpanKind: trace.SpanKindInternal, - Name: "foo", - StartTime: time.Date(2020, time.December, 8, 20, 23, 0, 0, time.UTC), - EndTime: time.Date(2020, time.December, 0, 20, 24, 0, 0, time.UTC), - Attributes: []attribute.KeyValue{}, - Events: []tracesdk.Event{}, - Links: []trace.Link{}, - Status: tracesdk.Status{Code: codes.Ok}, - DroppedAttributes: 0, - DroppedEvents: 0, - DroppedLinks: 0, - ChildSpanCount: 0, - Resource: resource.NewSchemaless(attribute.String("a", "b")), - InstrumentationLibrary: instrumentation.Library{ - Name: "bar", - Version: "0.0.0", - }, - }, - }.Snapshots() -} - -// EmptyCheckpointSet is a checkpointer that has no records at all. -type EmptyCheckpointSet struct { - noopLocker -} - -var _ exportmetric.CheckpointSet = EmptyCheckpointSet{} - -// ForEach implements exportmetric.CheckpointSet. It never invokes the -// callback. -func (EmptyCheckpointSet) ForEach(kindSelector exportmetric.ExportKindSelector, recordFunc func(exportmetric.Record) error) error { - return nil -} - -// FailCheckpointSet is a checkpointer that returns an error during -// ForEach. -type FailCheckpointSet struct { - noopLocker -} - -var _ exportmetric.CheckpointSet = FailCheckpointSet{} - -// ForEach implements exportmetric.CheckpointSet. It always fails. -func (FailCheckpointSet) ForEach(kindSelector exportmetric.ExportKindSelector, recordFunc func(exportmetric.Record) error) error { - return fmt.Errorf("fail") -} diff --git a/exporters/otlp/internal/otlptest/otlptest.go b/exporters/otlp/internal/otlptest/otlptest.go deleted file mode 100644 index bc5d4d801..000000000 --- a/exporters/otlp/internal/otlptest/otlptest.go +++ /dev/null @@ -1,250 +0,0 @@ -// Copyright The OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package otlptest - -import ( - "context" - "fmt" - "testing" - "time" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "go.opentelemetry.io/otel/attribute" - "go.opentelemetry.io/otel/exporters/otlp" - "go.opentelemetry.io/otel/metric" - "go.opentelemetry.io/otel/metric/number" - exportmetric "go.opentelemetry.io/otel/sdk/export/metric" - controller "go.opentelemetry.io/otel/sdk/metric/controller/basic" - processor "go.opentelemetry.io/otel/sdk/metric/processor/basic" - "go.opentelemetry.io/otel/sdk/metric/selector/simple" - "go.opentelemetry.io/otel/sdk/resource" - sdktrace "go.opentelemetry.io/otel/sdk/trace" - commonpb "go.opentelemetry.io/proto/otlp/common/v1" - metricpb "go.opentelemetry.io/proto/otlp/metrics/v1" -) - -// RunEndToEndTest can be used by protocol driver tests to validate -// themselves. -func RunEndToEndTest(ctx context.Context, t *testing.T, exp *otlp.Exporter, mcTraces, mcMetrics Collector) { - pOpts := []sdktrace.TracerProviderOption{ - sdktrace.WithSampler(sdktrace.AlwaysSample()), - sdktrace.WithBatcher( - exp, - // add following two options to ensure flush - sdktrace.WithBatchTimeout(5*time.Second), - sdktrace.WithMaxExportBatchSize(10), - ), - } - tp1 := sdktrace.NewTracerProvider(append(pOpts, - sdktrace.WithResource(resource.NewSchemaless( - attribute.String("rk1", "rv11)"), - attribute.Int64("rk2", 5), - )))...) - - tp2 := sdktrace.NewTracerProvider(append(pOpts, - sdktrace.WithResource(resource.NewSchemaless( - attribute.String("rk1", "rv12)"), - attribute.Float64("rk3", 6.5), - )))...) - - tr1 := tp1.Tracer("test-tracer1") - tr2 := tp2.Tracer("test-tracer2") - // Now create few spans - m := 4 - for i := 0; i < m; i++ { - _, span := tr1.Start(ctx, "AlwaysSample") - span.SetAttributes(attribute.Int64("i", int64(i))) - span.End() - - _, span = tr2.Start(ctx, "AlwaysSample") - span.SetAttributes(attribute.Int64("i", int64(i))) - span.End() - } - - selector := simple.NewWithInexpensiveDistribution() - processor := processor.New(selector, exportmetric.StatelessExportKindSelector()) - cont := controller.New(processor, controller.WithExporter(exp)) - require.NoError(t, cont.Start(ctx)) - - meter := cont.MeterProvider().Meter("test-meter") - labels := []attribute.KeyValue{attribute.Bool("test", true)} - - type data struct { - iKind metric.InstrumentKind - nKind number.Kind - val int64 - } - instruments := map[string]data{ - "test-int64-counter": {metric.CounterInstrumentKind, number.Int64Kind, 1}, - "test-float64-counter": {metric.CounterInstrumentKind, number.Float64Kind, 1}, - "test-int64-valuerecorder": {metric.ValueRecorderInstrumentKind, number.Int64Kind, 2}, - "test-float64-valuerecorder": {metric.ValueRecorderInstrumentKind, number.Float64Kind, 2}, - "test-int64-valueobserver": {metric.ValueObserverInstrumentKind, number.Int64Kind, 3}, - "test-float64-valueobserver": {metric.ValueObserverInstrumentKind, number.Float64Kind, 3}, - } - for name, data := range instruments { - data := data - switch data.iKind { - case metric.CounterInstrumentKind: - switch data.nKind { - case number.Int64Kind: - metric.Must(meter).NewInt64Counter(name).Add(ctx, data.val, labels...) - case number.Float64Kind: - metric.Must(meter).NewFloat64Counter(name).Add(ctx, float64(data.val), labels...) - default: - assert.Failf(t, "unsupported number testing kind", data.nKind.String()) - } - case metric.ValueRecorderInstrumentKind: - switch data.nKind { - case number.Int64Kind: - metric.Must(meter).NewInt64ValueRecorder(name).Record(ctx, data.val, labels...) - case number.Float64Kind: - metric.Must(meter).NewFloat64ValueRecorder(name).Record(ctx, float64(data.val), labels...) - default: - assert.Failf(t, "unsupported number testing kind", data.nKind.String()) - } - case metric.ValueObserverInstrumentKind: - switch data.nKind { - case number.Int64Kind: - metric.Must(meter).NewInt64ValueObserver(name, - func(_ context.Context, result metric.Int64ObserverResult) { - result.Observe(data.val, labels...) - }, - ) - case number.Float64Kind: - callback := func(v float64) metric.Float64ObserverFunc { - return metric.Float64ObserverFunc(func(_ context.Context, result metric.Float64ObserverResult) { result.Observe(v, labels...) }) - }(float64(data.val)) - metric.Must(meter).NewFloat64ValueObserver(name, callback) - default: - assert.Failf(t, "unsupported number testing kind", data.nKind.String()) - } - default: - assert.Failf(t, "unsupported metrics testing kind", data.iKind.String()) - } - } - - // Flush and close. - require.NoError(t, cont.Stop(ctx)) - func() { - ctx, cancel := context.WithTimeout(ctx, 10*time.Second) - defer cancel() - if err := tp1.Shutdown(ctx); err != nil { - t.Fatalf("failed to shut down a tracer provider 1: %v", err) - } - if err := tp2.Shutdown(ctx); err != nil { - t.Fatalf("failed to shut down a tracer provider 2: %v", err) - } - }() - - // Wait >2 cycles. - <-time.After(40 * time.Millisecond) - - // Now shutdown the exporter - ctx, cancel := context.WithTimeout(ctx, 10*time.Second) - defer cancel() - if err := exp.Shutdown(ctx); err != nil { - t.Fatalf("failed to stop the exporter: %v", err) - } - - // Shutdown the collector too so that we can begin - // verification checks of expected data back. - _ = mcTraces.Stop() - _ = mcMetrics.Stop() - - // Now verify that we only got two resources - rss := mcTraces.GetResourceSpans() - if got, want := len(rss), 2; got != want { - t.Fatalf("resource span count: got %d, want %d\n", got, want) - } - - // Now verify spans and attributes for each resource span. - for _, rs := range rss { - if len(rs.InstrumentationLibrarySpans) == 0 { - t.Fatalf("zero Instrumentation Library Spans") - } - if got, want := len(rs.InstrumentationLibrarySpans[0].Spans), m; got != want { - t.Fatalf("span counts: got %d, want %d", got, want) - } - attrMap := map[int64]bool{} - for _, s := range rs.InstrumentationLibrarySpans[0].Spans { - if gotName, want := s.Name, "AlwaysSample"; gotName != want { - t.Fatalf("span name: got %s, want %s", gotName, want) - } - attrMap[s.Attributes[0].Value.Value.(*commonpb.AnyValue_IntValue).IntValue] = true - } - if got, want := len(attrMap), m; got != want { - t.Fatalf("span attribute unique values: got %d want %d", got, want) - } - for i := 0; i < m; i++ { - _, ok := attrMap[int64(i)] - if !ok { - t.Fatalf("span with attribute %d missing", i) - } - } - } - - metrics := mcMetrics.GetMetrics() - assert.Len(t, metrics, len(instruments), "not enough metrics exported") - seen := make(map[string]struct{}, len(instruments)) - for _, m := range metrics { - data, ok := instruments[m.Name] - if !ok { - assert.Failf(t, "unknown metrics", m.Name) - continue - } - seen[m.Name] = struct{}{} - - switch data.iKind { - case metric.CounterInstrumentKind, metric.ValueObserverInstrumentKind: - var dp []*metricpb.NumberDataPoint - switch data.iKind { - case metric.CounterInstrumentKind: - require.NotNil(t, m.GetSum()) - dp = m.GetSum().GetDataPoints() - case metric.ValueObserverInstrumentKind: - require.NotNil(t, m.GetGauge()) - dp = m.GetGauge().GetDataPoints() - } - if assert.Len(t, dp, 1) { - switch data.nKind { - case number.Int64Kind: - v := &metricpb.NumberDataPoint_AsInt{AsInt: data.val} - assert.Equal(t, v, dp[0].Value, "invalid value for %q", m.Name) - case number.Float64Kind: - v := &metricpb.NumberDataPoint_AsDouble{AsDouble: float64(data.val)} - assert.Equal(t, v, dp[0].Value, "invalid value for %q", m.Name) - } - } - case metric.ValueRecorderInstrumentKind: - require.NotNil(t, m.GetSummary()) - if dp := m.GetSummary().DataPoints; assert.Len(t, dp, 1) { - count := dp[0].Count - assert.Equal(t, uint64(1), count, "invalid count for %q", m.Name) - assert.Equal(t, float64(data.val*int64(count)), dp[0].Sum, "invalid sum for %q (value %d)", m.Name, data.val) - } - default: - assert.Failf(t, "invalid metrics kind", data.iKind.String()) - } - } - - for i := range instruments { - if _, ok := seen[i]; !ok { - assert.Fail(t, fmt.Sprintf("no metric(s) exported for %q", i)) - } - } -} diff --git a/exporters/otlp/internal/transform/attribute.go b/exporters/otlp/internal/transform/attribute.go deleted file mode 100644 index 5902cad6a..000000000 --- a/exporters/otlp/internal/transform/attribute.go +++ /dev/null @@ -1,141 +0,0 @@ -// Copyright The OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package transform - -import ( - "reflect" - - "go.opentelemetry.io/otel/attribute" - commonpb "go.opentelemetry.io/proto/otlp/common/v1" - - "go.opentelemetry.io/otel/sdk/resource" -) - -// Attributes transforms a slice of KeyValues into a slice of OTLP attribute key-values. -func Attributes(attrs []attribute.KeyValue) []*commonpb.KeyValue { - if len(attrs) == 0 { - return nil - } - - out := make([]*commonpb.KeyValue, 0, len(attrs)) - for _, kv := range attrs { - out = append(out, toAttribute(kv)) - } - return out -} - -// ResourceAttributes transforms a Resource into a slice of OTLP attribute key-values. -func ResourceAttributes(resource *resource.Resource) []*commonpb.KeyValue { - if resource.Len() == 0 { - return nil - } - - out := make([]*commonpb.KeyValue, 0, resource.Len()) - for iter := resource.Iter(); iter.Next(); { - out = append(out, toAttribute(iter.Attribute())) - } - - return out -} - -func toAttribute(v attribute.KeyValue) *commonpb.KeyValue { - result := &commonpb.KeyValue{ - Key: string(v.Key), - Value: new(commonpb.AnyValue), - } - switch v.Value.Type() { - case attribute.BOOL: - result.Value.Value = &commonpb.AnyValue_BoolValue{ - BoolValue: v.Value.AsBool(), - } - case attribute.INT64: - result.Value.Value = &commonpb.AnyValue_IntValue{ - IntValue: v.Value.AsInt64(), - } - case attribute.FLOAT64: - result.Value.Value = &commonpb.AnyValue_DoubleValue{ - DoubleValue: v.Value.AsFloat64(), - } - case attribute.STRING: - result.Value.Value = &commonpb.AnyValue_StringValue{ - StringValue: v.Value.AsString(), - } - case attribute.ARRAY: - result.Value.Value = &commonpb.AnyValue_ArrayValue{ - ArrayValue: &commonpb.ArrayValue{ - Values: arrayValues(v), - }, - } - default: - result.Value.Value = &commonpb.AnyValue_StringValue{ - StringValue: "INVALID", - } - } - return result -} - -func arrayValues(kv attribute.KeyValue) []*commonpb.AnyValue { - a := kv.Value.AsArray() - aType := reflect.TypeOf(a) - var valueFunc func(reflect.Value) *commonpb.AnyValue - switch aType.Elem().Kind() { - case reflect.Bool: - valueFunc = func(v reflect.Value) *commonpb.AnyValue { - return &commonpb.AnyValue{ - Value: &commonpb.AnyValue_BoolValue{ - BoolValue: v.Bool(), - }, - } - } - case reflect.Int, reflect.Int64: - valueFunc = func(v reflect.Value) *commonpb.AnyValue { - return &commonpb.AnyValue{ - Value: &commonpb.AnyValue_IntValue{ - IntValue: v.Int(), - }, - } - } - case reflect.Uintptr: - valueFunc = func(v reflect.Value) *commonpb.AnyValue { - return &commonpb.AnyValue{ - Value: &commonpb.AnyValue_IntValue{ - IntValue: int64(v.Uint()), - }, - } - } - case reflect.Float64: - valueFunc = func(v reflect.Value) *commonpb.AnyValue { - return &commonpb.AnyValue{ - Value: &commonpb.AnyValue_DoubleValue{ - DoubleValue: v.Float(), - }, - } - } - case reflect.String: - valueFunc = func(v reflect.Value) *commonpb.AnyValue { - return &commonpb.AnyValue{ - Value: &commonpb.AnyValue_StringValue{ - StringValue: v.String(), - }, - } - } - } - - results := make([]*commonpb.AnyValue, aType.Len()) - for i, aValue := 0, reflect.ValueOf(a); i < aValue.Len(); i++ { - results[i] = valueFunc(aValue.Index(i)) - } - return results -} diff --git a/exporters/otlp/internal/transform/attribute_test.go b/exporters/otlp/internal/transform/attribute_test.go deleted file mode 100644 index 9d86a9687..000000000 --- a/exporters/otlp/internal/transform/attribute_test.go +++ /dev/null @@ -1,256 +0,0 @@ -// Copyright The OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package transform - -import ( - "testing" - - "github.com/stretchr/testify/assert" - - "go.opentelemetry.io/otel/attribute" - commonpb "go.opentelemetry.io/proto/otlp/common/v1" -) - -type attributeTest struct { - attrs []attribute.KeyValue - expected []*commonpb.KeyValue -} - -func TestAttributes(t *testing.T) { - for _, test := range []attributeTest{ - {nil, nil}, - { - []attribute.KeyValue{ - attribute.Int("int to int", 123), - attribute.Int64("int64 to int64", 1234567), - attribute.Float64("float64 to double", 1.61), - attribute.String("string to string", "string"), - attribute.Bool("bool to bool", true), - }, - []*commonpb.KeyValue{ - { - Key: "int to int", - Value: &commonpb.AnyValue{ - Value: &commonpb.AnyValue_IntValue{ - IntValue: 123, - }, - }, - }, - { - Key: "int64 to int64", - Value: &commonpb.AnyValue{ - Value: &commonpb.AnyValue_IntValue{ - IntValue: 1234567, - }, - }, - }, - { - Key: "float64 to double", - Value: &commonpb.AnyValue{ - Value: &commonpb.AnyValue_DoubleValue{ - DoubleValue: 1.61, - }, - }, - }, - { - Key: "string to string", - Value: &commonpb.AnyValue{ - Value: &commonpb.AnyValue_StringValue{ - StringValue: "string", - }, - }, - }, - { - Key: "bool to bool", - Value: &commonpb.AnyValue{ - Value: &commonpb.AnyValue_BoolValue{ - BoolValue: true, - }, - }, - }, - }, - }, - } { - got := Attributes(test.attrs) - if !assert.Len(t, got, len(test.expected)) { - continue - } - for i, actual := range got { - if a, ok := actual.Value.Value.(*commonpb.AnyValue_DoubleValue); ok { - e, ok := test.expected[i].Value.Value.(*commonpb.AnyValue_DoubleValue) - if !ok { - t.Errorf("expected AnyValue_DoubleValue, got %T", test.expected[i].Value.Value) - continue - } - if !assert.InDelta(t, e.DoubleValue, a.DoubleValue, 0.01) { - continue - } - e.DoubleValue = a.DoubleValue - } - assert.Equal(t, test.expected[i], actual) - } - } -} - -func TestArrayAttributes(t *testing.T) { - // Array KeyValue supports only arrays of primitive types: - // "bool", "int", "int64", - // "float64", "string", - for _, test := range []attributeTest{ - {nil, nil}, - { - []attribute.KeyValue{ - attribute.Array("invalid", [][]string{{"1", "2"}, {"a"}}), - }, - []*commonpb.KeyValue{ - { - Key: "invalid", - Value: &commonpb.AnyValue{ - Value: &commonpb.AnyValue_StringValue{ - StringValue: "INVALID", - }, - }, - }, - }, - }, - { - []attribute.KeyValue{ - attribute.Array("bool array to bool array", []bool{true, false}), - attribute.Array("int array to int64 array", []int{1, 2, 3}), - attribute.Array("int64 array to int64 array", []int64{1, 2, 3}), - attribute.Array("float64 array to double array", []float64{1.11, 2.22, 3.33}), - attribute.Array("string array to string array", []string{"foo", "bar", "baz"}), - }, - []*commonpb.KeyValue{ - newOTelBoolArray("bool array to bool array", []bool{true, false}), - newOTelIntArray("int array to int64 array", []int64{1, 2, 3}), - newOTelIntArray("int64 array to int64 array", []int64{1, 2, 3}), - newOTelDoubleArray("float64 array to double array", []float64{1.11, 2.22, 3.33}), - newOTelStringArray("string array to string array", []string{"foo", "bar", "baz"}), - }, - }, - } { - actualArrayAttributes := Attributes(test.attrs) - expectedArrayAttributes := test.expected - if !assert.Len(t, actualArrayAttributes, len(expectedArrayAttributes)) { - continue - } - - for i, actualArrayAttr := range actualArrayAttributes { - expectedArrayAttr := expectedArrayAttributes[i] - expectedKey, actualKey := expectedArrayAttr.Key, actualArrayAttr.Key - if !assert.Equal(t, expectedKey, actualKey) { - continue - } - - expected := expectedArrayAttr.Value.GetArrayValue() - actual := actualArrayAttr.Value.GetArrayValue() - if expected == nil { - assert.Nil(t, actual) - continue - } - if assert.NotNil(t, actual, "expected not nil for %s", actualKey) { - assertExpectedArrayValues(t, expected.Values, actual.Values) - } - } - - } -} - -func assertExpectedArrayValues(t *testing.T, expectedValues, actualValues []*commonpb.AnyValue) { - for i, actual := range actualValues { - expected := expectedValues[i] - if a, ok := actual.Value.(*commonpb.AnyValue_DoubleValue); ok { - e, ok := expected.Value.(*commonpb.AnyValue_DoubleValue) - if !ok { - t.Errorf("expected AnyValue_DoubleValue, got %T", expected.Value) - continue - } - if !assert.InDelta(t, e.DoubleValue, a.DoubleValue, 0.01) { - continue - } - e.DoubleValue = a.DoubleValue - } - assert.Equal(t, expected, actual) - } -} - -func newOTelBoolArray(key string, values []bool) *commonpb.KeyValue { - arrayValues := []*commonpb.AnyValue{} - for _, b := range values { - arrayValues = append(arrayValues, &commonpb.AnyValue{ - Value: &commonpb.AnyValue_BoolValue{ - BoolValue: b, - }, - }) - } - - return newOTelArray(key, arrayValues) -} - -func newOTelIntArray(key string, values []int64) *commonpb.KeyValue { - arrayValues := []*commonpb.AnyValue{} - - for _, i := range values { - arrayValues = append(arrayValues, &commonpb.AnyValue{ - Value: &commonpb.AnyValue_IntValue{ - IntValue: i, - }, - }) - } - - return newOTelArray(key, arrayValues) -} - -func newOTelDoubleArray(key string, values []float64) *commonpb.KeyValue { - arrayValues := []*commonpb.AnyValue{} - - for _, d := range values { - arrayValues = append(arrayValues, &commonpb.AnyValue{ - Value: &commonpb.AnyValue_DoubleValue{ - DoubleValue: d, - }, - }) - } - - return newOTelArray(key, arrayValues) -} - -func newOTelStringArray(key string, values []string) *commonpb.KeyValue { - arrayValues := []*commonpb.AnyValue{} - - for _, s := range values { - arrayValues = append(arrayValues, &commonpb.AnyValue{ - Value: &commonpb.AnyValue_StringValue{ - StringValue: s, - }, - }) - } - - return newOTelArray(key, arrayValues) -} - -func newOTelArray(key string, arrayValues []*commonpb.AnyValue) *commonpb.KeyValue { - return &commonpb.KeyValue{ - Key: key, - Value: &commonpb.AnyValue{ - Value: &commonpb.AnyValue_ArrayValue{ - ArrayValue: &commonpb.ArrayValue{ - Values: arrayValues, - }, - }, - }, - } -} diff --git a/exporters/otlp/internal/transform/instrumentation.go b/exporters/otlp/internal/transform/instrumentation.go deleted file mode 100644 index 4ed5f9664..000000000 --- a/exporters/otlp/internal/transform/instrumentation.go +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright The OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package transform - -import ( - commonpb "go.opentelemetry.io/proto/otlp/common/v1" - - "go.opentelemetry.io/otel/sdk/instrumentation" -) - -func instrumentationLibrary(il instrumentation.Library) *commonpb.InstrumentationLibrary { - if il == (instrumentation.Library{}) { - return nil - } - return &commonpb.InstrumentationLibrary{ - Name: il.Name, - Version: il.Version, - } -} diff --git a/exporters/otlp/internal/transform/metric.go b/exporters/otlp/internal/transform/metric.go deleted file mode 100644 index 518ae8e65..000000000 --- a/exporters/otlp/internal/transform/metric.go +++ /dev/null @@ -1,690 +0,0 @@ -// Copyright The OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// Package transform provides translations for opentelemetry-go concepts and -// structures to otlp structures. -package transform - -import ( - "context" - "errors" - "fmt" - "strings" - "sync" - "time" - - "go.opentelemetry.io/otel/attribute" - commonpb "go.opentelemetry.io/proto/otlp/common/v1" - metricpb "go.opentelemetry.io/proto/otlp/metrics/v1" - resourcepb "go.opentelemetry.io/proto/otlp/resource/v1" - - "go.opentelemetry.io/otel/metric/number" - export "go.opentelemetry.io/otel/sdk/export/metric" - "go.opentelemetry.io/otel/sdk/export/metric/aggregation" - "go.opentelemetry.io/otel/sdk/instrumentation" - "go.opentelemetry.io/otel/sdk/resource" -) - -var ( - // ErrUnimplementedAgg is returned when a transformation of an unimplemented - // aggregator is attempted. - ErrUnimplementedAgg = errors.New("unimplemented aggregator") - - // ErrIncompatibleAgg is returned when - // aggregation.Kind implies an interface conversion that has - // failed - ErrIncompatibleAgg = errors.New("incompatible aggregation type") - - // ErrUnknownValueType is returned when a transformation of an unknown value - // is attempted. - ErrUnknownValueType = errors.New("invalid value type") - - // ErrContextCanceled is returned when a context cancellation halts a - // transformation. - ErrContextCanceled = errors.New("context canceled") - - // ErrTransforming is returned when an unexected error is encoutered transforming. - ErrTransforming = errors.New("transforming failed") -) - -// result is the product of transforming Records into OTLP Metrics. -type result struct { - Resource *resource.Resource - InstrumentationLibrary instrumentation.Library - Metric *metricpb.Metric - Err error -} - -// toNanos returns the number of nanoseconds since the UNIX epoch. -func toNanos(t time.Time) uint64 { - if t.IsZero() { - return 0 - } - return uint64(t.UnixNano()) -} - -// CheckpointSet transforms all records contained in a checkpoint into -// batched OTLP ResourceMetrics. -func CheckpointSet(ctx context.Context, exportSelector export.ExportKindSelector, cps export.CheckpointSet, numWorkers uint) ([]*metricpb.ResourceMetrics, error) { - records, errc := source(ctx, exportSelector, cps) - - // Start a fixed number of goroutines to transform records. - transformed := make(chan result) - var wg sync.WaitGroup - wg.Add(int(numWorkers)) - for i := uint(0); i < numWorkers; i++ { - go func() { - defer wg.Done() - transformer(ctx, exportSelector, records, transformed) - }() - } - go func() { - wg.Wait() - close(transformed) - }() - - // Synchronously collect the transformed records and transmit. - rms, err := sink(ctx, transformed) - if err != nil { - return nil, err - } - - // source is complete, check for any errors. - if err := <-errc; err != nil { - return nil, err - } - return rms, nil -} - -// source starts a goroutine that sends each one of the Records yielded by -// the CheckpointSet on the returned chan. Any error encoutered will be sent -// on the returned error chan after seeding is complete. -func source(ctx context.Context, exportSelector export.ExportKindSelector, cps export.CheckpointSet) (<-chan export.Record, <-chan error) { - errc := make(chan error, 1) - out := make(chan export.Record) - // Seed records into process. - go func() { - defer close(out) - // No select is needed since errc is buffered. - errc <- cps.ForEach(exportSelector, func(r export.Record) error { - select { - case <-ctx.Done(): - return ErrContextCanceled - case out <- r: - } - return nil - }) - }() - return out, errc -} - -// transformer transforms records read from the passed in chan into -// OTLP Metrics which are sent on the out chan. -func transformer(ctx context.Context, exportSelector export.ExportKindSelector, in <-chan export.Record, out chan<- result) { - for r := range in { - m, err := Record(exportSelector, r) - // Propagate errors, but do not send empty results. - if err == nil && m == nil { - continue - } - res := result{ - Resource: r.Resource(), - InstrumentationLibrary: instrumentation.Library{ - Name: r.Descriptor().InstrumentationName(), - Version: r.Descriptor().InstrumentationVersion(), - }, - Metric: m, - Err: err, - } - select { - case <-ctx.Done(): - return - case out <- res: - } - } -} - -// sink collects transformed Records and batches them. -// -// Any errors encoutered transforming input will be reported with an -// ErrTransforming as well as the completed ResourceMetrics. It is up to the -// caller to handle any incorrect data in these ResourceMetrics. -func sink(ctx context.Context, in <-chan result) ([]*metricpb.ResourceMetrics, error) { - var errStrings []string - - type resourceBatch struct { - Resource *resourcepb.Resource - // Group by instrumentation library name and then the MetricDescriptor. - InstrumentationLibraryBatches map[instrumentation.Library]map[string]*metricpb.Metric - SchemaURL string - } - - // group by unique Resource string. - grouped := make(map[attribute.Distinct]resourceBatch) - for res := range in { - if res.Err != nil { - errStrings = append(errStrings, res.Err.Error()) - continue - } - - rID := res.Resource.Equivalent() - rb, ok := grouped[rID] - if !ok { - rb = resourceBatch{ - Resource: Resource(res.Resource), - InstrumentationLibraryBatches: make(map[instrumentation.Library]map[string]*metricpb.Metric), - } - if res.Resource != nil { - rb.SchemaURL = res.Resource.SchemaURL() - } - grouped[rID] = rb - } - - mb, ok := rb.InstrumentationLibraryBatches[res.InstrumentationLibrary] - if !ok { - mb = make(map[string]*metricpb.Metric) - rb.InstrumentationLibraryBatches[res.InstrumentationLibrary] = mb - } - - mID := res.Metric.GetName() - m, ok := mb[mID] - if !ok { - mb[mID] = res.Metric - continue - } - switch res.Metric.Data.(type) { - case *metricpb.Metric_Gauge: - m.GetGauge().DataPoints = append(m.GetGauge().DataPoints, res.Metric.GetGauge().DataPoints...) - case *metricpb.Metric_Sum: - m.GetSum().DataPoints = append(m.GetSum().DataPoints, res.Metric.GetSum().DataPoints...) - case *metricpb.Metric_Histogram: - m.GetHistogram().DataPoints = append(m.GetHistogram().DataPoints, res.Metric.GetHistogram().DataPoints...) - case *metricpb.Metric_Summary: - m.GetSummary().DataPoints = append(m.GetSummary().DataPoints, res.Metric.GetSummary().DataPoints...) - default: - err := fmt.Sprintf("unsupported metric type: %T", res.Metric.Data) - errStrings = append(errStrings, err) - } - } - - if len(grouped) == 0 { - return nil, nil - } - - var rms []*metricpb.ResourceMetrics - for _, rb := range grouped { - // TODO: populate ResourceMetrics.SchemaURL when the field is added to the Protobuf message. - rm := &metricpb.ResourceMetrics{Resource: rb.Resource} - for il, mb := range rb.InstrumentationLibraryBatches { - ilm := &metricpb.InstrumentationLibraryMetrics{ - Metrics: make([]*metricpb.Metric, 0, len(mb)), - } - if il != (instrumentation.Library{}) { - ilm.InstrumentationLibrary = &commonpb.InstrumentationLibrary{ - Name: il.Name, - Version: il.Version, - } - } - for _, m := range mb { - ilm.Metrics = append(ilm.Metrics, m) - } - rm.InstrumentationLibraryMetrics = append(rm.InstrumentationLibraryMetrics, ilm) - } - rms = append(rms, rm) - } - - // Report any transform errors. - if len(errStrings) > 0 { - return rms, fmt.Errorf("%w:\n -%s", ErrTransforming, strings.Join(errStrings, "\n -")) - } - return rms, nil -} - -// Record transforms a Record into an OTLP Metric. An ErrIncompatibleAgg -// error is returned if the Record Aggregator is not supported. -func Record(exportSelector export.ExportKindSelector, r export.Record) (*metricpb.Metric, error) { - agg := r.Aggregation() - switch agg.Kind() { - case aggregation.MinMaxSumCountKind: - mmsc, ok := agg.(aggregation.MinMaxSumCount) - if !ok { - return nil, fmt.Errorf("%w: %T", ErrIncompatibleAgg, agg) - } - return minMaxSumCount(r, mmsc) - - case aggregation.HistogramKind: - h, ok := agg.(aggregation.Histogram) - if !ok { - return nil, fmt.Errorf("%w: %T", ErrIncompatibleAgg, agg) - } - return histogramPoint(r, exportSelector.ExportKindFor(r.Descriptor(), aggregation.HistogramKind), h) - - case aggregation.SumKind: - s, ok := agg.(aggregation.Sum) - if !ok { - return nil, fmt.Errorf("%w: %T", ErrIncompatibleAgg, agg) - } - sum, err := s.Sum() - if err != nil { - return nil, err - } - return sumPoint(r, sum, r.StartTime(), r.EndTime(), exportSelector.ExportKindFor(r.Descriptor(), aggregation.SumKind), r.Descriptor().InstrumentKind().Monotonic()) - - case aggregation.LastValueKind: - lv, ok := agg.(aggregation.LastValue) - if !ok { - return nil, fmt.Errorf("%w: %T", ErrIncompatibleAgg, agg) - } - value, tm, err := lv.LastValue() - if err != nil { - return nil, err - } - return gaugePoint(r, value, time.Time{}, tm) - - case aggregation.ExactKind: - e, ok := agg.(aggregation.Points) - if !ok { - return nil, fmt.Errorf("%w: %T", ErrIncompatibleAgg, agg) - } - pts, err := e.Points() - if err != nil { - return nil, err - } - - return gaugeArray(r, pts) - - default: - return nil, fmt.Errorf("%w: %T", ErrUnimplementedAgg, agg) - } -} - -func gaugeArray(record export.Record, points []aggregation.Point) (*metricpb.Metric, error) { - desc := record.Descriptor() - labels := record.Labels() - m := &metricpb.Metric{ - Name: desc.Name(), - Description: desc.Description(), - Unit: string(desc.Unit()), - } - - pbAttrs := keyValues(labels.Iter()) - - ndp := make([]*metricpb.NumberDataPoint, 0, len(points)) - switch nk := desc.NumberKind(); nk { - case number.Int64Kind: - for _, p := range points { - ndp = append(ndp, &metricpb.NumberDataPoint{ - Attributes: pbAttrs, - StartTimeUnixNano: toNanos(record.StartTime()), - TimeUnixNano: toNanos(record.EndTime()), - Value: &metricpb.NumberDataPoint_AsInt{ - AsInt: p.Number.CoerceToInt64(nk), - }, - }) - } - case number.Float64Kind: - for _, p := range points { - ndp = append(ndp, &metricpb.NumberDataPoint{ - Attributes: pbAttrs, - StartTimeUnixNano: toNanos(record.StartTime()), - TimeUnixNano: toNanos(record.EndTime()), - Value: &metricpb.NumberDataPoint_AsDouble{ - AsDouble: p.Number.CoerceToFloat64(nk), - }, - }) - } - default: - return nil, fmt.Errorf("%w: %v", ErrUnknownValueType, nk) - } - - m.Data = &metricpb.Metric_Gauge{ - Gauge: &metricpb.Gauge{ - DataPoints: ndp, - }, - } - return m, nil -} - -func gaugePoint(record export.Record, num number.Number, start, end time.Time) (*metricpb.Metric, error) { - desc := record.Descriptor() - labels := record.Labels() - - m := &metricpb.Metric{ - Name: desc.Name(), - Description: desc.Description(), - Unit: string(desc.Unit()), - } - - switch n := desc.NumberKind(); n { - case number.Int64Kind: - m.Data = &metricpb.Metric_Gauge{ - Gauge: &metricpb.Gauge{ - DataPoints: []*metricpb.NumberDataPoint{ - { - Value: &metricpb.NumberDataPoint_AsInt{ - AsInt: num.CoerceToInt64(n), - }, - Attributes: keyValues(labels.Iter()), - StartTimeUnixNano: toNanos(start), - TimeUnixNano: toNanos(end), - }, - }, - }, - } - case number.Float64Kind: - m.Data = &metricpb.Metric_Gauge{ - Gauge: &metricpb.Gauge{ - DataPoints: []*metricpb.NumberDataPoint{ - { - Value: &metricpb.NumberDataPoint_AsDouble{ - AsDouble: num.CoerceToFloat64(n), - }, - Attributes: keyValues(labels.Iter()), - StartTimeUnixNano: toNanos(start), - TimeUnixNano: toNanos(end), - }, - }, - }, - } - default: - return nil, fmt.Errorf("%w: %v", ErrUnknownValueType, n) - } - - return m, nil -} - -func exportKindToTemporality(ek export.ExportKind) metricpb.AggregationTemporality { - switch ek { - case export.DeltaExportKind: - return metricpb.AggregationTemporality_AGGREGATION_TEMPORALITY_DELTA - case export.CumulativeExportKind: - return metricpb.AggregationTemporality_AGGREGATION_TEMPORALITY_CUMULATIVE - } - return metricpb.AggregationTemporality_AGGREGATION_TEMPORALITY_UNSPECIFIED -} - -func sumPoint(record export.Record, num number.Number, start, end time.Time, ek export.ExportKind, monotonic bool) (*metricpb.Metric, error) { - desc := record.Descriptor() - labels := record.Labels() - - m := &metricpb.Metric{ - Name: desc.Name(), - Description: desc.Description(), - Unit: string(desc.Unit()), - } - - switch n := desc.NumberKind(); n { - case number.Int64Kind: - m.Data = &metricpb.Metric_Sum{ - Sum: &metricpb.Sum{ - IsMonotonic: monotonic, - AggregationTemporality: exportKindToTemporality(ek), - DataPoints: []*metricpb.NumberDataPoint{ - { - Value: &metricpb.NumberDataPoint_AsInt{ - AsInt: num.CoerceToInt64(n), - }, - Attributes: keyValues(labels.Iter()), - StartTimeUnixNano: toNanos(start), - TimeUnixNano: toNanos(end), - }, - }, - }, - } - case number.Float64Kind: - m.Data = &metricpb.Metric_Sum{ - Sum: &metricpb.Sum{ - IsMonotonic: monotonic, - AggregationTemporality: exportKindToTemporality(ek), - DataPoints: []*metricpb.NumberDataPoint{ - { - Value: &metricpb.NumberDataPoint_AsDouble{ - AsDouble: num.CoerceToFloat64(n), - }, - Attributes: keyValues(labels.Iter()), - StartTimeUnixNano: toNanos(start), - TimeUnixNano: toNanos(end), - }, - }, - }, - } - default: - return nil, fmt.Errorf("%w: %v", ErrUnknownValueType, n) - } - - return m, nil -} - -// minMaxSumCountValue returns the values of the MinMaxSumCount Aggregator -// as discrete values. -func minMaxSumCountValues(a aggregation.MinMaxSumCount) (min, max, sum number.Number, count uint64, err error) { - if min, err = a.Min(); err != nil { - return - } - if max, err = a.Max(); err != nil { - return - } - if sum, err = a.Sum(); err != nil { - return - } - if count, err = a.Count(); err != nil { - return - } - return -} - -// minMaxSumCount transforms a MinMaxSumCount Aggregator into an OTLP Metric. -func minMaxSumCount(record export.Record, a aggregation.MinMaxSumCount) (*metricpb.Metric, error) { - desc := record.Descriptor() - labels := record.Labels() - min, max, sum, count, err := minMaxSumCountValues(a) - if err != nil { - return nil, err - } - - m := &metricpb.Metric{ - Name: desc.Name(), - Description: desc.Description(), - Unit: string(desc.Unit()), - Data: &metricpb.Metric_Summary{ - Summary: &metricpb.Summary{ - DataPoints: []*metricpb.SummaryDataPoint{ - { - Sum: sum.CoerceToFloat64(desc.NumberKind()), - Attributes: keyValues(labels.Iter()), - StartTimeUnixNano: toNanos(record.StartTime()), - TimeUnixNano: toNanos(record.EndTime()), - Count: uint64(count), - QuantileValues: []*metricpb.SummaryDataPoint_ValueAtQuantile{ - { - Quantile: 0.0, - Value: min.CoerceToFloat64(desc.NumberKind()), - }, - { - Quantile: 1.0, - Value: max.CoerceToFloat64(desc.NumberKind()), - }, - }, - }, - }, - }, - }, - } - return m, nil -} - -func histogramValues(a aggregation.Histogram) (boundaries []float64, counts []uint64, err error) { - var buckets aggregation.Buckets - if buckets, err = a.Histogram(); err != nil { - return - } - boundaries, counts = buckets.Boundaries, buckets.Counts - if len(counts) != len(boundaries)+1 { - err = ErrTransforming - return - } - return -} - -// histogram transforms a Histogram Aggregator into an OTLP Metric. -func histogramPoint(record export.Record, ek export.ExportKind, a aggregation.Histogram) (*metricpb.Metric, error) { - desc := record.Descriptor() - labels := record.Labels() - boundaries, counts, err := histogramValues(a) - if err != nil { - return nil, err - } - - count, err := a.Count() - if err != nil { - return nil, err - } - - sum, err := a.Sum() - if err != nil { - return nil, err - } - - m := &metricpb.Metric{ - Name: desc.Name(), - Description: desc.Description(), - Unit: string(desc.Unit()), - Data: &metricpb.Metric_Histogram{ - Histogram: &metricpb.Histogram{ - AggregationTemporality: exportKindToTemporality(ek), - DataPoints: []*metricpb.HistogramDataPoint{ - { - Sum: sum.CoerceToFloat64(desc.NumberKind()), - Attributes: keyValues(labels.Iter()), - StartTimeUnixNano: toNanos(record.StartTime()), - TimeUnixNano: toNanos(record.EndTime()), - Count: uint64(count), - BucketCounts: counts, - ExplicitBounds: boundaries, - }, - }, - }, - }, - } - return m, nil -} - -// keyValues transforms an attribute iterator into an OTLP KeyValues. -func keyValues(iter attribute.Iterator) []*commonpb.KeyValue { - l := iter.Len() - if l == 0 { - return nil - } - result := make([]*commonpb.KeyValue, 0, l) - for iter.Next() { - kv := iter.Label() - result = append(result, &commonpb.KeyValue{ - Key: string(kv.Key), - Value: value(kv.Value), - }) - } - return result -} - -// value transforms an attribute Value into an OTLP AnyValue. -func value(v attribute.Value) *commonpb.AnyValue { - switch v.Type() { - case attribute.BOOL: - return &commonpb.AnyValue{ - Value: &commonpb.AnyValue_BoolValue{ - BoolValue: v.AsBool(), - }, - } - case attribute.INT64: - return &commonpb.AnyValue{ - Value: &commonpb.AnyValue_IntValue{ - IntValue: v.AsInt64(), - }, - } - case attribute.FLOAT64: - return &commonpb.AnyValue{ - Value: &commonpb.AnyValue_DoubleValue{ - DoubleValue: v.AsFloat64(), - }, - } - case attribute.ARRAY: - return &commonpb.AnyValue{ - Value: &commonpb.AnyValue_ArrayValue{ - ArrayValue: &commonpb.ArrayValue{ - Values: arrayValue(v.AsArray()), - }, - }, - } - default: - return &commonpb.AnyValue{ - Value: &commonpb.AnyValue_StringValue{ - StringValue: v.Emit(), - }, - } - } -} - -// arrayValue transforms an attribute Value of ARRAY type into an slice of -// OTLP AnyValue. -func arrayValue(arr interface{}) []*commonpb.AnyValue { - var av []*commonpb.AnyValue - switch val := arr.(type) { - case []bool: - av = make([]*commonpb.AnyValue, len(val)) - for i, v := range val { - av[i] = &commonpb.AnyValue{ - Value: &commonpb.AnyValue_BoolValue{ - BoolValue: v, - }, - } - } - case []int: - av = make([]*commonpb.AnyValue, len(val)) - for i, v := range val { - av[i] = &commonpb.AnyValue{ - Value: &commonpb.AnyValue_IntValue{ - IntValue: int64(v), - }, - } - } - case []int64: - av = make([]*commonpb.AnyValue, len(val)) - for i, v := range val { - av[i] = &commonpb.AnyValue{ - Value: &commonpb.AnyValue_IntValue{ - IntValue: v, - }, - } - } - case []float64: - av = make([]*commonpb.AnyValue, len(val)) - for i, v := range val { - av[i] = &commonpb.AnyValue{ - Value: &commonpb.AnyValue_DoubleValue{ - DoubleValue: v, - }, - } - } - case []string: - av = make([]*commonpb.AnyValue, len(val)) - for i, v := range val { - av[i] = &commonpb.AnyValue{ - Value: &commonpb.AnyValue_StringValue{ - StringValue: v, - }, - } - } - } - return av -} diff --git a/exporters/otlp/internal/transform/metric_test.go b/exporters/otlp/internal/transform/metric_test.go deleted file mode 100644 index bc7985dc1..000000000 --- a/exporters/otlp/internal/transform/metric_test.go +++ /dev/null @@ -1,509 +0,0 @@ -// Copyright The OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package transform - -import ( - "context" - "errors" - "fmt" - "testing" - "time" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "go.opentelemetry.io/otel/attribute" - "go.opentelemetry.io/otel/metric" - "go.opentelemetry.io/otel/metric/number" - export "go.opentelemetry.io/otel/sdk/export/metric" - "go.opentelemetry.io/otel/sdk/export/metric/aggregation" - "go.opentelemetry.io/otel/sdk/export/metric/metrictest" - arrAgg "go.opentelemetry.io/otel/sdk/metric/aggregator/exact" - "go.opentelemetry.io/otel/sdk/metric/aggregator/lastvalue" - lvAgg "go.opentelemetry.io/otel/sdk/metric/aggregator/lastvalue" - "go.opentelemetry.io/otel/sdk/metric/aggregator/minmaxsumcount" - "go.opentelemetry.io/otel/sdk/metric/aggregator/sum" - sumAgg "go.opentelemetry.io/otel/sdk/metric/aggregator/sum" - "go.opentelemetry.io/otel/sdk/resource" - commonpb "go.opentelemetry.io/proto/otlp/common/v1" - metricpb "go.opentelemetry.io/proto/otlp/metrics/v1" -) - -var ( - // Timestamps used in this test: - - intervalStart = time.Now() - intervalEnd = intervalStart.Add(time.Hour) -) - -const ( - otelCumulative = metricpb.AggregationTemporality_AGGREGATION_TEMPORALITY_CUMULATIVE - otelDelta = metricpb.AggregationTemporality_AGGREGATION_TEMPORALITY_DELTA -) - -func TestStringKeyValues(t *testing.T) { - tests := []struct { - kvs []attribute.KeyValue - expected []*commonpb.KeyValue - }{ - { - nil, - nil, - }, - { - []attribute.KeyValue{}, - nil, - }, - { - []attribute.KeyValue{ - attribute.Bool("true", true), - attribute.Int64("one", 1), - attribute.Int64("two", 2), - attribute.Float64("three", 3), - attribute.Int("four", 4), - attribute.Int("five", 5), - attribute.Float64("six", 6), - attribute.Int("seven", 7), - attribute.Int("eight", 8), - attribute.String("the", "final word"), - }, - []*commonpb.KeyValue{ - {Key: "eight", Value: &commonpb.AnyValue{Value: &commonpb.AnyValue_IntValue{IntValue: 8}}}, - {Key: "five", Value: &commonpb.AnyValue{Value: &commonpb.AnyValue_IntValue{IntValue: 5}}}, - {Key: "four", Value: &commonpb.AnyValue{Value: &commonpb.AnyValue_IntValue{IntValue: 4}}}, - {Key: "one", Value: &commonpb.AnyValue{Value: &commonpb.AnyValue_IntValue{IntValue: 1}}}, - {Key: "seven", Value: &commonpb.AnyValue{Value: &commonpb.AnyValue_IntValue{IntValue: 7}}}, - {Key: "six", Value: &commonpb.AnyValue{Value: &commonpb.AnyValue_DoubleValue{DoubleValue: 6.0}}}, - {Key: "the", Value: &commonpb.AnyValue{Value: &commonpb.AnyValue_StringValue{StringValue: "final word"}}}, - {Key: "three", Value: &commonpb.AnyValue{Value: &commonpb.AnyValue_DoubleValue{DoubleValue: 3.0}}}, - {Key: "true", Value: &commonpb.AnyValue{Value: &commonpb.AnyValue_BoolValue{BoolValue: true}}}, - {Key: "two", Value: &commonpb.AnyValue{Value: &commonpb.AnyValue_IntValue{IntValue: 2}}}, - }, - }, - } - - for _, test := range tests { - labels := attribute.NewSet(test.kvs...) - assert.Equal(t, test.expected, keyValues(labels.Iter())) - } -} - -func TestMinMaxSumCountValue(t *testing.T) { - mmsc, ckpt := metrictest.Unslice2(minmaxsumcount.New(2, &metric.Descriptor{})) - - assert.NoError(t, mmsc.Update(context.Background(), 1, &metric.Descriptor{})) - assert.NoError(t, mmsc.Update(context.Background(), 10, &metric.Descriptor{})) - - // Prior to checkpointing ErrNoData should be returned. - _, _, _, _, err := minMaxSumCountValues(ckpt.(aggregation.MinMaxSumCount)) - assert.EqualError(t, err, aggregation.ErrNoData.Error()) - - // Checkpoint to set non-zero values - require.NoError(t, mmsc.SynchronizedMove(ckpt, &metric.Descriptor{})) - min, max, sum, count, err := minMaxSumCountValues(ckpt.(aggregation.MinMaxSumCount)) - if assert.NoError(t, err) { - assert.Equal(t, min, number.NewInt64Number(1)) - assert.Equal(t, max, number.NewInt64Number(10)) - assert.Equal(t, sum, number.NewInt64Number(11)) - assert.Equal(t, count, uint64(2)) - } -} - -func TestMinMaxSumCountDatapoints(t *testing.T) { - desc := metric.NewDescriptor("", metric.ValueRecorderInstrumentKind, number.Int64Kind) - labels := attribute.NewSet(attribute.String("one", "1")) - mmsc, ckpt := metrictest.Unslice2(minmaxsumcount.New(2, &desc)) - - assert.NoError(t, mmsc.Update(context.Background(), 1, &desc)) - assert.NoError(t, mmsc.Update(context.Background(), 10, &desc)) - require.NoError(t, mmsc.SynchronizedMove(ckpt, &desc)) - expected := []*metricpb.SummaryDataPoint{ - { - Count: 2, - Sum: 11, - StartTimeUnixNano: uint64(intervalStart.UnixNano()), - TimeUnixNano: uint64(intervalEnd.UnixNano()), - Attributes: []*commonpb.KeyValue{ - { - Key: "one", - Value: &commonpb.AnyValue{Value: &commonpb.AnyValue_StringValue{StringValue: "1"}}, - }, - }, - QuantileValues: []*metricpb.SummaryDataPoint_ValueAtQuantile{ - { - Quantile: 0.0, - Value: 1.0, - }, - { - Quantile: 1.0, - Value: 10.0, - }, - }, - }, - } - record := export.NewRecord(&desc, &labels, nil, ckpt.Aggregation(), intervalStart, intervalEnd) - m, err := minMaxSumCount(record, ckpt.(aggregation.MinMaxSumCount)) - if assert.NoError(t, err) { - assert.Nil(t, m.GetGauge()) - assert.Nil(t, m.GetSum()) - assert.Nil(t, m.GetHistogram()) - assert.Equal(t, expected, m.GetSummary().DataPoints) - assert.Nil(t, m.GetIntGauge()) // nolint - assert.Nil(t, m.GetIntSum()) // nolint - assert.Nil(t, m.GetIntHistogram()) // nolint - } -} - -func TestMinMaxSumCountPropagatesErrors(t *testing.T) { - // ErrNoData should be returned by both the Min and Max values of - // a MinMaxSumCount Aggregator. Use this fact to check the error is - // correctly returned. - mmsc := &minmaxsumcount.New(1, &metric.Descriptor{})[0] - _, _, _, _, err := minMaxSumCountValues(mmsc) - assert.Error(t, err) - assert.Equal(t, aggregation.ErrNoData, err) -} - -func TestSumIntDataPoints(t *testing.T) { - desc := metric.NewDescriptor("", metric.ValueRecorderInstrumentKind, number.Int64Kind) - labels := attribute.NewSet(attribute.String("one", "1")) - s, ckpt := metrictest.Unslice2(sumAgg.New(2)) - assert.NoError(t, s.Update(context.Background(), number.Number(1), &desc)) - require.NoError(t, s.SynchronizedMove(ckpt, &desc)) - record := export.NewRecord(&desc, &labels, nil, ckpt.Aggregation(), intervalStart, intervalEnd) - sum, ok := ckpt.(aggregation.Sum) - require.True(t, ok, "ckpt is not an aggregation.Sum: %T", ckpt) - value, err := sum.Sum() - require.NoError(t, err) - - if m, err := sumPoint(record, value, record.StartTime(), record.EndTime(), export.CumulativeExportKind, true); assert.NoError(t, err) { - assert.Nil(t, m.GetGauge()) - assert.Equal(t, &metricpb.Sum{ - AggregationTemporality: otelCumulative, - IsMonotonic: true, - DataPoints: []*metricpb.NumberDataPoint{{ - StartTimeUnixNano: uint64(intervalStart.UnixNano()), - TimeUnixNano: uint64(intervalEnd.UnixNano()), - Attributes: []*commonpb.KeyValue{ - { - Key: "one", - Value: &commonpb.AnyValue{Value: &commonpb.AnyValue_StringValue{StringValue: "1"}}, - }, - }, - Value: &metricpb.NumberDataPoint_AsInt{ - AsInt: 1, - }, - }}, - }, m.GetSum()) - assert.Nil(t, m.GetHistogram()) - assert.Nil(t, m.GetSummary()) - assert.Nil(t, m.GetIntGauge()) // nolint - assert.Nil(t, m.GetIntSum()) // nolint - assert.Nil(t, m.GetIntHistogram()) // nolint - } -} - -func TestSumFloatDataPoints(t *testing.T) { - desc := metric.NewDescriptor("", metric.ValueRecorderInstrumentKind, number.Float64Kind) - labels := attribute.NewSet(attribute.String("one", "1")) - s, ckpt := metrictest.Unslice2(sumAgg.New(2)) - assert.NoError(t, s.Update(context.Background(), number.NewFloat64Number(1), &desc)) - require.NoError(t, s.SynchronizedMove(ckpt, &desc)) - record := export.NewRecord(&desc, &labels, nil, ckpt.Aggregation(), intervalStart, intervalEnd) - sum, ok := ckpt.(aggregation.Sum) - require.True(t, ok, "ckpt is not an aggregation.Sum: %T", ckpt) - value, err := sum.Sum() - require.NoError(t, err) - - if m, err := sumPoint(record, value, record.StartTime(), record.EndTime(), export.DeltaExportKind, false); assert.NoError(t, err) { - assert.Nil(t, m.GetGauge()) - assert.Equal(t, &metricpb.Sum{ - IsMonotonic: false, - AggregationTemporality: otelDelta, - DataPoints: []*metricpb.NumberDataPoint{{ - Value: &metricpb.NumberDataPoint_AsDouble{ - AsDouble: 1.0, - }, - StartTimeUnixNano: uint64(intervalStart.UnixNano()), - TimeUnixNano: uint64(intervalEnd.UnixNano()), - Attributes: []*commonpb.KeyValue{ - { - Key: "one", - Value: &commonpb.AnyValue{Value: &commonpb.AnyValue_StringValue{StringValue: "1"}}, - }, - }, - }}}, m.GetSum()) - assert.Nil(t, m.GetHistogram()) - assert.Nil(t, m.GetSummary()) - assert.Nil(t, m.GetIntGauge()) // nolint - assert.Nil(t, m.GetIntSum()) // nolint - assert.Nil(t, m.GetIntHistogram()) // nolint - } -} - -func TestLastValueIntDataPoints(t *testing.T) { - desc := metric.NewDescriptor("", metric.ValueRecorderInstrumentKind, number.Int64Kind) - labels := attribute.NewSet(attribute.String("one", "1")) - s, ckpt := metrictest.Unslice2(lvAgg.New(2)) - assert.NoError(t, s.Update(context.Background(), number.Number(100), &desc)) - require.NoError(t, s.SynchronizedMove(ckpt, &desc)) - record := export.NewRecord(&desc, &labels, nil, ckpt.Aggregation(), intervalStart, intervalEnd) - sum, ok := ckpt.(aggregation.LastValue) - require.True(t, ok, "ckpt is not an aggregation.LastValue: %T", ckpt) - value, timestamp, err := sum.LastValue() - require.NoError(t, err) - - if m, err := gaugePoint(record, value, time.Time{}, timestamp); assert.NoError(t, err) { - assert.Equal(t, []*metricpb.NumberDataPoint{{ - StartTimeUnixNano: 0, - TimeUnixNano: uint64(timestamp.UnixNano()), - Attributes: []*commonpb.KeyValue{ - { - Key: "one", - Value: &commonpb.AnyValue{Value: &commonpb.AnyValue_StringValue{StringValue: "1"}}, - }, - }, - Value: &metricpb.NumberDataPoint_AsInt{ - AsInt: 100, - }, - }}, m.GetGauge().DataPoints) - assert.Nil(t, m.GetSum()) - assert.Nil(t, m.GetHistogram()) - assert.Nil(t, m.GetSummary()) - assert.Nil(t, m.GetIntGauge()) // nolint - assert.Nil(t, m.GetIntSum()) // nolint - assert.Nil(t, m.GetIntHistogram()) // nolint - } -} - -func TestExactIntDataPoints(t *testing.T) { - desc := metric.NewDescriptor("", metric.ValueRecorderInstrumentKind, number.Int64Kind) - labels := attribute.NewSet(attribute.String("one", "1")) - e, ckpt := metrictest.Unslice2(arrAgg.New(2)) - assert.NoError(t, e.Update(context.Background(), number.Number(100), &desc)) - require.NoError(t, e.SynchronizedMove(ckpt, &desc)) - record := export.NewRecord(&desc, &labels, nil, ckpt.Aggregation(), intervalStart, intervalEnd) - p, ok := ckpt.(aggregation.Points) - require.True(t, ok, "ckpt is not an aggregation.Points: %T", ckpt) - pts, err := p.Points() - require.NoError(t, err) - - if m, err := gaugeArray(record, pts); assert.NoError(t, err) { - assert.Equal(t, []*metricpb.NumberDataPoint{{ - StartTimeUnixNano: toNanos(intervalStart), - TimeUnixNano: toNanos(intervalEnd), - Attributes: []*commonpb.KeyValue{ - { - Key: "one", - Value: &commonpb.AnyValue{Value: &commonpb.AnyValue_StringValue{StringValue: "1"}}, - }, - }, - Value: &metricpb.NumberDataPoint_AsInt{ - AsInt: 100, - }, - }}, m.GetGauge().DataPoints) - assert.Nil(t, m.GetSum()) - assert.Nil(t, m.GetHistogram()) - assert.Nil(t, m.GetSummary()) - assert.Nil(t, m.GetIntGauge()) // nolint - assert.Nil(t, m.GetIntSum()) // nolint - assert.Nil(t, m.GetIntHistogram()) // nolint - } -} - -func TestExactFloatDataPoints(t *testing.T) { - desc := metric.NewDescriptor("", metric.ValueRecorderInstrumentKind, number.Float64Kind) - labels := attribute.NewSet(attribute.String("one", "1")) - e, ckpt := metrictest.Unslice2(arrAgg.New(2)) - assert.NoError(t, e.Update(context.Background(), number.NewFloat64Number(100), &desc)) - require.NoError(t, e.SynchronizedMove(ckpt, &desc)) - record := export.NewRecord(&desc, &labels, nil, ckpt.Aggregation(), intervalStart, intervalEnd) - p, ok := ckpt.(aggregation.Points) - require.True(t, ok, "ckpt is not an aggregation.Points: %T", ckpt) - pts, err := p.Points() - require.NoError(t, err) - - if m, err := gaugeArray(record, pts); assert.NoError(t, err) { - assert.Equal(t, []*metricpb.NumberDataPoint{{ - Value: &metricpb.NumberDataPoint_AsDouble{ - AsDouble: 100, - }, - StartTimeUnixNano: toNanos(intervalStart), - TimeUnixNano: toNanos(intervalEnd), - Attributes: []*commonpb.KeyValue{ - { - Key: "one", - Value: &commonpb.AnyValue{Value: &commonpb.AnyValue_StringValue{StringValue: "1"}}, - }, - }, - }}, m.GetGauge().DataPoints) - assert.Nil(t, m.GetSum()) - assert.Nil(t, m.GetHistogram()) - assert.Nil(t, m.GetSummary()) - assert.Nil(t, m.GetIntGauge()) // nolint - assert.Nil(t, m.GetIntSum()) // nolint - assert.Nil(t, m.GetIntHistogram()) // nolint - } -} - -func TestSumErrUnknownValueType(t *testing.T) { - desc := metric.NewDescriptor("", metric.ValueRecorderInstrumentKind, number.Kind(-1)) - labels := attribute.NewSet() - s := &sumAgg.New(1)[0] - record := export.NewRecord(&desc, &labels, nil, s, intervalStart, intervalEnd) - value, err := s.Sum() - require.NoError(t, err) - - _, err = sumPoint(record, value, record.StartTime(), record.EndTime(), export.CumulativeExportKind, true) - assert.Error(t, err) - if !errors.Is(err, ErrUnknownValueType) { - t.Errorf("expected ErrUnknownValueType, got %v", err) - } -} - -type testAgg struct { - kind aggregation.Kind - agg aggregation.Aggregation -} - -func (t *testAgg) Kind() aggregation.Kind { - return t.kind -} - -func (t *testAgg) Aggregation() aggregation.Aggregation { - return t.agg -} - -// None of these three are used: - -func (t *testAgg) Update(ctx context.Context, number number.Number, descriptor *metric.Descriptor) error { - return nil -} -func (t *testAgg) SynchronizedMove(destination export.Aggregator, descriptor *metric.Descriptor) error { - return nil -} -func (t *testAgg) Merge(aggregator export.Aggregator, descriptor *metric.Descriptor) error { - return nil -} - -type testErrSum struct { - err error -} - -type testErrLastValue struct { - err error -} - -type testErrMinMaxSumCount struct { - testErrSum -} - -func (te *testErrLastValue) LastValue() (number.Number, time.Time, error) { - return 0, time.Time{}, te.err -} -func (te *testErrLastValue) Kind() aggregation.Kind { - return aggregation.LastValueKind -} - -func (te *testErrSum) Sum() (number.Number, error) { - return 0, te.err -} -func (te *testErrSum) Kind() aggregation.Kind { - return aggregation.SumKind -} - -func (te *testErrMinMaxSumCount) Min() (number.Number, error) { - return 0, te.err -} - -func (te *testErrMinMaxSumCount) Max() (number.Number, error) { - return 0, te.err -} - -func (te *testErrMinMaxSumCount) Count() (uint64, error) { - return 0, te.err -} - -var _ export.Aggregator = &testAgg{} -var _ aggregation.Aggregation = &testAgg{} -var _ aggregation.Sum = &testErrSum{} -var _ aggregation.LastValue = &testErrLastValue{} -var _ aggregation.MinMaxSumCount = &testErrMinMaxSumCount{} - -func TestRecordAggregatorIncompatibleErrors(t *testing.T) { - makeMpb := func(kind aggregation.Kind, agg aggregation.Aggregation) (*metricpb.Metric, error) { - desc := metric.NewDescriptor("things", metric.CounterInstrumentKind, number.Int64Kind) - labels := attribute.NewSet() - res := resource.Empty() - test := &testAgg{ - kind: kind, - agg: agg, - } - return Record(export.CumulativeExportKindSelector(), export.NewRecord(&desc, &labels, res, test, intervalStart, intervalEnd)) - } - - mpb, err := makeMpb(aggregation.SumKind, &lastvalue.New(1)[0]) - - require.Error(t, err) - require.Nil(t, mpb) - require.True(t, errors.Is(err, ErrIncompatibleAgg)) - - mpb, err = makeMpb(aggregation.LastValueKind, &sum.New(1)[0]) - - require.Error(t, err) - require.Nil(t, mpb) - require.True(t, errors.Is(err, ErrIncompatibleAgg)) - - mpb, err = makeMpb(aggregation.MinMaxSumCountKind, &lastvalue.New(1)[0]) - - require.Error(t, err) - require.Nil(t, mpb) - require.True(t, errors.Is(err, ErrIncompatibleAgg)) - - mpb, err = makeMpb(aggregation.ExactKind, &lastvalue.New(1)[0]) - - require.Error(t, err) - require.Nil(t, mpb) - require.True(t, errors.Is(err, ErrIncompatibleAgg)) -} - -func TestRecordAggregatorUnexpectedErrors(t *testing.T) { - makeMpb := func(kind aggregation.Kind, agg aggregation.Aggregation) (*metricpb.Metric, error) { - desc := metric.NewDescriptor("things", metric.CounterInstrumentKind, number.Int64Kind) - labels := attribute.NewSet() - res := resource.Empty() - return Record(export.CumulativeExportKindSelector(), export.NewRecord(&desc, &labels, res, agg, intervalStart, intervalEnd)) - } - - errEx := fmt.Errorf("timeout") - - mpb, err := makeMpb(aggregation.SumKind, &testErrSum{errEx}) - - require.Error(t, err) - require.Nil(t, mpb) - require.True(t, errors.Is(err, errEx)) - - mpb, err = makeMpb(aggregation.LastValueKind, &testErrLastValue{errEx}) - - require.Error(t, err) - require.Nil(t, mpb) - require.True(t, errors.Is(err, errEx)) - - mpb, err = makeMpb(aggregation.MinMaxSumCountKind, &testErrMinMaxSumCount{testErrSum{errEx}}) - - require.Error(t, err) - require.Nil(t, mpb) - require.True(t, errors.Is(err, errEx)) -} diff --git a/exporters/otlp/internal/transform/resource.go b/exporters/otlp/internal/transform/resource.go deleted file mode 100644 index 5be53566b..000000000 --- a/exporters/otlp/internal/transform/resource.go +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright The OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package transform - -import ( - resourcepb "go.opentelemetry.io/proto/otlp/resource/v1" - - "go.opentelemetry.io/otel/sdk/resource" -) - -// Resource transforms a Resource into an OTLP Resource. -func Resource(r *resource.Resource) *resourcepb.Resource { - if r == nil { - return nil - } - return &resourcepb.Resource{Attributes: ResourceAttributes(r)} -} diff --git a/exporters/otlp/internal/transform/resource_test.go b/exporters/otlp/internal/transform/resource_test.go deleted file mode 100644 index 679e4ecb2..000000000 --- a/exporters/otlp/internal/transform/resource_test.go +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright The OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package transform - -import ( - "testing" - - "github.com/stretchr/testify/assert" - - "go.opentelemetry.io/otel/attribute" - "go.opentelemetry.io/otel/sdk/resource" -) - -func TestNilResource(t *testing.T) { - assert.Empty(t, Resource(nil)) -} - -func TestEmptyResource(t *testing.T) { - assert.Empty(t, Resource(&resource.Resource{})) -} - -/* -* This does not include any testing on the ordering of Resource Attributes. -* They are stored as a map internally to the Resource and their order is not -* guaranteed. - */ - -func TestResourceAttributes(t *testing.T) { - attrs := []attribute.KeyValue{attribute.Int("one", 1), attribute.Int("two", 2)} - - got := Resource(resource.NewSchemaless(attrs...)).GetAttributes() - if !assert.Len(t, attrs, 2) { - return - } - assert.ElementsMatch(t, Attributes(attrs), got) -} diff --git a/exporters/otlp/internal/transform/span.go b/exporters/otlp/internal/transform/span.go deleted file mode 100644 index 1431e350a..000000000 --- a/exporters/otlp/internal/transform/span.go +++ /dev/null @@ -1,221 +0,0 @@ -// Copyright The OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package transform - -import ( - "go.opentelemetry.io/otel/attribute" - "go.opentelemetry.io/otel/codes" - tracepb "go.opentelemetry.io/proto/otlp/trace/v1" - - "go.opentelemetry.io/otel/sdk/instrumentation" - tracesdk "go.opentelemetry.io/otel/sdk/trace" - "go.opentelemetry.io/otel/trace" -) - -const ( - maxEventsPerSpan = 128 -) - -// Spans transforms a slice of OpenTelemetry spans into a slice of OTLP -// ResourceSpans. -func Spans(sdl []tracesdk.ReadOnlySpan) []*tracepb.ResourceSpans { - if len(sdl) == 0 { - return nil - } - - rsm := make(map[attribute.Distinct]*tracepb.ResourceSpans) - - type ilsKey struct { - r attribute.Distinct - il instrumentation.Library - } - ilsm := make(map[ilsKey]*tracepb.InstrumentationLibrarySpans) - - var resources int - for _, sd := range sdl { - if sd == nil { - continue - } - - rKey := sd.Resource().Equivalent() - iKey := ilsKey{ - r: rKey, - il: sd.InstrumentationLibrary(), - } - ils, iOk := ilsm[iKey] - if !iOk { - // Either the resource or instrumentation library were unknown. - ils = &tracepb.InstrumentationLibrarySpans{ - InstrumentationLibrary: instrumentationLibrary(sd.InstrumentationLibrary()), - Spans: []*tracepb.Span{}, - } - // TODO: set schema_url field of ils when it is available in the proto. - _ = sd.InstrumentationLibrary().SchemaURL - } - ils.Spans = append(ils.Spans, span(sd)) - ilsm[iKey] = ils - - rs, rOk := rsm[rKey] - if !rOk { - resources++ - // The resource was unknown. - rs = &tracepb.ResourceSpans{ - Resource: Resource(sd.Resource()), - InstrumentationLibrarySpans: []*tracepb.InstrumentationLibrarySpans{ils}, - } - // TODO: populate ResourceSpans.SchemaURL when the field is added to the Protobuf message. - rsm[rKey] = rs - continue - } - - // The resource has been seen before. Check if the instrumentation - // library lookup was unknown because if so we need to add it to the - // ResourceSpans. Otherwise, the instrumentation library has already - // been seen and the append we did above will be included it in the - // InstrumentationLibrarySpans reference. - if !iOk { - rs.InstrumentationLibrarySpans = append(rs.InstrumentationLibrarySpans, ils) - } - } - - // Transform the categorized map into a slice - rss := make([]*tracepb.ResourceSpans, 0, resources) - for _, rs := range rsm { - rss = append(rss, rs) - } - return rss -} - -// span transforms a Span into an OTLP span. -func span(sd tracesdk.ReadOnlySpan) *tracepb.Span { - if sd == nil { - return nil - } - - tid := sd.SpanContext().TraceID() - sid := sd.SpanContext().SpanID() - - s := &tracepb.Span{ - TraceId: tid[:], - SpanId: sid[:], - TraceState: sd.SpanContext().TraceState().String(), - Status: status(sd.Status().Code, sd.Status().Description), - StartTimeUnixNano: uint64(sd.StartTime().UnixNano()), - EndTimeUnixNano: uint64(sd.EndTime().UnixNano()), - Links: links(sd.Links()), - Kind: spanKind(sd.SpanKind()), - Name: sd.Name(), - Attributes: Attributes(sd.Attributes()), - Events: spanEvents(sd.Events()), - DroppedAttributesCount: uint32(sd.DroppedAttributes()), - DroppedEventsCount: uint32(sd.DroppedEvents()), - DroppedLinksCount: uint32(sd.DroppedLinks()), - } - - if psid := sd.Parent().SpanID(); psid.IsValid() { - s.ParentSpanId = psid[:] - } - - return s -} - -// status transform a span code and message into an OTLP span status. -func status(status codes.Code, message string) *tracepb.Status { - var c tracepb.Status_StatusCode - switch status { - case codes.Error: - c = tracepb.Status_STATUS_CODE_ERROR - default: - c = tracepb.Status_STATUS_CODE_OK - } - return &tracepb.Status{ - Code: c, - Message: message, - } -} - -// links transforms span Links to OTLP span links. -func links(links []trace.Link) []*tracepb.Span_Link { - if len(links) == 0 { - return nil - } - - sl := make([]*tracepb.Span_Link, 0, len(links)) - for _, otLink := range links { - // This redefinition is necessary to prevent otLink.*ID[:] copies - // being reused -- in short we need a new otLink per iteration. - otLink := otLink - - tid := otLink.SpanContext.TraceID() - sid := otLink.SpanContext.SpanID() - - sl = append(sl, &tracepb.Span_Link{ - TraceId: tid[:], - SpanId: sid[:], - Attributes: Attributes(otLink.Attributes), - }) - } - return sl -} - -// spanEvents transforms span Events to an OTLP span events. -func spanEvents(es []tracesdk.Event) []*tracepb.Span_Event { - if len(es) == 0 { - return nil - } - - evCount := len(es) - if evCount > maxEventsPerSpan { - evCount = maxEventsPerSpan - } - events := make([]*tracepb.Span_Event, 0, evCount) - nEvents := 0 - - // Transform message events - for _, e := range es { - if nEvents >= maxEventsPerSpan { - break - } - nEvents++ - events = append(events, - &tracepb.Span_Event{ - Name: e.Name, - TimeUnixNano: uint64(e.Time.UnixNano()), - Attributes: Attributes(e.Attributes), - // TODO (rghetia) : Add Drop Counts when supported. - }, - ) - } - - return events -} - -// spanKind transforms a SpanKind to an OTLP span kind. -func spanKind(kind trace.SpanKind) tracepb.Span_SpanKind { - switch kind { - case trace.SpanKindInternal: - return tracepb.Span_SPAN_KIND_INTERNAL - case trace.SpanKindClient: - return tracepb.Span_SPAN_KIND_CLIENT - case trace.SpanKindServer: - return tracepb.Span_SPAN_KIND_SERVER - case trace.SpanKindProducer: - return tracepb.Span_SPAN_KIND_PRODUCER - case trace.SpanKindConsumer: - return tracepb.Span_SPAN_KIND_CONSUMER - default: - return tracepb.Span_SPAN_KIND_UNSPECIFIED - } -} diff --git a/exporters/otlp/internal/transform/span_test.go b/exporters/otlp/internal/transform/span_test.go deleted file mode 100644 index dd4311785..000000000 --- a/exporters/otlp/internal/transform/span_test.go +++ /dev/null @@ -1,322 +0,0 @@ -// Copyright The OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package transform - -import ( - "strconv" - "testing" - "time" - - "github.com/google/go-cmp/cmp" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "google.golang.org/protobuf/proto" - - "go.opentelemetry.io/otel/attribute" - "go.opentelemetry.io/otel/oteltest" - "go.opentelemetry.io/otel/trace" - tracepb "go.opentelemetry.io/proto/otlp/trace/v1" - - "go.opentelemetry.io/otel/codes" - "go.opentelemetry.io/otel/sdk/instrumentation" - "go.opentelemetry.io/otel/sdk/resource" - tracesdk "go.opentelemetry.io/otel/sdk/trace" - "go.opentelemetry.io/otel/sdk/trace/tracetest" -) - -func TestSpanKind(t *testing.T) { - for _, test := range []struct { - kind trace.SpanKind - expected tracepb.Span_SpanKind - }{ - { - trace.SpanKindInternal, - tracepb.Span_SPAN_KIND_INTERNAL, - }, - { - trace.SpanKindClient, - tracepb.Span_SPAN_KIND_CLIENT, - }, - { - trace.SpanKindServer, - tracepb.Span_SPAN_KIND_SERVER, - }, - { - trace.SpanKindProducer, - tracepb.Span_SPAN_KIND_PRODUCER, - }, - { - trace.SpanKindConsumer, - tracepb.Span_SPAN_KIND_CONSUMER, - }, - { - trace.SpanKind(-1), - tracepb.Span_SPAN_KIND_UNSPECIFIED, - }, - } { - assert.Equal(t, test.expected, spanKind(test.kind)) - } -} - -func TestNilSpanEvent(t *testing.T) { - assert.Nil(t, spanEvents(nil)) -} - -func TestEmptySpanEvent(t *testing.T) { - assert.Nil(t, spanEvents([]tracesdk.Event{})) -} - -func TestSpanEvent(t *testing.T) { - attrs := []attribute.KeyValue{attribute.Int("one", 1), attribute.Int("two", 2)} - eventTime := time.Date(2020, 5, 20, 0, 0, 0, 0, time.UTC) - got := spanEvents([]tracesdk.Event{ - { - Name: "test 1", - Attributes: []attribute.KeyValue{}, - Time: eventTime, - }, - { - Name: "test 2", - Attributes: attrs, - Time: eventTime, - }, - }) - if !assert.Len(t, got, 2) { - return - } - eventTimestamp := uint64(1589932800 * 1e9) - assert.Equal(t, &tracepb.Span_Event{Name: "test 1", Attributes: nil, TimeUnixNano: eventTimestamp}, got[0]) - // Do not test Attributes directly, just that the return value goes to the correct field. - assert.Equal(t, &tracepb.Span_Event{Name: "test 2", Attributes: Attributes(attrs), TimeUnixNano: eventTimestamp}, got[1]) -} - -func TestExcessiveSpanEvents(t *testing.T) { - e := make([]tracesdk.Event, maxEventsPerSpan+1) - for i := 0; i < maxEventsPerSpan+1; i++ { - e[i] = tracesdk.Event{Name: strconv.Itoa(i)} - } - assert.Len(t, e, maxEventsPerSpan+1) - got := spanEvents(e) - assert.Len(t, got, maxEventsPerSpan) - // Ensure the drop order. - assert.Equal(t, strconv.Itoa(maxEventsPerSpan-1), got[len(got)-1].Name) -} - -func TestNilLinks(t *testing.T) { - assert.Nil(t, links(nil)) -} - -func TestEmptyLinks(t *testing.T) { - assert.Nil(t, links([]trace.Link{})) -} - -func TestLinks(t *testing.T) { - attrs := []attribute.KeyValue{attribute.Int("one", 1), attribute.Int("two", 2)} - l := []trace.Link{ - {}, - { - SpanContext: trace.SpanContext{}, - Attributes: attrs, - }, - } - got := links(l) - - // Make sure we get the same number back first. - if !assert.Len(t, got, 2) { - return - } - - // Empty should be empty. - expected := &tracepb.Span_Link{ - TraceId: []uint8{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, - SpanId: []uint8{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, - } - assert.Equal(t, expected, got[0]) - - // Do not test Attributes directly, just that the return value goes to the correct field. - expected.Attributes = Attributes(attrs) - assert.Equal(t, expected, got[1]) - - // Changes to our links should not change the produced links. - l[1].SpanContext = l[1].SpanContext.WithTraceID(trace.TraceID{}) - assert.Equal(t, expected, got[1]) -} - -func TestStatus(t *testing.T) { - for _, test := range []struct { - code codes.Code - message string - otlpStatus tracepb.Status_StatusCode - }{ - { - codes.Ok, - "test Ok", - tracepb.Status_STATUS_CODE_OK, - }, - { - codes.Unset, - "test Unset", - tracepb.Status_STATUS_CODE_OK, - }, - { - codes.Error, - "test Error", - tracepb.Status_STATUS_CODE_ERROR, - }, - } { - expected := &tracepb.Status{Code: test.otlpStatus, Message: test.message} - assert.Equal(t, expected, status(test.code, test.message)) - } - -} - -func TestNilSpan(t *testing.T) { - assert.Nil(t, span(nil)) -} - -func TestNilSpanData(t *testing.T) { - assert.Nil(t, Spans(nil)) -} - -func TestEmptySpanData(t *testing.T) { - assert.Nil(t, Spans(nil)) -} - -func TestSpanData(t *testing.T) { - // Full test of span data transform. - - // March 31, 2020 5:01:26 1234nanos (UTC) - startTime := time.Unix(1585674086, 1234) - endTime := startTime.Add(10 * time.Second) - traceState, _ := oteltest.TraceStateFromKeyValues(attribute.String("key1", "val1"), attribute.String("key2", "val2")) - spanData := tracetest.SpanStub{ - SpanContext: trace.NewSpanContext(trace.SpanContextConfig{ - TraceID: trace.TraceID{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F}, - SpanID: trace.SpanID{0xFF, 0xFE, 0xFD, 0xFC, 0xFB, 0xFA, 0xF9, 0xF8}, - TraceState: traceState, - }), - Parent: trace.NewSpanContext(trace.SpanContextConfig{ - TraceID: trace.TraceID{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F}, - SpanID: trace.SpanID{0xEF, 0xEE, 0xED, 0xEC, 0xEB, 0xEA, 0xE9, 0xE8}, - TraceState: traceState, - Remote: true, - }), - SpanKind: trace.SpanKindServer, - Name: "span data to span data", - StartTime: startTime, - EndTime: endTime, - Events: []tracesdk.Event{ - {Time: startTime, - Attributes: []attribute.KeyValue{ - attribute.Int64("CompressedByteSize", 512), - }, - }, - {Time: endTime, - Attributes: []attribute.KeyValue{ - attribute.String("EventType", "Recv"), - }, - }, - }, - Links: []trace.Link{ - { - SpanContext: trace.NewSpanContext(trace.SpanContextConfig{ - TraceID: trace.TraceID{0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF}, - SpanID: trace.SpanID{0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7}, - TraceFlags: 0, - }), - Attributes: []attribute.KeyValue{ - attribute.String("LinkType", "Parent"), - }, - }, - { - SpanContext: trace.NewSpanContext(trace.SpanContextConfig{ - TraceID: trace.TraceID{0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF}, - SpanID: trace.SpanID{0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7}, - TraceFlags: 0, - }), - Attributes: []attribute.KeyValue{ - attribute.String("LinkType", "Child"), - }, - }, - }, - Status: tracesdk.Status{ - Code: codes.Error, - Description: "utterly unrecognized", - }, - Attributes: []attribute.KeyValue{ - attribute.Int64("timeout_ns", 12e9), - }, - DroppedAttributes: 1, - DroppedEvents: 2, - DroppedLinks: 3, - Resource: resource.NewSchemaless(attribute.String("rk1", "rv1"), attribute.Int64("rk2", 5)), - InstrumentationLibrary: instrumentation.Library{ - Name: "go.opentelemetry.io/test/otel", - Version: "v0.0.1", - SchemaURL: "https://opentelemetry.io/schemas/1.2.0", - }, - } - - // Not checking resource as the underlying map of our Resource makes - // ordering impossible to guarantee on the output. The Resource - // transform function has unit tests that should suffice. - expectedSpan := &tracepb.Span{ - TraceId: []byte{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F}, - SpanId: []byte{0xFF, 0xFE, 0xFD, 0xFC, 0xFB, 0xFA, 0xF9, 0xF8}, - ParentSpanId: []byte{0xEF, 0xEE, 0xED, 0xEC, 0xEB, 0xEA, 0xE9, 0xE8}, - TraceState: "key1=val1,key2=val2", - Name: spanData.Name, - Kind: tracepb.Span_SPAN_KIND_SERVER, - StartTimeUnixNano: uint64(startTime.UnixNano()), - EndTimeUnixNano: uint64(endTime.UnixNano()), - Status: status(spanData.Status.Code, spanData.Status.Description), - Events: spanEvents(spanData.Events), - Links: links(spanData.Links), - Attributes: Attributes(spanData.Attributes), - DroppedAttributesCount: 1, - DroppedEventsCount: 2, - DroppedLinksCount: 3, - } - - got := Spans(tracetest.SpanStubs{spanData}.Snapshots()) - require.Len(t, got, 1) - - assert.Equal(t, got[0].GetResource(), Resource(spanData.Resource)) - ilSpans := got[0].GetInstrumentationLibrarySpans() - require.Len(t, ilSpans, 1) - // TODO: Add SchemaURL field checking once the field is added to the proto. - assert.Equal(t, ilSpans[0].GetInstrumentationLibrary(), instrumentationLibrary(spanData.InstrumentationLibrary)) - require.Len(t, ilSpans[0].Spans, 1) - actualSpan := ilSpans[0].Spans[0] - - if diff := cmp.Diff(expectedSpan, actualSpan, cmp.Comparer(proto.Equal)); diff != "" { - t.Fatalf("transformed span differs %v\n", diff) - } -} - -// Empty parent span ID should be treated as root span. -func TestRootSpanData(t *testing.T) { - sd := Spans(tracetest.SpanStubs{{}}.Snapshots()) - require.Len(t, sd, 1) - rs := sd[0] - got := rs.GetInstrumentationLibrarySpans()[0].GetSpans()[0].GetParentSpanId() - - // Empty means root span. - assert.Nil(t, got, "incorrect transform of root parent span ID") -} - -func TestSpanDataNilResource(t *testing.T) { - assert.NotPanics(t, func() { Spans(tracetest.SpanStubs{{}}.Snapshots()) }) -} diff --git a/exporters/otlp/options.go b/exporters/otlp/options.go deleted file mode 100644 index bcd0ddc3d..000000000 --- a/exporters/otlp/options.go +++ /dev/null @@ -1,86 +0,0 @@ -// Copyright The OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package otlp // import "go.opentelemetry.io/otel/exporters/otlp" - -import ( - metricsdk "go.opentelemetry.io/otel/sdk/export/metric" -) - -const ( - // DefaultCollectorPort is the port the Exporter will attempt connect to - // if no collector port is provided. - DefaultCollectorPort uint16 = 4317 - // DefaultCollectorHost is the host address the Exporter will attempt - // connect to if no collector address is provided. - DefaultCollectorHost string = "localhost" -) - -// ExporterOption are setting options passed to an Exporter on creation. -type ExporterOption interface { - apply(*config) -} - -type exporterOptionFunc func(*config) - -func (fn exporterOptionFunc) apply(cfg *config) { - fn(cfg) -} - -type config struct { - exportKindSelector metricsdk.ExportKindSelector -} - -// WithMetricExportKindSelector defines the ExportKindSelector used -// for selecting AggregationTemporality (i.e., Cumulative vs. Delta -// aggregation). If not specified otherwise, exporter will use a -// cumulative export kind selector. -func WithMetricExportKindSelector(selector metricsdk.ExportKindSelector) ExporterOption { - return exporterOptionFunc(func(cfg *config) { - cfg.exportKindSelector = selector - }) -} - -// SplitDriverOption provides options for setting up a split driver. -type SplitDriverOption interface { - apply(*splitDriver) -} - -// WithMetricDriver allows one to set the driver used for metrics -// in a SplitDriver. -func WithMetricDriver(dr ProtocolDriver) SplitDriverOption { - return metricDriverOption{dr} -} - -type metricDriverOption struct { - driver ProtocolDriver -} - -func (o metricDriverOption) apply(s *splitDriver) { - s.metric = o.driver -} - -// WithTraceDriver allows one to set the driver used for traces -// in a SplitDriver. -func WithTraceDriver(dr ProtocolDriver) SplitDriverOption { - return traceDriverOption{dr} -} - -type traceDriverOption struct { - driver ProtocolDriver -} - -func (o traceDriverOption) apply(s *splitDriver) { - s.trace = o.driver -} diff --git a/exporters/otlp/optiontypes.go b/exporters/otlp/optiontypes.go deleted file mode 100644 index 682625f45..000000000 --- a/exporters/otlp/optiontypes.go +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright The OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package otlp // import "go.opentelemetry.io/otel/exporters/otlp" - -// Compression describes the compression used for payloads sent to the -// collector. -type Compression int - -const ( - // NoCompression tells the driver to send payloads without - // compression. - NoCompression Compression = iota - // GzipCompression tells the driver to send payloads after - // compressing them with gzip. - GzipCompression -) - -// Marshaler describes the kind of message format sent to the collector -type Marshaler int - -const ( - // MarshalProto tells the driver to send using the protobuf binary format. - MarshalProto Marshaler = iota - // MarshalJSON tells the driver to send using json format. - MarshalJSON -) diff --git a/exporters/otlp/otlp.go b/exporters/otlp/otlp.go deleted file mode 100644 index f6c3ce72e..000000000 --- a/exporters/otlp/otlp.go +++ /dev/null @@ -1,131 +0,0 @@ -// Copyright The OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package otlp // import "go.opentelemetry.io/otel/exporters/otlp" - -import ( - "context" - "errors" - "sync" - - "go.opentelemetry.io/otel/metric" - metricsdk "go.opentelemetry.io/otel/sdk/export/metric" - "go.opentelemetry.io/otel/sdk/export/metric/aggregation" - - tracesdk "go.opentelemetry.io/otel/sdk/trace" -) - -// Exporter is an OpenTelemetry exporter. It exports both traces and metrics -// from OpenTelemetry instrumented to code using OpenTelemetry protocol -// buffers to a configurable receiver. -type Exporter struct { - cfg config - driver ProtocolDriver - - mu sync.RWMutex - started bool - - startOnce sync.Once - stopOnce sync.Once -} - -var _ tracesdk.SpanExporter = (*Exporter)(nil) -var _ metricsdk.Exporter = (*Exporter)(nil) - -// New constructs a new Exporter and starts it. -func New(ctx context.Context, driver ProtocolDriver, opts ...ExporterOption) (*Exporter, error) { - exp := NewUnstarted(driver, opts...) - if err := exp.Start(ctx); err != nil { - return nil, err - } - return exp, nil -} - -// NewUnstarted constructs a new Exporter and does not start it. -func NewUnstarted(driver ProtocolDriver, opts ...ExporterOption) *Exporter { - cfg := config{ - // Note: the default ExportKindSelector is specified - // as Cumulative: - // https://github.com/open-telemetry/opentelemetry-specification/issues/731 - exportKindSelector: metricsdk.CumulativeExportKindSelector(), - } - for _, opt := range opts { - opt.apply(&cfg) - } - return &Exporter{ - cfg: cfg, - driver: driver, - } -} - -var ( - errAlreadyStarted = errors.New("already started") -) - -// Start establishes connections to the OpenTelemetry collector. Starting an -// already started exporter returns an error. -func (e *Exporter) Start(ctx context.Context) error { - var err = errAlreadyStarted - e.startOnce.Do(func() { - e.mu.Lock() - e.started = true - e.mu.Unlock() - err = e.driver.Start(ctx) - }) - - return err -} - -// Shutdown closes all connections and releases resources currently being used -// by the exporter. If the exporter is not started this does nothing. A shut -// down exporter can't be started again. Shutting down an already shut down -// exporter does nothing. -func (e *Exporter) Shutdown(ctx context.Context) error { - e.mu.RLock() - started := e.started - e.mu.RUnlock() - - if !started { - return nil - } - - var err error - - e.stopOnce.Do(func() { - err = e.driver.Stop(ctx) - e.mu.Lock() - e.started = false - e.mu.Unlock() - }) - - return err -} - -// Export transforms and batches metric Records into OTLP Metrics and -// transmits them to the configured collector. -func (e *Exporter) Export(parent context.Context, cps metricsdk.CheckpointSet) error { - return e.driver.ExportMetrics(parent, cps, e.cfg.exportKindSelector) -} - -// ExportKindFor reports back to the OpenTelemetry SDK sending this Exporter -// metric telemetry that it needs to be provided in a configured format. -func (e *Exporter) ExportKindFor(desc *metric.Descriptor, kind aggregation.Kind) metricsdk.ExportKind { - return e.cfg.exportKindSelector.ExportKindFor(desc, kind) -} - -// ExportSpans transforms and batches OpenTelemetry spans into OTLP Trace and -// transmits them to the configured collector. -func (e *Exporter) ExportSpans(ctx context.Context, spans []tracesdk.ReadOnlySpan) error { - return e.driver.ExportTraces(ctx, spans) -} diff --git a/exporters/otlp/otlp_metric_test.go b/exporters/otlp/otlp_metric_test.go deleted file mode 100644 index ab5abf985..000000000 --- a/exporters/otlp/otlp_metric_test.go +++ /dev/null @@ -1,869 +0,0 @@ -// Copyright The OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package otlp_test - -import ( - "context" - "sync" - "testing" - "time" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "go.opentelemetry.io/otel/attribute" - "go.opentelemetry.io/otel/exporters/otlp" - "go.opentelemetry.io/otel/metric" - "go.opentelemetry.io/otel/metric/number" - metricsdk "go.opentelemetry.io/otel/sdk/export/metric" - "go.opentelemetry.io/otel/sdk/export/metric/aggregation" - "go.opentelemetry.io/otel/sdk/export/metric/metrictest" - "go.opentelemetry.io/otel/sdk/metric/aggregator/histogram" - "go.opentelemetry.io/otel/sdk/metric/aggregator/sum" - "go.opentelemetry.io/otel/sdk/resource" - commonpb "go.opentelemetry.io/proto/otlp/common/v1" - metricpb "go.opentelemetry.io/proto/otlp/metrics/v1" - resourcepb "go.opentelemetry.io/proto/otlp/resource/v1" -) - -var ( - // Timestamps used in this test: - - intervalStart = time.Now() - intervalEnd = intervalStart.Add(time.Hour) -) - -func startTime() uint64 { - return uint64(intervalStart.UnixNano()) -} - -func pointTime() uint64 { - return uint64(intervalEnd.UnixNano()) -} - -type checkpointSet struct { - sync.RWMutex - records []metricsdk.Record -} - -func (m *checkpointSet) ForEach(_ metricsdk.ExportKindSelector, fn func(metricsdk.Record) error) error { - for _, r := range m.records { - if err := fn(r); err != nil && err != aggregation.ErrNoData { - return err - } - } - return nil -} - -type record struct { - name string - iKind metric.InstrumentKind - nKind number.Kind - resource *resource.Resource - opts []metric.InstrumentOption - labels []attribute.KeyValue -} - -var ( - baseKeyValues = []attribute.KeyValue{attribute.String("host", "test.com")} - cpuKey = attribute.Key("CPU") - - testInstA = resource.NewSchemaless(attribute.String("instance", "tester-a")) - testInstB = resource.NewSchemaless(attribute.String("instance", "tester-b")) - - testHistogramBoundaries = []float64{2.0, 4.0, 8.0} - - cpu1Labels = []*commonpb.KeyValue{ - { - Key: "CPU", - Value: &commonpb.AnyValue{ - Value: &commonpb.AnyValue_IntValue{ - IntValue: 1, - }, - }, - }, - { - Key: "host", - Value: &commonpb.AnyValue{ - Value: &commonpb.AnyValue_StringValue{ - StringValue: "test.com", - }, - }, - }, - } - cpu2Labels = []*commonpb.KeyValue{ - { - Key: "CPU", - Value: &commonpb.AnyValue{ - Value: &commonpb.AnyValue_IntValue{ - IntValue: 2, - }, - }, - }, - { - Key: "host", - Value: &commonpb.AnyValue{ - Value: &commonpb.AnyValue_StringValue{ - StringValue: "test.com", - }, - }, - }, - } - - testerAResource = &resourcepb.Resource{ - Attributes: []*commonpb.KeyValue{ - { - Key: "instance", - Value: &commonpb.AnyValue{ - Value: &commonpb.AnyValue_StringValue{ - StringValue: "tester-a", - }, - }, - }, - }, - } - testerBResource = &resourcepb.Resource{ - Attributes: []*commonpb.KeyValue{ - { - Key: "instance", - Value: &commonpb.AnyValue{ - Value: &commonpb.AnyValue_StringValue{ - StringValue: "tester-b", - }, - }, - }, - }, - } -) - -func TestNoGroupingExport(t *testing.T) { - runMetricExportTests( - t, - nil, - []record{ - { - "int64-count", - metric.CounterInstrumentKind, - number.Int64Kind, - nil, - nil, - append(baseKeyValues, cpuKey.Int(1)), - }, - { - "int64-count", - metric.CounterInstrumentKind, - number.Int64Kind, - nil, - nil, - append(baseKeyValues, cpuKey.Int(2)), - }, - }, - []*metricpb.ResourceMetrics{ - { - Resource: nil, - InstrumentationLibraryMetrics: []*metricpb.InstrumentationLibraryMetrics{ - { - Metrics: []*metricpb.Metric{ - { - Name: "int64-count", - Data: &metricpb.Metric_Sum{ - Sum: &metricpb.Sum{ - IsMonotonic: true, - AggregationTemporality: metricpb.AggregationTemporality_AGGREGATION_TEMPORALITY_CUMULATIVE, - DataPoints: []*metricpb.NumberDataPoint{ - { - Value: &metricpb.NumberDataPoint_AsInt{AsInt: 11}, - Attributes: cpu1Labels, - StartTimeUnixNano: startTime(), - TimeUnixNano: pointTime(), - }, - { - Value: &metricpb.NumberDataPoint_AsInt{AsInt: 11}, - Attributes: cpu2Labels, - StartTimeUnixNano: startTime(), - TimeUnixNano: pointTime(), - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - ) -} - -func TestValuerecorderMetricGroupingExport(t *testing.T) { - r := record{ - "valuerecorder", - metric.ValueRecorderInstrumentKind, - number.Int64Kind, - nil, - nil, - append(baseKeyValues, cpuKey.Int(1)), - } - expected := []*metricpb.ResourceMetrics{ - { - Resource: nil, - InstrumentationLibraryMetrics: []*metricpb.InstrumentationLibraryMetrics{ - { - Metrics: []*metricpb.Metric{ - { - Name: "valuerecorder", - Data: &metricpb.Metric_Histogram{ - Histogram: &metricpb.Histogram{ - AggregationTemporality: metricpb.AggregationTemporality_AGGREGATION_TEMPORALITY_CUMULATIVE, - DataPoints: []*metricpb.HistogramDataPoint{ - { - Attributes: cpu1Labels, - StartTimeUnixNano: startTime(), - TimeUnixNano: pointTime(), - Count: 2, - Sum: 11, - ExplicitBounds: testHistogramBoundaries, - BucketCounts: []uint64{1, 0, 0, 1}, - }, - { - Attributes: cpu1Labels, - Count: 2, - Sum: 11, - ExplicitBounds: testHistogramBoundaries, - BucketCounts: []uint64{1, 0, 0, 1}, - StartTimeUnixNano: startTime(), - TimeUnixNano: pointTime(), - }, - }, - }, - }, - }, - }, - }, - }, - }, - } - runMetricExportTests(t, nil, []record{r, r}, expected) -} - -func TestCountInt64MetricGroupingExport(t *testing.T) { - r := record{ - "int64-count", - metric.CounterInstrumentKind, - number.Int64Kind, - nil, - nil, - append(baseKeyValues, cpuKey.Int(1)), - } - runMetricExportTests( - t, - nil, - []record{r, r}, - []*metricpb.ResourceMetrics{ - { - Resource: nil, - InstrumentationLibraryMetrics: []*metricpb.InstrumentationLibraryMetrics{ - { - Metrics: []*metricpb.Metric{ - { - Name: "int64-count", - Data: &metricpb.Metric_Sum{ - Sum: &metricpb.Sum{ - IsMonotonic: true, - AggregationTemporality: metricpb.AggregationTemporality_AGGREGATION_TEMPORALITY_CUMULATIVE, - DataPoints: []*metricpb.NumberDataPoint{ - { - Value: &metricpb.NumberDataPoint_AsInt{AsInt: 11}, - Attributes: cpu1Labels, - StartTimeUnixNano: startTime(), - TimeUnixNano: pointTime(), - }, - { - Value: &metricpb.NumberDataPoint_AsInt{AsInt: 11}, - Attributes: cpu1Labels, - StartTimeUnixNano: startTime(), - TimeUnixNano: pointTime(), - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - ) -} - -func TestCountFloat64MetricGroupingExport(t *testing.T) { - r := record{ - "float64-count", - metric.CounterInstrumentKind, - number.Float64Kind, - nil, - nil, - append(baseKeyValues, cpuKey.Int(1)), - } - runMetricExportTests( - t, - nil, - []record{r, r}, - []*metricpb.ResourceMetrics{ - { - Resource: nil, - InstrumentationLibraryMetrics: []*metricpb.InstrumentationLibraryMetrics{ - { - Metrics: []*metricpb.Metric{ - { - Name: "float64-count", - Data: &metricpb.Metric_Sum{ - Sum: &metricpb.Sum{ - IsMonotonic: true, - AggregationTemporality: metricpb.AggregationTemporality_AGGREGATION_TEMPORALITY_CUMULATIVE, - DataPoints: []*metricpb.NumberDataPoint{ - { - Value: &metricpb.NumberDataPoint_AsDouble{AsDouble: 11.0}, - Attributes: cpu1Labels, - StartTimeUnixNano: startTime(), - TimeUnixNano: pointTime(), - }, - { - Value: &metricpb.NumberDataPoint_AsDouble{AsDouble: 11.0}, - Attributes: cpu1Labels, - StartTimeUnixNano: startTime(), - TimeUnixNano: pointTime(), - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - ) -} - -func TestResourceMetricGroupingExport(t *testing.T) { - runMetricExportTests( - t, - nil, - []record{ - { - "int64-count", - metric.CounterInstrumentKind, - number.Int64Kind, - testInstA, - nil, - append(baseKeyValues, cpuKey.Int(1)), - }, - { - "int64-count", - metric.CounterInstrumentKind, - number.Int64Kind, - testInstA, - nil, - append(baseKeyValues, cpuKey.Int(1)), - }, - { - "int64-count", - metric.CounterInstrumentKind, - number.Int64Kind, - testInstA, - nil, - append(baseKeyValues, cpuKey.Int(2)), - }, - { - "int64-count", - metric.CounterInstrumentKind, - number.Int64Kind, - testInstB, - nil, - append(baseKeyValues, cpuKey.Int(1)), - }, - }, - []*metricpb.ResourceMetrics{ - { - Resource: testerAResource, - InstrumentationLibraryMetrics: []*metricpb.InstrumentationLibraryMetrics{ - { - Metrics: []*metricpb.Metric{ - { - Name: "int64-count", - Data: &metricpb.Metric_Sum{ - Sum: &metricpb.Sum{ - IsMonotonic: true, - AggregationTemporality: metricpb.AggregationTemporality_AGGREGATION_TEMPORALITY_CUMULATIVE, - DataPoints: []*metricpb.NumberDataPoint{ - { - Value: &metricpb.NumberDataPoint_AsInt{AsInt: 11}, - Attributes: cpu1Labels, - StartTimeUnixNano: startTime(), - TimeUnixNano: pointTime(), - }, - { - Value: &metricpb.NumberDataPoint_AsInt{AsInt: 11}, - Attributes: cpu1Labels, - StartTimeUnixNano: startTime(), - TimeUnixNano: pointTime(), - }, - { - Value: &metricpb.NumberDataPoint_AsInt{AsInt: 11}, - Attributes: cpu2Labels, - StartTimeUnixNano: startTime(), - TimeUnixNano: pointTime(), - }, - }, - }, - }, - }, - }, - }, - }, - }, - { - Resource: testerBResource, - InstrumentationLibraryMetrics: []*metricpb.InstrumentationLibraryMetrics{ - { - Metrics: []*metricpb.Metric{ - { - Name: "int64-count", - Data: &metricpb.Metric_Sum{ - Sum: &metricpb.Sum{ - IsMonotonic: true, - AggregationTemporality: metricpb.AggregationTemporality_AGGREGATION_TEMPORALITY_CUMULATIVE, - DataPoints: []*metricpb.NumberDataPoint{ - { - Value: &metricpb.NumberDataPoint_AsInt{AsInt: 11}, - Attributes: cpu1Labels, - StartTimeUnixNano: startTime(), - TimeUnixNano: pointTime(), - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - ) -} - -func TestResourceInstLibMetricGroupingExport(t *testing.T) { - countingLib1 := []metric.InstrumentOption{ - metric.WithInstrumentationName("counting-lib"), - metric.WithInstrumentationVersion("v1"), - } - countingLib2 := []metric.InstrumentOption{ - metric.WithInstrumentationName("counting-lib"), - metric.WithInstrumentationVersion("v2"), - } - summingLib := []metric.InstrumentOption{ - metric.WithInstrumentationName("summing-lib"), - } - runMetricExportTests( - t, - nil, - []record{ - { - "int64-count", - metric.CounterInstrumentKind, - number.Int64Kind, - testInstA, - countingLib1, - append(baseKeyValues, cpuKey.Int(1)), - }, - { - "int64-count", - metric.CounterInstrumentKind, - number.Int64Kind, - testInstA, - countingLib2, - append(baseKeyValues, cpuKey.Int(1)), - }, - { - "int64-count", - metric.CounterInstrumentKind, - number.Int64Kind, - testInstA, - countingLib1, - append(baseKeyValues, cpuKey.Int(1)), - }, - { - "int64-count", - metric.CounterInstrumentKind, - number.Int64Kind, - testInstA, - countingLib1, - append(baseKeyValues, cpuKey.Int(2)), - }, - { - "int64-count", - metric.CounterInstrumentKind, - number.Int64Kind, - testInstA, - summingLib, - append(baseKeyValues, cpuKey.Int(1)), - }, - { - "int64-count", - metric.CounterInstrumentKind, - number.Int64Kind, - testInstB, - countingLib1, - append(baseKeyValues, cpuKey.Int(1)), - }, - }, - []*metricpb.ResourceMetrics{ - { - Resource: testerAResource, - InstrumentationLibraryMetrics: []*metricpb.InstrumentationLibraryMetrics{ - { - InstrumentationLibrary: &commonpb.InstrumentationLibrary{ - Name: "counting-lib", - Version: "v1", - }, - Metrics: []*metricpb.Metric{ - { - Name: "int64-count", - Data: &metricpb.Metric_Sum{ - Sum: &metricpb.Sum{ - IsMonotonic: true, - AggregationTemporality: metricpb.AggregationTemporality_AGGREGATION_TEMPORALITY_CUMULATIVE, - DataPoints: []*metricpb.NumberDataPoint{ - { - Value: &metricpb.NumberDataPoint_AsInt{AsInt: 11}, - Attributes: cpu1Labels, - StartTimeUnixNano: startTime(), - TimeUnixNano: pointTime(), - }, - { - Value: &metricpb.NumberDataPoint_AsInt{AsInt: 11}, - Attributes: cpu1Labels, - StartTimeUnixNano: startTime(), - TimeUnixNano: pointTime(), - }, - { - Value: &metricpb.NumberDataPoint_AsInt{AsInt: 11}, - Attributes: cpu2Labels, - StartTimeUnixNano: startTime(), - TimeUnixNano: pointTime(), - }, - }, - }, - }, - }, - }, - }, - { - InstrumentationLibrary: &commonpb.InstrumentationLibrary{ - Name: "counting-lib", - Version: "v2", - }, - Metrics: []*metricpb.Metric{ - { - Name: "int64-count", - Data: &metricpb.Metric_Sum{ - Sum: &metricpb.Sum{ - IsMonotonic: true, - AggregationTemporality: metricpb.AggregationTemporality_AGGREGATION_TEMPORALITY_CUMULATIVE, - DataPoints: []*metricpb.NumberDataPoint{ - { - Value: &metricpb.NumberDataPoint_AsInt{AsInt: 11}, - Attributes: cpu1Labels, - StartTimeUnixNano: startTime(), - TimeUnixNano: pointTime(), - }, - }, - }, - }, - }, - }, - }, - { - InstrumentationLibrary: &commonpb.InstrumentationLibrary{ - Name: "summing-lib", - }, - Metrics: []*metricpb.Metric{ - { - Name: "int64-count", - Data: &metricpb.Metric_Sum{ - Sum: &metricpb.Sum{ - IsMonotonic: true, - AggregationTemporality: metricpb.AggregationTemporality_AGGREGATION_TEMPORALITY_CUMULATIVE, - DataPoints: []*metricpb.NumberDataPoint{ - { - Value: &metricpb.NumberDataPoint_AsInt{AsInt: 11}, - Attributes: cpu1Labels, - StartTimeUnixNano: startTime(), - TimeUnixNano: pointTime(), - }, - }, - }, - }, - }, - }, - }, - }, - }, - { - Resource: testerBResource, - InstrumentationLibraryMetrics: []*metricpb.InstrumentationLibraryMetrics{ - { - InstrumentationLibrary: &commonpb.InstrumentationLibrary{ - Name: "counting-lib", - Version: "v1", - }, - Metrics: []*metricpb.Metric{ - { - Name: "int64-count", - Data: &metricpb.Metric_Sum{ - Sum: &metricpb.Sum{ - IsMonotonic: true, - AggregationTemporality: metricpb.AggregationTemporality_AGGREGATION_TEMPORALITY_CUMULATIVE, - DataPoints: []*metricpb.NumberDataPoint{ - { - Value: &metricpb.NumberDataPoint_AsInt{AsInt: 11}, - Attributes: cpu1Labels, - StartTimeUnixNano: startTime(), - TimeUnixNano: pointTime(), - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - ) -} - -func TestStatelessExportKind(t *testing.T) { - type testcase struct { - name string - instrumentKind metric.InstrumentKind - aggTemporality metricpb.AggregationTemporality - monotonic bool - } - - for _, k := range []testcase{ - {"counter", metric.CounterInstrumentKind, metricpb.AggregationTemporality_AGGREGATION_TEMPORALITY_DELTA, true}, - {"updowncounter", metric.UpDownCounterInstrumentKind, metricpb.AggregationTemporality_AGGREGATION_TEMPORALITY_DELTA, false}, - {"sumobserver", metric.SumObserverInstrumentKind, metricpb.AggregationTemporality_AGGREGATION_TEMPORALITY_CUMULATIVE, true}, - {"updownsumobserver", metric.UpDownSumObserverInstrumentKind, metricpb.AggregationTemporality_AGGREGATION_TEMPORALITY_CUMULATIVE, false}, - } { - t.Run(k.name, func(t *testing.T) { - runMetricExportTests( - t, - []otlp.ExporterOption{ - otlp.WithMetricExportKindSelector( - metricsdk.StatelessExportKindSelector(), - ), - }, - []record{ - { - "instrument", - k.instrumentKind, - number.Int64Kind, - testInstA, - nil, - append(baseKeyValues, cpuKey.Int(1)), - }, - }, - []*metricpb.ResourceMetrics{ - { - Resource: testerAResource, - InstrumentationLibraryMetrics: []*metricpb.InstrumentationLibraryMetrics{ - { - Metrics: []*metricpb.Metric{ - { - Name: "instrument", - Data: &metricpb.Metric_Sum{ - Sum: &metricpb.Sum{ - IsMonotonic: k.monotonic, - AggregationTemporality: k.aggTemporality, - DataPoints: []*metricpb.NumberDataPoint{ - { - Value: &metricpb.NumberDataPoint_AsInt{AsInt: 11}, - Attributes: cpu1Labels, - StartTimeUnixNano: startTime(), - TimeUnixNano: pointTime(), - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - ) - }) - } -} - -func runMetricExportTests(t *testing.T, opts []otlp.ExporterOption, rs []record, expected []*metricpb.ResourceMetrics) { - exp, driver := newExporter(t, opts...) - - recs := map[attribute.Distinct][]metricsdk.Record{} - resources := map[attribute.Distinct]*resource.Resource{} - for _, r := range rs { - lcopy := make([]attribute.KeyValue, len(r.labels)) - copy(lcopy, r.labels) - desc := metric.NewDescriptor(r.name, r.iKind, r.nKind, r.opts...) - labs := attribute.NewSet(lcopy...) - - var agg, ckpt metricsdk.Aggregator - if r.iKind.Adding() { - agg, ckpt = metrictest.Unslice2(sum.New(2)) - } else { - agg, ckpt = metrictest.Unslice2(histogram.New(2, &desc, histogram.WithExplicitBoundaries(testHistogramBoundaries))) - } - - ctx := context.Background() - if r.iKind.Synchronous() { - // For synchronous instruments, perform two updates: 1 and 10 - switch r.nKind { - case number.Int64Kind: - require.NoError(t, agg.Update(ctx, number.NewInt64Number(1), &desc)) - require.NoError(t, agg.Update(ctx, number.NewInt64Number(10), &desc)) - case number.Float64Kind: - require.NoError(t, agg.Update(ctx, number.NewFloat64Number(1), &desc)) - require.NoError(t, agg.Update(ctx, number.NewFloat64Number(10), &desc)) - default: - t.Fatalf("invalid number kind: %v", r.nKind) - } - } else { - // For asynchronous instruments, perform a single update: 11 - switch r.nKind { - case number.Int64Kind: - require.NoError(t, agg.Update(ctx, number.NewInt64Number(11), &desc)) - case number.Float64Kind: - require.NoError(t, agg.Update(ctx, number.NewFloat64Number(11), &desc)) - default: - t.Fatalf("invalid number kind: %v", r.nKind) - } - } - require.NoError(t, agg.SynchronizedMove(ckpt, &desc)) - - equiv := r.resource.Equivalent() - resources[equiv] = r.resource - recs[equiv] = append(recs[equiv], metricsdk.NewRecord(&desc, &labs, r.resource, ckpt.Aggregation(), intervalStart, intervalEnd)) - } - for _, records := range recs { - assert.NoError(t, exp.Export(context.Background(), &checkpointSet{records: records})) - } - - // assert.ElementsMatch does not equate nested slices of different order, - // therefore this requires the top level slice to be broken down. - // Build a map of Resource/InstrumentationLibrary pairs to Metrics, from - // that validate the metric elements match for all expected pairs. Finally, - // make we saw all expected pairs. - type key struct { - resource, instrumentationLibrary string - } - got := map[key][]*metricpb.Metric{} - for _, rm := range driver.rm { - for _, ilm := range rm.InstrumentationLibraryMetrics { - k := key{ - resource: rm.GetResource().String(), - instrumentationLibrary: ilm.GetInstrumentationLibrary().String(), - } - got[k] = ilm.GetMetrics() - } - } - seen := map[key]struct{}{} - for _, rm := range expected { - for _, ilm := range rm.InstrumentationLibraryMetrics { - k := key{ - resource: rm.GetResource().String(), - instrumentationLibrary: ilm.GetInstrumentationLibrary().String(), - } - seen[k] = struct{}{} - g, ok := got[k] - if !ok { - t.Errorf("missing metrics for:\n\tResource: %s\n\tInstrumentationLibrary: %s\n", k.resource, k.instrumentationLibrary) - continue - } - if !assert.Len(t, g, len(ilm.GetMetrics())) { - continue - } - for i, expected := range ilm.GetMetrics() { - assert.Equal(t, expected.Name, g[i].Name) - assert.Equal(t, expected.Unit, g[i].Unit) - assert.Equal(t, expected.Description, g[i].Description) - switch g[i].Data.(type) { - case *metricpb.Metric_Gauge: - assert.ElementsMatch(t, expected.GetGauge().GetDataPoints(), g[i].GetGauge().GetDataPoints()) - case *metricpb.Metric_Sum: - assert.Equal(t, - expected.GetSum().GetAggregationTemporality(), - g[i].GetSum().GetAggregationTemporality(), - ) - assert.Equal(t, - expected.GetSum().GetIsMonotonic(), - g[i].GetSum().GetIsMonotonic(), - ) - assert.ElementsMatch(t, expected.GetSum().GetDataPoints(), g[i].GetSum().GetDataPoints()) - case *metricpb.Metric_Histogram: - assert.Equal( - t, - expected.GetHistogram().GetAggregationTemporality(), - g[i].GetHistogram().GetAggregationTemporality(), - ) - assert.ElementsMatch(t, expected.GetHistogram().GetDataPoints(), g[i].GetHistogram().GetDataPoints()) - case *metricpb.Metric_Summary: - assert.ElementsMatch(t, expected.GetSummary().GetDataPoints(), g[i].GetSummary().GetDataPoints()) - default: - assert.Failf(t, "unknown data type", g[i].Name) - } - } - } - } - for k := range got { - if _, ok := seen[k]; !ok { - t.Errorf("did not expect metrics for:\n\tResource: %s\n\tInstrumentationLibrary: %s\n", k.resource, k.instrumentationLibrary) - } - } -} - -func TestEmptyMetricExport(t *testing.T) { - exp, driver := newExporter(t) - - for _, test := range []struct { - records []metricsdk.Record - want []*metricpb.ResourceMetrics - }{ - { - []metricsdk.Record(nil), - []*metricpb.ResourceMetrics(nil), - }, - { - []metricsdk.Record{}, - []*metricpb.ResourceMetrics(nil), - }, - } { - driver.Reset() - require.NoError(t, exp.Export(context.Background(), &checkpointSet{records: test.records})) - assert.Equal(t, test.want, driver.rm) - } -} diff --git a/exporters/otlp/otlp_span_test.go b/exporters/otlp/otlp_span_test.go deleted file mode 100644 index 5c379fb7c..000000000 --- a/exporters/otlp/otlp_span_test.go +++ /dev/null @@ -1,345 +0,0 @@ -// Copyright The OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package otlp_test - -import ( - "context" - "testing" - "time" - - "github.com/stretchr/testify/assert" - - "go.opentelemetry.io/otel/attribute" - "go.opentelemetry.io/otel/codes" - "go.opentelemetry.io/otel/trace" - commonpb "go.opentelemetry.io/proto/otlp/common/v1" - resourcepb "go.opentelemetry.io/proto/otlp/resource/v1" - tracepb "go.opentelemetry.io/proto/otlp/trace/v1" - - "go.opentelemetry.io/otel/sdk/instrumentation" - "go.opentelemetry.io/otel/sdk/resource" - tracesdk "go.opentelemetry.io/otel/sdk/trace" - "go.opentelemetry.io/otel/sdk/trace/tracetest" -) - -func TestExportSpans(t *testing.T) { - exp, driver := newExporter(t) - - // March 31, 2020 5:01:26 1234nanos (UTC) - startTime := time.Unix(1585674086, 1234) - endTime := startTime.Add(10 * time.Second) - - for _, test := range []struct { - sd tracetest.SpanStubs - want []*tracepb.ResourceSpans - }{ - { - tracetest.SpanStubsFromReadOnlySpans(nil), - []*tracepb.ResourceSpans(nil), - }, - { - tracetest.SpanStubs{}, - []*tracepb.ResourceSpans(nil), - }, - { - tracetest.SpanStubs{ - { - SpanContext: trace.NewSpanContext(trace.SpanContextConfig{ - TraceID: trace.TraceID([16]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}), - SpanID: trace.SpanID([8]byte{0, 0, 0, 0, 0, 0, 0, 1}), - TraceFlags: trace.FlagsSampled, - }), - SpanKind: trace.SpanKindServer, - Name: "parent process", - StartTime: startTime, - EndTime: endTime, - Attributes: []attribute.KeyValue{ - attribute.String("user", "alice"), - attribute.Bool("authenticated", true), - }, - Status: tracesdk.Status{ - Code: codes.Ok, - Description: "Ok", - }, - Resource: resource.NewSchemaless(attribute.String("instance", "tester-a")), - InstrumentationLibrary: instrumentation.Library{ - Name: "lib-a", - Version: "v0.1.0", - }, - }, - { - SpanContext: trace.NewSpanContext(trace.SpanContextConfig{ - TraceID: trace.TraceID([16]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2}), - SpanID: trace.SpanID([8]byte{0, 0, 0, 0, 0, 0, 0, 1}), - TraceFlags: trace.FlagsSampled, - }), - SpanKind: trace.SpanKindServer, - Name: "secondary parent process", - StartTime: startTime, - EndTime: endTime, - Attributes: []attribute.KeyValue{ - attribute.String("user", "alice"), - attribute.Bool("authenticated", true), - }, - Status: tracesdk.Status{ - Code: codes.Ok, - Description: "Ok", - }, - Resource: resource.NewSchemaless(attribute.String("instance", "tester-a")), - InstrumentationLibrary: instrumentation.Library{ - Name: "lib-b", - Version: "v0.1.0", - }, - }, - { - SpanContext: trace.NewSpanContext(trace.SpanContextConfig{ - TraceID: trace.TraceID([16]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}), - SpanID: trace.SpanID([8]byte{0, 0, 0, 0, 0, 0, 0, 2}), - TraceFlags: trace.FlagsSampled, - }), - Parent: trace.NewSpanContext(trace.SpanContextConfig{ - TraceID: trace.TraceID([16]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}), - SpanID: trace.SpanID([8]byte{0, 0, 0, 0, 0, 0, 0, 1}), - TraceFlags: trace.FlagsSampled, - }), - SpanKind: trace.SpanKindInternal, - Name: "internal process", - StartTime: startTime, - EndTime: endTime, - Attributes: []attribute.KeyValue{ - attribute.String("user", "alice"), - attribute.Bool("authenticated", true), - }, - Status: tracesdk.Status{ - Code: codes.Ok, - Description: "Ok", - }, - Resource: resource.NewSchemaless(attribute.String("instance", "tester-a")), - InstrumentationLibrary: instrumentation.Library{ - Name: "lib-a", - Version: "v0.1.0", - }, - }, - { - SpanContext: trace.NewSpanContext(trace.SpanContextConfig{ - TraceID: trace.TraceID([16]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2}), - SpanID: trace.SpanID([8]byte{0, 0, 0, 0, 0, 0, 0, 1}), - TraceFlags: trace.FlagsSampled, - }), - SpanKind: trace.SpanKindServer, - Name: "parent process", - StartTime: startTime, - EndTime: endTime, - Attributes: []attribute.KeyValue{ - attribute.String("user", "bob"), - attribute.Bool("authenticated", false), - }, - Status: tracesdk.Status{ - Code: codes.Error, - Description: "Unauthenticated", - }, - Resource: resource.NewSchemaless(attribute.String("instance", "tester-b")), - InstrumentationLibrary: instrumentation.Library{ - Name: "lib-a", - Version: "v1.1.0", - }, - }, - }, - []*tracepb.ResourceSpans{ - { - Resource: &resourcepb.Resource{ - Attributes: []*commonpb.KeyValue{ - { - Key: "instance", - Value: &commonpb.AnyValue{ - Value: &commonpb.AnyValue_StringValue{ - StringValue: "tester-a", - }, - }, - }, - }, - }, - InstrumentationLibrarySpans: []*tracepb.InstrumentationLibrarySpans{ - { - InstrumentationLibrary: &commonpb.InstrumentationLibrary{ - Name: "lib-a", - Version: "v0.1.0", - }, - Spans: []*tracepb.Span{ - { - TraceId: []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}, - SpanId: []byte{0, 0, 0, 0, 0, 0, 0, 1}, - Name: "parent process", - Kind: tracepb.Span_SPAN_KIND_SERVER, - StartTimeUnixNano: uint64(startTime.UnixNano()), - EndTimeUnixNano: uint64(endTime.UnixNano()), - Attributes: []*commonpb.KeyValue{ - { - Key: "user", - Value: &commonpb.AnyValue{ - Value: &commonpb.AnyValue_StringValue{ - StringValue: "alice", - }, - }, - }, - { - Key: "authenticated", - Value: &commonpb.AnyValue{ - Value: &commonpb.AnyValue_BoolValue{ - BoolValue: true, - }, - }, - }, - }, - Status: &tracepb.Status{ - Code: tracepb.Status_STATUS_CODE_OK, - Message: "Ok", - }, - }, - { - TraceId: []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}, - SpanId: []byte{0, 0, 0, 0, 0, 0, 0, 2}, - ParentSpanId: []byte{0, 0, 0, 0, 0, 0, 0, 1}, - Name: "internal process", - Kind: tracepb.Span_SPAN_KIND_INTERNAL, - StartTimeUnixNano: uint64(startTime.UnixNano()), - EndTimeUnixNano: uint64(endTime.UnixNano()), - Attributes: []*commonpb.KeyValue{ - { - Key: "user", - Value: &commonpb.AnyValue{ - Value: &commonpb.AnyValue_StringValue{ - StringValue: "alice", - }, - }, - }, - { - Key: "authenticated", - Value: &commonpb.AnyValue{ - Value: &commonpb.AnyValue_BoolValue{ - BoolValue: true, - }, - }, - }, - }, - Status: &tracepb.Status{ - Code: tracepb.Status_STATUS_CODE_OK, - Message: "Ok", - }, - }, - }, - }, - { - InstrumentationLibrary: &commonpb.InstrumentationLibrary{ - Name: "lib-b", - Version: "v0.1.0", - }, - Spans: []*tracepb.Span{ - { - TraceId: []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2}, - SpanId: []byte{0, 0, 0, 0, 0, 0, 0, 1}, - Name: "secondary parent process", - Kind: tracepb.Span_SPAN_KIND_SERVER, - StartTimeUnixNano: uint64(startTime.UnixNano()), - EndTimeUnixNano: uint64(endTime.UnixNano()), - Attributes: []*commonpb.KeyValue{ - { - Key: "user", - Value: &commonpb.AnyValue{ - Value: &commonpb.AnyValue_StringValue{ - StringValue: "alice", - }, - }, - }, - { - Key: "authenticated", - Value: &commonpb.AnyValue{ - Value: &commonpb.AnyValue_BoolValue{ - BoolValue: true, - }, - }, - }, - }, - Status: &tracepb.Status{ - Code: tracepb.Status_STATUS_CODE_OK, - Message: "Ok", - }, - }, - }, - }, - }, - }, - { - Resource: &resourcepb.Resource{ - Attributes: []*commonpb.KeyValue{ - { - Key: "instance", - Value: &commonpb.AnyValue{ - Value: &commonpb.AnyValue_StringValue{ - StringValue: "tester-b", - }, - }, - }, - }, - }, - InstrumentationLibrarySpans: []*tracepb.InstrumentationLibrarySpans{ - { - InstrumentationLibrary: &commonpb.InstrumentationLibrary{ - Name: "lib-a", - Version: "v1.1.0", - }, - Spans: []*tracepb.Span{ - { - TraceId: []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2}, - SpanId: []byte{0, 0, 0, 0, 0, 0, 0, 1}, - Name: "parent process", - Kind: tracepb.Span_SPAN_KIND_SERVER, - StartTimeUnixNano: uint64(startTime.UnixNano()), - EndTimeUnixNano: uint64(endTime.UnixNano()), - Attributes: []*commonpb.KeyValue{ - { - Key: "user", - Value: &commonpb.AnyValue{ - Value: &commonpb.AnyValue_StringValue{ - StringValue: "bob", - }, - }, - }, - { - Key: "authenticated", - Value: &commonpb.AnyValue{ - Value: &commonpb.AnyValue_BoolValue{ - BoolValue: false, - }, - }, - }, - }, - Status: &tracepb.Status{ - Code: tracepb.Status_STATUS_CODE_ERROR, - Message: "Unauthenticated", - }, - }, - }, - }, - }, - }, - }, - }, - } { - driver.Reset() - assert.NoError(t, exp.ExportSpans(context.Background(), test.sd.Snapshots())) - assert.ElementsMatch(t, test.want, driver.rs) - } -} diff --git a/exporters/otlp/otlp_test.go b/exporters/otlp/otlp_test.go deleted file mode 100644 index 801ac1eed..000000000 --- a/exporters/otlp/otlp_test.go +++ /dev/null @@ -1,403 +0,0 @@ -// Copyright The OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package otlp_test - -import ( - "context" - "errors" - "sync" - "testing" - "time" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "go.opentelemetry.io/otel/exporters/otlp" - "go.opentelemetry.io/otel/exporters/otlp/internal/transform" - metricsdk "go.opentelemetry.io/otel/sdk/export/metric" - tracesdk "go.opentelemetry.io/otel/sdk/trace" - "go.opentelemetry.io/otel/sdk/trace/tracetest" - metricpb "go.opentelemetry.io/proto/otlp/metrics/v1" - tracepb "go.opentelemetry.io/proto/otlp/trace/v1" -) - -func readonlyspans(count int) []tracesdk.ReadOnlySpan { - spans := make(tracetest.SpanStubs, 0, count) - for i := 0; i < count; i++ { - spans = append(spans, tracetest.SpanStub{}) - } - return spans.Snapshots() -} - -type stubCheckpointSet struct { - limit int -} - -var _ metricsdk.CheckpointSet = stubCheckpointSet{} - -func (s stubCheckpointSet) ForEach(kindSelector metricsdk.ExportKindSelector, recordFunc func(metricsdk.Record) error) error { - for i := 0; i < s.limit; i++ { - if err := recordFunc(metricsdk.Record{}); err != nil { - return err - } - } - return nil -} - -func (stubCheckpointSet) Lock() {} -func (stubCheckpointSet) Unlock() {} -func (stubCheckpointSet) RLock() {} -func (stubCheckpointSet) RUnlock() {} - -type stubProtocolDriver struct { - started int - stopped int - tracesExported int - metricsExported int - - injectedStartError error - injectedStopError error - - rm []metricsdk.Record - rs tracetest.SpanStubs -} - -var _ otlp.ProtocolDriver = (*stubProtocolDriver)(nil) - -func (m *stubProtocolDriver) Start(ctx context.Context) error { - m.started++ - select { - case <-ctx.Done(): - return ctx.Err() - default: - return m.injectedStartError - } -} - -func (m *stubProtocolDriver) Stop(ctx context.Context) error { - m.stopped++ - select { - case <-ctx.Done(): - return ctx.Err() - default: - return m.injectedStopError - } -} - -func (m *stubProtocolDriver) ExportMetrics(parent context.Context, cps metricsdk.CheckpointSet, selector metricsdk.ExportKindSelector) error { - m.metricsExported++ - return cps.ForEach(selector, func(record metricsdk.Record) error { - m.rm = append(m.rm, record) - return nil - }) -} - -func (m *stubProtocolDriver) ExportTraces(ctx context.Context, ss []tracesdk.ReadOnlySpan) error { - m.tracesExported++ - for _, rs := range ss { - if rs == nil { - continue - } - m.rs = append(m.rs, tracetest.SpanStubFromReadOnlySpan(rs)) - } - return nil -} - -type stubTransformingProtocolDriver struct { - rm []*metricpb.ResourceMetrics - rs []*tracepb.ResourceSpans -} - -var _ otlp.ProtocolDriver = (*stubTransformingProtocolDriver)(nil) - -func (m *stubTransformingProtocolDriver) Start(ctx context.Context) error { - return nil -} - -func (m *stubTransformingProtocolDriver) Stop(ctx context.Context) error { - return nil -} - -func (m *stubTransformingProtocolDriver) ExportMetrics(parent context.Context, cps metricsdk.CheckpointSet, selector metricsdk.ExportKindSelector) error { - rms, err := transform.CheckpointSet(parent, selector, cps, 1) - if err != nil { - return err - } - for _, rm := range rms { - if rm == nil { - continue - } - m.rm = append(m.rm, rm) - } - return nil -} - -func (m *stubTransformingProtocolDriver) ExportTraces(ctx context.Context, ss []tracesdk.ReadOnlySpan) error { - for _, rs := range transform.Spans(ss) { - if rs == nil { - continue - } - m.rs = append(m.rs, rs) - } - return nil -} - -func (m *stubTransformingProtocolDriver) Reset() { - m.rm = nil - m.rs = nil -} - -func newExporter(t *testing.T, opts ...otlp.ExporterOption) (*otlp.Exporter, *stubTransformingProtocolDriver) { - driver := &stubTransformingProtocolDriver{} - exp, err := otlp.New(context.Background(), driver, opts...) - require.NoError(t, err) - return exp, driver -} - -func TestExporterShutdownHonorsTimeout(t *testing.T) { - ctx, cancel := context.WithTimeout(context.Background(), 1*time.Minute) - defer cancel() - - e := otlp.NewUnstarted(&stubProtocolDriver{}) - if err := e.Start(ctx); err != nil { - t.Fatalf("failed to start exporter: %v", err) - } - - innerCtx, innerCancel := context.WithTimeout(ctx, time.Microsecond) - <-time.After(time.Second) - if err := e.Shutdown(innerCtx); err == nil { - t.Error("expected context DeadlineExceeded error, got nil") - } else if !errors.Is(err, context.DeadlineExceeded) { - t.Errorf("expected context DeadlineExceeded error, got %v", err) - } - innerCancel() -} - -func TestExporterShutdownHonorsCancel(t *testing.T) { - ctx, cancel := context.WithTimeout(context.Background(), 1*time.Minute) - defer cancel() - - e := otlp.NewUnstarted(&stubProtocolDriver{}) - if err := e.Start(ctx); err != nil { - t.Fatalf("failed to start exporter: %v", err) - } - - var innerCancel context.CancelFunc - ctx, innerCancel = context.WithCancel(ctx) - innerCancel() - if err := e.Shutdown(ctx); err == nil { - t.Error("expected context canceled error, got nil") - } else if !errors.Is(err, context.Canceled) { - t.Errorf("expected context canceled error, got %v", err) - } -} - -func TestExporterShutdownNoError(t *testing.T) { - ctx, cancel := context.WithTimeout(context.Background(), 1*time.Minute) - defer cancel() - - e := otlp.NewUnstarted(&stubProtocolDriver{}) - if err := e.Start(ctx); err != nil { - t.Fatalf("failed to start exporter: %v", err) - } - - if err := e.Shutdown(ctx); err != nil { - t.Errorf("shutdown errored: expected nil, got %v", err) - } -} - -func TestExporterShutdownManyTimes(t *testing.T) { - ctx := context.Background() - e, err := otlp.New(ctx, &stubProtocolDriver{}) - if err != nil { - t.Fatalf("failed to start an exporter: %v", err) - } - ch := make(chan struct{}) - wg := sync.WaitGroup{} - const num int = 20 - wg.Add(num) - errs := make([]error, num) - for i := 0; i < num; i++ { - go func(idx int) { - defer wg.Done() - <-ch - errs[idx] = e.Shutdown(ctx) - }(i) - } - close(ch) - wg.Wait() - for _, err := range errs { - if err != nil { - t.Fatalf("failed to shutdown exporter: %v", err) - } - } -} - -func TestSplitDriver(t *testing.T) { - - recordCount := 5 - spanCount := 7 - assertExport := func(t testing.TB, ctx context.Context, driver otlp.ProtocolDriver) { - t.Helper() - assert.NoError(t, driver.ExportMetrics(ctx, stubCheckpointSet{recordCount}, metricsdk.StatelessExportKindSelector())) - assert.NoError(t, driver.ExportTraces(ctx, readonlyspans(spanCount))) - } - - t.Run("with metric/trace drivers configured", func(t *testing.T) { - driverTraces := &stubProtocolDriver{} - driverMetrics := &stubProtocolDriver{} - - driver := otlp.NewSplitDriver(otlp.WithMetricDriver(driverMetrics), otlp.WithTraceDriver(driverTraces)) - ctx := context.Background() - assert.NoError(t, driver.Start(ctx)) - assert.Equal(t, 1, driverTraces.started) - assert.Equal(t, 1, driverMetrics.started) - assert.Equal(t, 0, driverTraces.stopped) - assert.Equal(t, 0, driverMetrics.stopped) - assert.Equal(t, 0, driverTraces.tracesExported) - assert.Equal(t, 0, driverTraces.metricsExported) - assert.Equal(t, 0, driverMetrics.tracesExported) - assert.Equal(t, 0, driverMetrics.metricsExported) - - assertExport(t, ctx, driver) - assert.Len(t, driverTraces.rm, 0) - assert.Len(t, driverTraces.rs, spanCount) - assert.Len(t, driverMetrics.rm, recordCount) - assert.Len(t, driverMetrics.rs, 0) - assert.Equal(t, 1, driverTraces.tracesExported) - assert.Equal(t, 0, driverTraces.metricsExported) - assert.Equal(t, 0, driverMetrics.tracesExported) - assert.Equal(t, 1, driverMetrics.metricsExported) - - assert.NoError(t, driver.Stop(ctx)) - assert.Equal(t, 1, driverTraces.started) - assert.Equal(t, 1, driverMetrics.started) - assert.Equal(t, 1, driverTraces.stopped) - assert.Equal(t, 1, driverMetrics.stopped) - assert.Equal(t, 1, driverTraces.tracesExported) - assert.Equal(t, 0, driverTraces.metricsExported) - assert.Equal(t, 0, driverMetrics.tracesExported) - assert.Equal(t, 1, driverMetrics.metricsExported) - }) - - t.Run("with just metric driver", func(t *testing.T) { - driverMetrics := &stubProtocolDriver{} - - driver := otlp.NewSplitDriver(otlp.WithMetricDriver(driverMetrics)) - ctx := context.Background() - assert.NoError(t, driver.Start(ctx)) - - assert.Equal(t, 1, driverMetrics.started) - assert.Equal(t, 0, driverMetrics.stopped) - assert.Equal(t, 0, driverMetrics.tracesExported) - assert.Equal(t, 0, driverMetrics.metricsExported) - - assertExport(t, ctx, driver) - assert.Len(t, driverMetrics.rm, recordCount) - assert.Len(t, driverMetrics.rs, 0) - assert.Equal(t, 0, driverMetrics.tracesExported) - assert.Equal(t, 1, driverMetrics.metricsExported) - - assert.NoError(t, driver.Stop(ctx)) - assert.Equal(t, 1, driverMetrics.started) - assert.Equal(t, 1, driverMetrics.stopped) - assert.Equal(t, 0, driverMetrics.tracesExported) - assert.Equal(t, 1, driverMetrics.metricsExported) - }) - - t.Run("with just trace driver", func(t *testing.T) { - driverTraces := &stubProtocolDriver{} - - driver := otlp.NewSplitDriver(otlp.WithTraceDriver(driverTraces)) - ctx := context.Background() - assert.NoError(t, driver.Start(ctx)) - assert.Equal(t, 1, driverTraces.started) - assert.Equal(t, 0, driverTraces.stopped) - assert.Equal(t, 0, driverTraces.tracesExported) - assert.Equal(t, 0, driverTraces.metricsExported) - - assertExport(t, ctx, driver) - assert.Len(t, driverTraces.rm, 0) - assert.Len(t, driverTraces.rs, spanCount) - assert.Equal(t, 1, driverTraces.tracesExported) - assert.Equal(t, 0, driverTraces.metricsExported) - - assert.NoError(t, driver.Stop(ctx)) - assert.Equal(t, 1, driverTraces.started) - assert.Equal(t, 1, driverTraces.stopped) - assert.Equal(t, 1, driverTraces.tracesExported) - assert.Equal(t, 0, driverTraces.metricsExported) - }) - - t.Run("with no drivers configured", func(t *testing.T) { - - driver := otlp.NewSplitDriver() - ctx := context.Background() - assert.NoError(t, driver.Start(ctx)) - - assert.NoError(t, driver.ExportMetrics(ctx, stubCheckpointSet{recordCount}, metricsdk.StatelessExportKindSelector())) - assert.NoError(t, driver.ExportTraces(ctx, readonlyspans(spanCount))) - assert.NoError(t, driver.Stop(ctx)) - }) - -} - -func TestSplitDriverFail(t *testing.T) { - ctx := context.Background() - for i := 0; i < 16; i++ { - var ( - errStartMetric error - errStartTrace error - errStopMetric error - errStopTrace error - ) - if (i & 1) != 0 { - errStartTrace = errors.New("trace start failed") - } - if (i & 2) != 0 { - errStopTrace = errors.New("trace stop failed") - } - if (i & 4) != 0 { - errStartMetric = errors.New("metric start failed") - } - if (i & 8) != 0 { - errStopMetric = errors.New("metric stop failed") - } - shouldStartFail := errStartTrace != nil || errStartMetric != nil - shouldStopFail := errStopTrace != nil || errStopMetric != nil - - driverTraces := &stubProtocolDriver{ - injectedStartError: errStartTrace, - injectedStopError: errStopTrace, - } - driverMetrics := &stubProtocolDriver{ - injectedStartError: errStartMetric, - injectedStopError: errStopMetric, - } - driver := otlp.NewSplitDriver(otlp.WithMetricDriver(driverMetrics), otlp.WithTraceDriver(driverTraces)) - errStart := driver.Start(ctx) - if shouldStartFail { - assert.Error(t, errStart) - } else { - assert.NoError(t, errStart) - } - errStop := driver.Stop(ctx) - if shouldStopFail { - assert.Error(t, errStop) - } else { - assert.NoError(t, errStop) - } - } -} diff --git a/exporters/otlp/otlpgrpc/alignment_test.go b/exporters/otlp/otlpgrpc/alignment_test.go deleted file mode 100644 index db19e7d03..000000000 --- a/exporters/otlp/otlpgrpc/alignment_test.go +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright The OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package otlpgrpc - -import ( - "os" - "testing" - "unsafe" - - ottest "go.opentelemetry.io/otel/internal/internaltest" -) - -// Ensure struct alignment prior to running tests. -func TestMain(m *testing.M) { - fields := []ottest.FieldOffset{ - { - Name: "connection.lastConnectErrPtr", - Offset: unsafe.Offsetof(connection{}.lastConnectErrPtr), - }, - } - if !ottest.Aligned8Byte(fields, os.Stderr) { - os.Exit(1) - } - - os.Exit(m.Run()) -} diff --git a/exporters/otlp/otlpgrpc/connection.go b/exporters/otlp/otlpgrpc/connection.go deleted file mode 100644 index 097388aab..000000000 --- a/exporters/otlp/otlpgrpc/connection.go +++ /dev/null @@ -1,426 +0,0 @@ -// Copyright The OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package otlpgrpc - -import ( - "context" - "fmt" - "math/rand" - "sync" - "sync/atomic" - "time" - "unsafe" - - "github.com/cenkalti/backoff/v4" - "google.golang.org/genproto/googleapis/rpc/errdetails" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/status" - - "google.golang.org/grpc/encoding/gzip" - - "go.opentelemetry.io/otel/exporters/otlp" - "go.opentelemetry.io/otel/exporters/otlp/internal/otlpconfig" - - "google.golang.org/grpc" - "google.golang.org/grpc/metadata" -) - -type connection struct { - // Ensure pointer is 64-bit aligned for atomic operations on both 32 and 64 bit machines. - lastConnectErrPtr unsafe.Pointer - - // mu protects the connection as it is accessed by the - // exporter goroutines and background connection goroutine - mu sync.Mutex - cc *grpc.ClientConn - - // these fields are read-only after constructor is finished - cfg otlpconfig.Config - sCfg otlpconfig.SignalConfig - metadata metadata.MD - newConnectionHandler func(cc *grpc.ClientConn) - - // these channels are created once - disconnectedCh chan bool - backgroundConnectionDoneCh chan struct{} - stopCh chan struct{} - - // this is for tests, so they can replace the closing - // routine without a worry of modifying some global variable - // or changing it back to original after the test is done - closeBackgroundConnectionDoneCh func(ch chan struct{}) -} - -func newConnection(cfg otlpconfig.Config, sCfg otlpconfig.SignalConfig, handler func(cc *grpc.ClientConn)) *connection { - c := new(connection) - c.newConnectionHandler = handler - c.cfg = cfg - c.sCfg = sCfg - if len(c.sCfg.Headers) > 0 { - c.metadata = metadata.New(c.sCfg.Headers) - } - c.closeBackgroundConnectionDoneCh = func(ch chan struct{}) { - close(ch) - } - return c -} - -func (c *connection) startConnection(ctx context.Context) { - c.stopCh = make(chan struct{}) - c.disconnectedCh = make(chan bool, 1) - c.backgroundConnectionDoneCh = make(chan struct{}) - - if err := c.connect(ctx); err == nil { - c.setStateConnected() - } else { - c.setStateDisconnected(err) - } - go c.indefiniteBackgroundConnection() -} - -func (c *connection) lastConnectError() error { - errPtr := (*error)(atomic.LoadPointer(&c.lastConnectErrPtr)) - if errPtr == nil { - return nil - } - return *errPtr -} - -func (c *connection) saveLastConnectError(err error) { - var errPtr *error - if err != nil { - errPtr = &err - } - atomic.StorePointer(&c.lastConnectErrPtr, unsafe.Pointer(errPtr)) -} - -func (c *connection) setStateDisconnected(err error) { - c.saveLastConnectError(err) - select { - case c.disconnectedCh <- true: - default: - } - c.newConnectionHandler(nil) -} - -func (c *connection) setStateConnected() { - c.saveLastConnectError(nil) -} - -func (c *connection) connected() bool { - return c.lastConnectError() == nil -} - -const defaultConnReattemptPeriod = 10 * time.Second - -func (c *connection) indefiniteBackgroundConnection() { - defer func() { - c.closeBackgroundConnectionDoneCh(c.backgroundConnectionDoneCh) - }() - - connReattemptPeriod := c.cfg.ReconnectionPeriod - if connReattemptPeriod <= 0 { - connReattemptPeriod = defaultConnReattemptPeriod - } - - // No strong seeding required, nano time can - // already help with pseudo uniqueness. - rng := rand.New(rand.NewSource(time.Now().UnixNano() + rand.Int63n(1024))) - - // maxJitterNanos: 70% of the connectionReattemptPeriod - maxJitterNanos := int64(0.7 * float64(connReattemptPeriod)) - - for { - // Otherwise these will be the normal scenarios to enable - // reconnection if we trip out. - // 1. If we've stopped, return entirely - // 2. Otherwise block until we are disconnected, and - // then retry connecting - select { - case <-c.stopCh: - return - - case <-c.disconnectedCh: - // Quickly check if we haven't stopped at the - // same time. - select { - case <-c.stopCh: - return - - default: - } - - // Normal scenario that we'll wait for - } - - if err := c.connect(context.Background()); err == nil { - c.setStateConnected() - } else { - // this code is unreachable in most cases - // c.connect does not establish connection - c.setStateDisconnected(err) - } - - // Apply some jitter to avoid lockstep retrials of other - // collector-exporters. Lockstep retrials could result in an - // innocent DDOS, by clogging the machine's resources and network. - jitter := time.Duration(rng.Int63n(maxJitterNanos)) - select { - case <-c.stopCh: - return - case <-time.After(connReattemptPeriod + jitter): - } - } -} - -func (c *connection) connect(ctx context.Context) error { - cc, err := c.dialToCollector(ctx) - if err != nil { - return err - } - c.setConnection(cc) - c.newConnectionHandler(cc) - return nil -} - -// setConnection sets cc as the client connection and returns true if -// the connection state changed. -func (c *connection) setConnection(cc *grpc.ClientConn) bool { - c.mu.Lock() - defer c.mu.Unlock() - - // If previous clientConn is same as the current then just return. - // This doesn't happen right now as this func is only called with new ClientConn. - // It is more about future-proofing. - if c.cc == cc { - return false - } - - // If the previous clientConn was non-nil, close it - if c.cc != nil { - _ = c.cc.Close() - } - c.cc = cc - return true -} - -func (c *connection) dialToCollector(ctx context.Context) (*grpc.ClientConn, error) { - dialOpts := []grpc.DialOption{} - if c.cfg.ServiceConfig != "" { - dialOpts = append(dialOpts, grpc.WithDefaultServiceConfig(c.cfg.ServiceConfig)) - } - if c.sCfg.GRPCCredentials != nil { - dialOpts = append(dialOpts, grpc.WithTransportCredentials(c.sCfg.GRPCCredentials)) - } else if c.sCfg.Insecure { - dialOpts = append(dialOpts, grpc.WithInsecure()) - } - if c.sCfg.Compression == otlp.GzipCompression { - dialOpts = append(dialOpts, grpc.WithDefaultCallOptions(grpc.UseCompressor(gzip.Name))) - } - if len(c.cfg.DialOptions) != 0 { - dialOpts = append(dialOpts, c.cfg.DialOptions...) - } - - ctx, cancel := c.contextWithStop(ctx) - defer cancel() - ctx = c.contextWithMetadata(ctx) - return grpc.DialContext(ctx, c.sCfg.Endpoint, dialOpts...) -} - -func (c *connection) contextWithMetadata(ctx context.Context) context.Context { - if c.metadata.Len() > 0 { - return metadata.NewOutgoingContext(ctx, c.metadata) - } - return ctx -} - -func (c *connection) shutdown(ctx context.Context) error { - close(c.stopCh) - // Ensure that the backgroundConnector returns - select { - case <-c.backgroundConnectionDoneCh: - case <-ctx.Done(): - return ctx.Err() - } - - c.mu.Lock() - cc := c.cc - c.cc = nil - c.mu.Unlock() - - if cc != nil { - return cc.Close() - } - - return nil -} - -func (c *connection) contextWithStop(ctx context.Context) (context.Context, context.CancelFunc) { - // Unify the parent context Done signal with the connection's - // stop channel. - ctx, cancel := context.WithCancel(ctx) - go func(ctx context.Context, cancel context.CancelFunc) { - select { - case <-ctx.Done(): - // Nothing to do, either cancelled or deadline - // happened. - case <-c.stopCh: - cancel() - } - }(ctx, cancel) - return ctx, cancel -} - -func (c *connection) doRequest(ctx context.Context, fn func(context.Context) error) error { - expBackoff := newExponentialBackoff(c.cfg.RetrySettings) - - for { - err := fn(ctx) - if err == nil { - // request succeeded. - return nil - } - - if !c.cfg.RetrySettings.Enabled { - return err - } - - // We have an error, check gRPC status code. - st := status.Convert(err) - if st.Code() == codes.OK { - // Not really an error, still success. - return nil - } - - // Now, this is this a real error. - - if !shouldRetry(st.Code()) { - // It is not a retryable error, we should not retry. - return err - } - - // Need to retry. - - throttle := getThrottleDuration(st) - - backoffDelay := expBackoff.NextBackOff() - if backoffDelay == backoff.Stop { - // throw away the batch - err = fmt.Errorf("max elapsed time expired: %w", err) - return err - } - - var delay time.Duration - - if backoffDelay > throttle { - delay = backoffDelay - } else { - if expBackoff.GetElapsedTime()+throttle > expBackoff.MaxElapsedTime { - err = fmt.Errorf("max elapsed time expired when respecting server throttle: %w", err) - return err - } - - // Respect server throttling. - delay = throttle - } - - // back-off, but get interrupted when shutting down or request is cancelled or timed out. - err = func() error { - dt := time.NewTimer(delay) - defer dt.Stop() - - select { - case <-ctx.Done(): - return ctx.Err() - case <-c.stopCh: - return fmt.Errorf("interrupted due to shutdown: %w", err) - case <-dt.C: - } - - return nil - }() - - if err != nil { - return err - } - - } -} - -func shouldRetry(code codes.Code) bool { - switch code { - case codes.OK: - // Success. This function should not be called for this code, the best we - // can do is tell the caller not to retry. - return false - - case codes.Canceled, - codes.DeadlineExceeded, - codes.ResourceExhausted, - codes.Aborted, - codes.OutOfRange, - codes.Unavailable, - codes.DataLoss: - // These are retryable errors. - return true - - case codes.Unknown, - codes.InvalidArgument, - codes.Unauthenticated, - codes.PermissionDenied, - codes.NotFound, - codes.AlreadyExists, - codes.FailedPrecondition, - codes.Unimplemented, - codes.Internal: - // These are fatal errors, don't retry. - return false - - default: - // Don't retry on unknown codes. - return false - } -} - -func getThrottleDuration(status *status.Status) time.Duration { - // See if throttling information is available. - for _, detail := range status.Details() { - if t, ok := detail.(*errdetails.RetryInfo); ok { - if t.RetryDelay.Seconds > 0 || t.RetryDelay.Nanos > 0 { - // We are throttled. Wait before retrying as requested by the server. - return time.Duration(t.RetryDelay.Seconds)*time.Second + time.Duration(t.RetryDelay.Nanos)*time.Nanosecond - } - return 0 - } - } - return 0 -} - -func newExponentialBackoff(rs otlp.RetrySettings) *backoff.ExponentialBackOff { - // Do not use NewExponentialBackOff since it calls Reset and the code here must - // call Reset after changing the InitialInterval (this saves an unnecessary call to Now). - expBackoff := &backoff.ExponentialBackOff{ - InitialInterval: rs.InitialInterval, - RandomizationFactor: backoff.DefaultRandomizationFactor, - Multiplier: backoff.DefaultMultiplier, - MaxInterval: rs.MaxInterval, - MaxElapsedTime: rs.MaxElapsedTime, - Stop: backoff.Stop, - Clock: backoff.SystemClock, - } - expBackoff.Reset() - - return expBackoff -} diff --git a/exporters/otlp/otlpgrpc/connection_test.go b/exporters/otlp/otlpgrpc/connection_test.go deleted file mode 100644 index ea0e5d70b..000000000 --- a/exporters/otlp/otlpgrpc/connection_test.go +++ /dev/null @@ -1,90 +0,0 @@ -// Copyright The OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package otlpgrpc - -import ( - "testing" - "time" - - "github.com/stretchr/testify/require" - - "google.golang.org/genproto/googleapis/rpc/errdetails" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/status" - "google.golang.org/protobuf/types/known/durationpb" -) - -func TestGetThrottleDuration(t *testing.T) { - tts := []struct { - stsFn func() (*status.Status, error) - throttle time.Duration - }{ - { - stsFn: func() (*status.Status, error) { - return status.New( - codes.OK, - "status with no retry info", - ), nil - }, - throttle: 0, - }, - { - stsFn: func() (*status.Status, error) { - st := status.New(codes.ResourceExhausted, "status with retry info") - return st.WithDetails( - &errdetails.RetryInfo{RetryDelay: durationpb.New(15 * time.Millisecond)}, - ) - }, - throttle: 15 * time.Millisecond, - }, - { - stsFn: func() (*status.Status, error) { - st := status.New(codes.ResourceExhausted, "status with error info detail") - return st.WithDetails( - &errdetails.ErrorInfo{Reason: "no throttle detail"}, - ) - }, - throttle: 0, - }, - { - stsFn: func() (*status.Status, error) { - st := status.New(codes.ResourceExhausted, "status with error info and retry info") - return st.WithDetails( - &errdetails.ErrorInfo{Reason: "no throttle detail"}, - &errdetails.RetryInfo{RetryDelay: durationpb.New(13 * time.Minute)}, - ) - }, - throttle: 13 * time.Minute, - }, - { - stsFn: func() (*status.Status, error) { - st := status.New(codes.ResourceExhausted, "status with two retry info should take the first") - return st.WithDetails( - &errdetails.RetryInfo{RetryDelay: durationpb.New(13 * time.Minute)}, - &errdetails.RetryInfo{RetryDelay: durationpb.New(18 * time.Minute)}, - ) - }, - throttle: 13 * time.Minute, - }, - } - - for _, tt := range tts { - sts, _ := tt.stsFn() - t.Run(sts.Message(), func(t *testing.T) { - th := getThrottleDuration(sts) - require.Equal(t, tt.throttle, th) - }) - } -} diff --git a/exporters/otlp/otlpgrpc/doc.go b/exporters/otlp/otlpgrpc/doc.go deleted file mode 100644 index 68f53fb0a..000000000 --- a/exporters/otlp/otlpgrpc/doc.go +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright The OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -/* -Package otlpgrpc provides an implementation of otlp.ProtocolDriver -that connects to the collector and sends traces and metrics using -gRPC. - -This package is currently in a pre-GA phase. Backwards incompatible -changes may be introduced in subsequent minor version releases as we -work to track the evolving OpenTelemetry specification and user -feedback. -*/ -package otlpgrpc // import "go.opentelemetry.io/otel/exporters/otlp/otlpgrpc" diff --git a/exporters/otlp/otlpgrpc/driver.go b/exporters/otlp/otlpgrpc/driver.go deleted file mode 100644 index dedfd3f21..000000000 --- a/exporters/otlp/otlpgrpc/driver.go +++ /dev/null @@ -1,200 +0,0 @@ -// Copyright The OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package otlpgrpc // import "go.opentelemetry.io/otel/exporters/otlp/otlpgrpc" - -import ( - "context" - "errors" - "fmt" - "sync" - - "go.opentelemetry.io/otel/exporters/otlp/internal/otlpconfig" - - "google.golang.org/grpc" - - "go.opentelemetry.io/otel/exporters/otlp" - "go.opentelemetry.io/otel/exporters/otlp/internal/transform" - metricsdk "go.opentelemetry.io/otel/sdk/export/metric" - tracesdk "go.opentelemetry.io/otel/sdk/trace" - colmetricpb "go.opentelemetry.io/proto/otlp/collector/metrics/v1" - coltracepb "go.opentelemetry.io/proto/otlp/collector/trace/v1" - metricpb "go.opentelemetry.io/proto/otlp/metrics/v1" - tracepb "go.opentelemetry.io/proto/otlp/trace/v1" -) - -type driver struct { - metricsDriver metricsDriver - tracesDriver tracesDriver -} - -type metricsDriver struct { - connection *connection - - lock sync.Mutex - metricsClient colmetricpb.MetricsServiceClient -} - -type tracesDriver struct { - connection *connection - - lock sync.Mutex - tracesClient coltracepb.TraceServiceClient -} - -var ( - errNoClient = errors.New("no client") -) - -// NewDriver creates a new gRPC protocol driver. -func NewDriver(opts ...Option) otlp.ProtocolDriver { - cfg := otlpconfig.NewDefaultConfig() - otlpconfig.ApplyGRPCEnvConfigs(&cfg) - for _, opt := range opts { - opt.applyGRPCOption(&cfg) - } - - d := &driver{} - - d.tracesDriver = tracesDriver{ - connection: newConnection(cfg, cfg.Traces, d.tracesDriver.handleNewConnection), - } - - d.metricsDriver = metricsDriver{ - connection: newConnection(cfg, cfg.Metrics, d.metricsDriver.handleNewConnection), - } - return d -} - -func (md *metricsDriver) handleNewConnection(cc *grpc.ClientConn) { - md.lock.Lock() - defer md.lock.Unlock() - if cc != nil { - md.metricsClient = colmetricpb.NewMetricsServiceClient(cc) - } else { - md.metricsClient = nil - } -} - -func (td *tracesDriver) handleNewConnection(cc *grpc.ClientConn) { - td.lock.Lock() - defer td.lock.Unlock() - if cc != nil { - td.tracesClient = coltracepb.NewTraceServiceClient(cc) - } else { - td.tracesClient = nil - } -} - -// Start implements otlp.ProtocolDriver. It establishes a connection -// to the collector. -func (d *driver) Start(ctx context.Context) error { - d.tracesDriver.connection.startConnection(ctx) - d.metricsDriver.connection.startConnection(ctx) - return nil -} - -// Stop implements otlp.ProtocolDriver. It shuts down the connection -// to the collector. -func (d *driver) Stop(ctx context.Context) error { - if err := d.tracesDriver.connection.shutdown(ctx); err != nil { - return err - } - - return d.metricsDriver.connection.shutdown(ctx) -} - -// ExportMetrics implements otlp.ProtocolDriver. It transforms metrics -// to protobuf binary format and sends the result to the collector. -func (d *driver) ExportMetrics(ctx context.Context, cps metricsdk.CheckpointSet, selector metricsdk.ExportKindSelector) error { - if !d.metricsDriver.connection.connected() { - return fmt.Errorf("metrics exporter is disconnected from the server %s: %w", d.metricsDriver.connection.sCfg.Endpoint, d.metricsDriver.connection.lastConnectError()) - } - ctx, cancel := d.metricsDriver.connection.contextWithStop(ctx) - defer cancel() - ctx, tCancel := context.WithTimeout(ctx, d.metricsDriver.connection.sCfg.Timeout) - defer tCancel() - - rms, err := transform.CheckpointSet(ctx, selector, cps, 1) - if err != nil { - return err - } - if len(rms) == 0 { - return nil - } - - return d.metricsDriver.uploadMetrics(ctx, rms) -} - -func (md *metricsDriver) uploadMetrics(ctx context.Context, protoMetrics []*metricpb.ResourceMetrics) error { - ctx = md.connection.contextWithMetadata(ctx) - err := func() error { - md.lock.Lock() - defer md.lock.Unlock() - if md.metricsClient == nil { - return errNoClient - } - - return md.connection.doRequest(ctx, func(ctx context.Context) error { - _, err := md.metricsClient.Export(ctx, &colmetricpb.ExportMetricsServiceRequest{ - ResourceMetrics: protoMetrics, - }) - return err - }) - }() - if err != nil { - md.connection.setStateDisconnected(err) - } - return err -} - -// ExportTraces implements otlp.ProtocolDriver. It transforms spans to -// protobuf binary format and sends the result to the collector. -func (d *driver) ExportTraces(ctx context.Context, ss []tracesdk.ReadOnlySpan) error { - if !d.tracesDriver.connection.connected() { - return fmt.Errorf("traces exporter is disconnected from the server %s: %w", d.tracesDriver.connection.sCfg.Endpoint, d.tracesDriver.connection.lastConnectError()) - } - ctx, cancel := d.tracesDriver.connection.contextWithStop(ctx) - defer cancel() - ctx, tCancel := context.WithTimeout(ctx, d.tracesDriver.connection.sCfg.Timeout) - defer tCancel() - - protoSpans := transform.Spans(ss) - if len(protoSpans) == 0 { - return nil - } - - return d.tracesDriver.uploadTraces(ctx, protoSpans) -} - -func (td *tracesDriver) uploadTraces(ctx context.Context, protoSpans []*tracepb.ResourceSpans) error { - ctx = td.connection.contextWithMetadata(ctx) - err := func() error { - td.lock.Lock() - defer td.lock.Unlock() - if td.tracesClient == nil { - return errNoClient - } - return td.connection.doRequest(ctx, func(ctx context.Context) error { - _, err := td.tracesClient.Export(ctx, &coltracepb.ExportTraceServiceRequest{ - ResourceSpans: protoSpans, - }) - return err - }) - }() - if err != nil { - td.connection.setStateDisconnected(err) - } - return err -} diff --git a/exporters/otlp/otlpgrpc/example_test.go b/exporters/otlp/otlpgrpc/example_test.go deleted file mode 100644 index a247a6844..000000000 --- a/exporters/otlp/otlpgrpc/example_test.go +++ /dev/null @@ -1,226 +0,0 @@ -// Copyright The OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package otlpgrpc_test - -import ( - "context" - "fmt" - "log" - "time" - - "google.golang.org/grpc/credentials" - - "go.opentelemetry.io/otel" - "go.opentelemetry.io/otel/exporters/otlp" - "go.opentelemetry.io/otel/exporters/otlp/otlpgrpc" - "go.opentelemetry.io/otel/metric" - "go.opentelemetry.io/otel/metric/global" - controller "go.opentelemetry.io/otel/sdk/metric/controller/basic" - processor "go.opentelemetry.io/otel/sdk/metric/processor/basic" - "go.opentelemetry.io/otel/sdk/metric/selector/simple" - sdktrace "go.opentelemetry.io/otel/sdk/trace" -) - -func Example_insecure() { - ctx := context.Background() - driver := otlpgrpc.NewDriver(otlpgrpc.WithInsecure()) - exp, err := otlp.New(ctx, driver) - if err != nil { - log.Fatalf("Failed to create the collector exporter: %v", err) - } - defer func() { - ctx, cancel := context.WithTimeout(ctx, time.Second) - defer cancel() - if err := exp.Shutdown(ctx); err != nil { - otel.Handle(err) - } - }() - - tp := sdktrace.NewTracerProvider( - sdktrace.WithSampler(sdktrace.AlwaysSample()), - sdktrace.WithBatcher( - exp, - // add following two options to ensure flush - sdktrace.WithBatchTimeout(5*time.Second), - sdktrace.WithMaxExportBatchSize(10), - ), - ) - defer func() { - ctx, cancel := context.WithTimeout(ctx, time.Second) - defer cancel() - if err := tp.Shutdown(ctx); err != nil { - otel.Handle(err) - } - }() - otel.SetTracerProvider(tp) - - tracer := otel.Tracer("test-tracer") - - // Then use the OpenTelemetry tracing library, like we normally would. - ctx, span := tracer.Start(ctx, "CollectorExporter-Example") - defer span.End() - - for i := 0; i < 10; i++ { - _, iSpan := tracer.Start(ctx, fmt.Sprintf("Sample-%d", i)) - <-time.After(6 * time.Millisecond) - iSpan.End() - } -} - -func Example_withTLS() { - // Please take at look at https://pkg.go.dev/google.golang.org/grpc/credentials#TransportCredentials - // for ways on how to initialize gRPC TransportCredentials. - creds, err := credentials.NewClientTLSFromFile("my-cert.pem", "") - if err != nil { - log.Fatalf("failed to create gRPC client TLS credentials: %v", err) - } - - ctx := context.Background() - driver := otlpgrpc.NewDriver(otlpgrpc.WithTLSCredentials(creds)) - exp, err := otlp.New(ctx, driver) - if err != nil { - log.Fatalf("failed to create the collector exporter: %v", err) - } - defer func() { - ctx, cancel := context.WithTimeout(ctx, time.Second) - defer cancel() - if err := exp.Shutdown(ctx); err != nil { - otel.Handle(err) - } - }() - - tp := sdktrace.NewTracerProvider( - sdktrace.WithSampler(sdktrace.AlwaysSample()), - sdktrace.WithBatcher( - exp, - // add following two options to ensure flush - sdktrace.WithBatchTimeout(5*time.Second), - sdktrace.WithMaxExportBatchSize(10), - ), - ) - defer func() { - ctx, cancel := context.WithTimeout(ctx, time.Second) - defer cancel() - if err := tp.Shutdown(ctx); err != nil { - otel.Handle(err) - } - }() - otel.SetTracerProvider(tp) - - tracer := otel.Tracer("test-tracer") - - // Then use the OpenTelemetry tracing library, like we normally would. - ctx, span := tracer.Start(ctx, "Securely-Talking-To-Collector-Span") - defer span.End() - - for i := 0; i < 10; i++ { - _, iSpan := tracer.Start(ctx, fmt.Sprintf("Sample-%d", i)) - <-time.After(6 * time.Millisecond) - iSpan.End() - } -} - -func Example_withDifferentSignalCollectors() { - - // Set different endpoints for the metrics and traces collectors - metricsDriver := otlpgrpc.NewDriver( - otlpgrpc.WithInsecure(), - otlpgrpc.WithEndpoint("localhost:30080"), - ) - tracesDriver := otlpgrpc.NewDriver( - otlpgrpc.WithInsecure(), - otlpgrpc.WithEndpoint("localhost:30082"), - ) - driver := otlp.NewSplitDriver(otlp.WithMetricDriver(metricsDriver), otlp.WithTraceDriver(tracesDriver)) - ctx := context.Background() - exp, err := otlp.New(ctx, driver) - if err != nil { - log.Fatalf("failed to create the collector exporter: %v", err) - } - - defer func() { - ctx, cancel := context.WithTimeout(ctx, time.Second) - defer cancel() - if err := exp.Shutdown(ctx); err != nil { - otel.Handle(err) - } - }() - - tp := sdktrace.NewTracerProvider( - sdktrace.WithSampler(sdktrace.AlwaysSample()), - sdktrace.WithBatcher( - exp, - // add following two options to ensure flush - sdktrace.WithBatchTimeout(5*time.Second), - sdktrace.WithMaxExportBatchSize(10), - ), - ) - defer func() { - ctx, cancel := context.WithTimeout(ctx, time.Second) - defer cancel() - if err := tp.Shutdown(ctx); err != nil { - otel.Handle(err) - } - }() - otel.SetTracerProvider(tp) - - pusher := controller.New( - processor.New( - simple.NewWithExactDistribution(), - exp, - ), - controller.WithExporter(exp), - controller.WithCollectPeriod(2*time.Second), - ) - global.SetMeterProvider(pusher.MeterProvider()) - - if err := pusher.Start(ctx); err != nil { - log.Fatalf("could not start metric controoler: %v", err) - } - defer func() { - ctx, cancel := context.WithTimeout(ctx, time.Second) - defer cancel() - // pushes any last exports to the receiver - if err := pusher.Stop(ctx); err != nil { - otel.Handle(err) - } - }() - - tracer := otel.Tracer("test-tracer") - meter := global.Meter("test-meter") - - // Recorder metric example - counter := metric.Must(meter). - NewFloat64Counter( - "an_important_metric", - metric.WithDescription("Measures the cumulative epicness of the app"), - ) - - // work begins - ctx, span := tracer.Start( - ctx, - "DifferentCollectors-Example") - defer span.End() - for i := 0; i < 10; i++ { - _, iSpan := tracer.Start(ctx, fmt.Sprintf("Sample-%d", i)) - log.Printf("Doing really hard work (%d / 10)\n", i+1) - counter.Add(ctx, 1.0) - - <-time.After(time.Second) - iSpan.End() - } - - log.Printf("Done!") -} diff --git a/exporters/otlp/otlpgrpc/mock_collector_test.go b/exporters/otlp/otlpgrpc/mock_collector_test.go deleted file mode 100644 index 13ce21039..000000000 --- a/exporters/otlp/otlpgrpc/mock_collector_test.go +++ /dev/null @@ -1,292 +0,0 @@ -// Copyright The OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package otlpgrpc_test - -import ( - "context" - "fmt" - "net" - "runtime" - "strings" - "sync" - "testing" - "time" - - "google.golang.org/grpc" - metadata "google.golang.org/grpc/metadata" - - "go.opentelemetry.io/otel/exporters/otlp/internal/otlptest" - collectormetricpb "go.opentelemetry.io/proto/otlp/collector/metrics/v1" - collectortracepb "go.opentelemetry.io/proto/otlp/collector/trace/v1" - metricpb "go.opentelemetry.io/proto/otlp/metrics/v1" - tracepb "go.opentelemetry.io/proto/otlp/trace/v1" -) - -func makeMockCollector(t *testing.T, mockConfig *mockConfig) *mockCollector { - return &mockCollector{ - t: t, - traceSvc: &mockTraceService{ - storage: otlptest.NewSpansStorage(), - errors: mockConfig.errors, - }, - metricSvc: &mockMetricService{ - storage: otlptest.NewMetricsStorage(), - }, - } -} - -type mockTraceService struct { - collectortracepb.UnimplementedTraceServiceServer - - errors []error - requests int - mu sync.RWMutex - storage otlptest.SpansStorage - headers metadata.MD - delay time.Duration -} - -func (mts *mockTraceService) getHeaders() metadata.MD { - mts.mu.RLock() - defer mts.mu.RUnlock() - return mts.headers -} - -func (mts *mockTraceService) getSpans() []*tracepb.Span { - mts.mu.RLock() - defer mts.mu.RUnlock() - return mts.storage.GetSpans() -} - -func (mts *mockTraceService) getResourceSpans() []*tracepb.ResourceSpans { - mts.mu.RLock() - defer mts.mu.RUnlock() - return mts.storage.GetResourceSpans() -} - -func (mts *mockTraceService) Export(ctx context.Context, exp *collectortracepb.ExportTraceServiceRequest) (*collectortracepb.ExportTraceServiceResponse, error) { - if mts.delay > 0 { - time.Sleep(mts.delay) - } - - mts.mu.Lock() - defer func() { - mts.requests++ - mts.mu.Unlock() - }() - - reply := &collectortracepb.ExportTraceServiceResponse{} - if mts.requests < len(mts.errors) { - idx := mts.requests - return reply, mts.errors[idx] - } - - mts.headers, _ = metadata.FromIncomingContext(ctx) - mts.storage.AddSpans(exp) - return reply, nil -} - -type mockMetricService struct { - collectormetricpb.UnimplementedMetricsServiceServer - - mu sync.RWMutex - storage otlptest.MetricsStorage - delay time.Duration -} - -func (mms *mockMetricService) getMetrics() []*metricpb.Metric { - mms.mu.RLock() - defer mms.mu.RUnlock() - return mms.storage.GetMetrics() -} - -func (mms *mockMetricService) Export(ctx context.Context, exp *collectormetricpb.ExportMetricsServiceRequest) (*collectormetricpb.ExportMetricsServiceResponse, error) { - if mms.delay > 0 { - time.Sleep(mms.delay) - } - reply := &collectormetricpb.ExportMetricsServiceResponse{} - mms.mu.Lock() - defer mms.mu.Unlock() - mms.storage.AddMetrics(exp) - return reply, nil -} - -type mockCollector struct { - t *testing.T - - traceSvc *mockTraceService - metricSvc *mockMetricService - - endpoint string - ln *listener - stopFunc func() - stopOnce sync.Once -} - -type mockConfig struct { - errors []error - endpoint string -} - -var _ collectortracepb.TraceServiceServer = (*mockTraceService)(nil) -var _ collectormetricpb.MetricsServiceServer = (*mockMetricService)(nil) - -var errAlreadyStopped = fmt.Errorf("already stopped") - -func (mc *mockCollector) stop() error { - var err = errAlreadyStopped - mc.stopOnce.Do(func() { - err = nil - if mc.stopFunc != nil { - mc.stopFunc() - } - }) - // Give it sometime to shutdown. - <-time.After(160 * time.Millisecond) - - // Wait for services to finish reading/writing. - var wg sync.WaitGroup - wg.Add(1) - go func() { - // Getting the lock ensures the traceSvc is done flushing. - mc.traceSvc.mu.Lock() - defer mc.traceSvc.mu.Unlock() - wg.Done() - }() - wg.Add(1) - go func() { - // Getting the lock ensures the metricSvc is done flushing. - mc.metricSvc.mu.Lock() - defer mc.metricSvc.mu.Unlock() - wg.Done() - }() - wg.Wait() - return err -} - -func (mc *mockCollector) Stop() error { - return mc.stop() -} - -func (mc *mockCollector) getSpans() []*tracepb.Span { - return mc.traceSvc.getSpans() -} - -func (mc *mockCollector) getResourceSpans() []*tracepb.ResourceSpans { - return mc.traceSvc.getResourceSpans() -} - -func (mc *mockCollector) GetResourceSpans() []*tracepb.ResourceSpans { - return mc.getResourceSpans() -} - -func (mc *mockCollector) getHeaders() metadata.MD { - return mc.traceSvc.getHeaders() -} - -func (mc *mockCollector) getMetrics() []*metricpb.Metric { - return mc.metricSvc.getMetrics() -} - -func (mc *mockCollector) GetMetrics() []*metricpb.Metric { - return mc.getMetrics() -} - -// runMockCollector is a helper function to create a mock Collector -func runMockCollector(t *testing.T) *mockCollector { - return runMockCollectorAtEndpoint(t, "localhost:0") -} - -func runMockCollectorAtEndpoint(t *testing.T, endpoint string) *mockCollector { - return runMockCollectorWithConfig(t, &mockConfig{endpoint: endpoint}) -} - -func runMockCollectorWithConfig(t *testing.T, mockConfig *mockConfig) *mockCollector { - ln, err := net.Listen("tcp", mockConfig.endpoint) - if err != nil { - t.Fatalf("Failed to get an endpoint: %v", err) - } - - srv := grpc.NewServer() - mc := makeMockCollector(t, mockConfig) - collectortracepb.RegisterTraceServiceServer(srv, mc.traceSvc) - collectormetricpb.RegisterMetricsServiceServer(srv, mc.metricSvc) - mc.ln = newListener(ln) - go func() { - _ = srv.Serve((net.Listener)(mc.ln)) - }() - - mc.endpoint = ln.Addr().String() - // srv.Stop calls Close on mc.ln. - mc.stopFunc = srv.Stop - - return mc -} - -type listener struct { - closeOnce sync.Once - wrapped net.Listener - C chan struct{} -} - -func newListener(wrapped net.Listener) *listener { - return &listener{ - wrapped: wrapped, - C: make(chan struct{}, 1), - } -} - -func (l *listener) Close() error { return l.wrapped.Close() } - -func (l *listener) Addr() net.Addr { return l.wrapped.Addr() } - -// Accept waits for and returns the next connection to the listener. It will -// send a signal on l.C that a connection has been made before returning. -func (l *listener) Accept() (net.Conn, error) { - conn, err := l.wrapped.Accept() - if err != nil { - // Go 1.16 exported net.ErrClosed that could clean up this check, but to - // remain backwards compatible with previous versions of Go that we - // support the following string evaluation is used instead to keep in line - // with the previously recommended way to check this: - // https://github.com/golang/go/issues/4373#issuecomment-353076799 - if strings.Contains(err.Error(), "use of closed network connection") { - // If the listener has been closed, do not allow callers of - // WaitForConn to wait for a connection that will never come. - l.closeOnce.Do(func() { close(l.C) }) - } - return conn, err - } - - select { - case l.C <- struct{}{}: - default: - // If C is full, assume nobody is listening and move on. - } - return conn, nil -} - -// WaitForConn will wait indefintely for a connection to be estabilished with -// the listener before returning. -func (l *listener) WaitForConn() { - for { - select { - case <-l.C: - return - default: - runtime.Gosched() - } - } -} diff --git a/exporters/otlp/otlpgrpc/options.go b/exporters/otlp/otlpgrpc/options.go deleted file mode 100644 index 1b685e7a4..000000000 --- a/exporters/otlp/otlpgrpc/options.go +++ /dev/null @@ -1,219 +0,0 @@ -// Copyright The OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package otlpgrpc - -import ( - "fmt" - "time" - - "go.opentelemetry.io/otel" - "go.opentelemetry.io/otel/exporters/otlp" - "go.opentelemetry.io/otel/exporters/otlp/internal/otlpconfig" - - "google.golang.org/grpc" - "google.golang.org/grpc/credentials" -) - -// Option applies an option to the gRPC driver. -type Option interface { - applyGRPCOption(*otlpconfig.Config) -} - -type wrappedOption struct { - otlpconfig.GRPCOption -} - -func (w wrappedOption) applyGRPCOption(cfg *otlpconfig.Config) { - w.ApplyGRPCOption(cfg) -} - -// WithInsecure disables client transport security for the exporter's gRPC connection -// just like grpc.WithInsecure() https://pkg.go.dev/google.golang.org/grpc#WithInsecure -// does. Note, by default, client security is required unless WithInsecure is used. -func WithInsecure() Option { - return wrappedOption{otlpconfig.WithInsecure()} -} - -// WithTracesInsecure disables client transport security for the traces exporter's gRPC connection -// just like grpc.WithInsecure() https://pkg.go.dev/google.golang.org/grpc#WithInsecure -// does. Note, by default, client security is required unless WithInsecure is used. -func WithTracesInsecure() Option { - return wrappedOption{otlpconfig.WithInsecureTraces()} -} - -// WithInsecureMetrics disables client transport security for the metrics exporter's gRPC connection -// just like grpc.WithInsecure() https://pkg.go.dev/google.golang.org/grpc#WithInsecure -// does. Note, by default, client security is required unless WithInsecure is used. -func WithInsecureMetrics() Option { - return wrappedOption{otlpconfig.WithInsecureMetrics()} -} - -// WithEndpoint allows one to set the endpoint that the exporter will -// connect to the collector on. If unset, it will instead try to use -// connect to DefaultCollectorHost:DefaultCollectorPort. -func WithEndpoint(endpoint string) Option { - return wrappedOption{otlpconfig.WithEndpoint(endpoint)} -} - -// WithTracesEndpoint allows one to set the traces endpoint that the exporter will -// connect to the collector on. If unset, it will instead try to use -// connect to DefaultCollectorHost:DefaultCollectorPort. -func WithTracesEndpoint(endpoint string) Option { - return wrappedOption{otlpconfig.WithTracesEndpoint(endpoint)} -} - -// WithMetricsEndpoint allows one to set the metrics endpoint that the exporter will -// connect to the collector on. If unset, it will instead try to use -// connect to DefaultCollectorHost:DefaultCollectorPort. -func WithMetricsEndpoint(endpoint string) Option { - return wrappedOption{otlpconfig.WithMetricsEndpoint(endpoint)} -} - -// WithReconnectionPeriod allows one to set the delay between next connection attempt -// after failing to connect with the collector. -func WithReconnectionPeriod(rp time.Duration) Option { - return wrappedOption{otlpconfig.NewGRPCOption(func(cfg *otlpconfig.Config) { - cfg.ReconnectionPeriod = rp - })} -} - -func compressorToCompression(compressor string) otlp.Compression { - switch compressor { - case "gzip": - return otlp.GzipCompression - } - - otel.Handle(fmt.Errorf("invalid compression type: '%s', using no compression as default", compressor)) - return otlp.NoCompression -} - -// WithCompressor will set the compressor for the gRPC client to use when sending requests. -// It is the responsibility of the caller to ensure that the compressor set has been registered -// with google.golang.org/grpc/encoding. This can be done by encoding.RegisterCompressor. Some -// compressors auto-register on import, such as gzip, which can be registered by calling -// `import _ "google.golang.org/grpc/encoding/gzip"`. -func WithCompressor(compressor string) Option { - return wrappedOption{otlpconfig.WithCompression(compressorToCompression(compressor))} -} - -// WithTracesCompression will set the compressor for the gRPC client to use when sending traces requests. -// It is the responsibility of the caller to ensure that the compressor set has been registered -// with google.golang.org/grpc/encoding. This can be done by encoding.RegisterCompressor. Some -// compressors auto-register on import, such as gzip, which can be registered by calling -// `import _ "google.golang.org/grpc/encoding/gzip"`. -func WithTracesCompression(compressor string) Option { - return wrappedOption{otlpconfig.WithTracesCompression(compressorToCompression(compressor))} -} - -// WithMetricsCompression will set the compressor for the gRPC client to use when sending metrics requests. -// It is the responsibility of the caller to ensure that the compressor set has been registered -// with google.golang.org/grpc/encoding. This can be done by encoding.RegisterCompressor. Some -// compressors auto-register on import, such as gzip, which can be registered by calling -// `import _ "google.golang.org/grpc/encoding/gzip"`. -func WithMetricsCompression(compressor string) Option { - return wrappedOption{otlpconfig.WithMetricsCompression(compressorToCompression(compressor))} -} - -// WithHeaders will send the provided headers with gRPC requests. -func WithHeaders(headers map[string]string) Option { - return wrappedOption{otlpconfig.WithHeaders(headers)} -} - -// WithTracesHeaders will send the provided headers with gRPC traces requests. -func WithTracesHeaders(headers map[string]string) Option { - return wrappedOption{otlpconfig.WithTracesHeaders(headers)} -} - -// WithMetricsHeaders will send the provided headers with gRPC metrics requests. -func WithMetricsHeaders(headers map[string]string) Option { - return wrappedOption{otlpconfig.WithMetricsHeaders(headers)} -} - -// WithTLSCredentials allows the connection to use TLS credentials -// when talking to the server. It takes in grpc.TransportCredentials instead -// of say a Certificate file or a tls.Certificate, because the retrieving of -// these credentials can be done in many ways e.g. plain file, in code tls.Config -// or by certificate rotation, so it is up to the caller to decide what to use. -func WithTLSCredentials(creds credentials.TransportCredentials) Option { - return wrappedOption{otlpconfig.NewGRPCOption(func(cfg *otlpconfig.Config) { - cfg.Traces.GRPCCredentials = creds - cfg.Metrics.GRPCCredentials = creds - })} -} - -// WithTracesTLSCredentials allows the connection to use TLS credentials -// when talking to the traces server. It takes in grpc.TransportCredentials instead -// of say a Certificate file or a tls.Certificate, because the retrieving of -// these credentials can be done in many ways e.g. plain file, in code tls.Config -// or by certificate rotation, so it is up to the caller to decide what to use. -func WithTracesTLSCredentials(creds credentials.TransportCredentials) Option { - return wrappedOption{otlpconfig.NewGRPCOption(func(cfg *otlpconfig.Config) { - cfg.Traces.GRPCCredentials = creds - })} -} - -// WithMetricsTLSCredentials allows the connection to use TLS credentials -// when talking to the metrics server. It takes in grpc.TransportCredentials instead -// of say a Certificate file or a tls.Certificate, because the retrieving of -// these credentials can be done in many ways e.g. plain file, in code tls.Config -// or by certificate rotation, so it is up to the caller to decide what to use. -func WithMetricsTLSCredentials(creds credentials.TransportCredentials) Option { - return wrappedOption{otlpconfig.NewGRPCOption(func(cfg *otlpconfig.Config) { - cfg.Metrics.GRPCCredentials = creds - })} -} - -// WithServiceConfig defines the default gRPC service config used. -func WithServiceConfig(serviceConfig string) Option { - return wrappedOption{otlpconfig.NewGRPCOption(func(cfg *otlpconfig.Config) { - cfg.ServiceConfig = serviceConfig - })} -} - -// WithDialOption opens support to any grpc.DialOption to be used. If it conflicts -// with some other configuration the GRPC specified via the collector the ones here will -// take preference since they are set last. -func WithDialOption(opts ...grpc.DialOption) Option { - return wrappedOption{otlpconfig.NewGRPCOption(func(cfg *otlpconfig.Config) { - cfg.DialOptions = opts - })} -} - -// WithTimeout tells the driver the max waiting time for the backend to process -// each spans or metrics batch. If unset, the default will be 10 seconds. -func WithTimeout(duration time.Duration) Option { - return wrappedOption{otlpconfig.WithTimeout(duration)} -} - -// WithTracesTimeout tells the driver the max waiting time for the backend to process -// each spans batch. If unset, the default will be 10 seconds. -func WithTracesTimeout(duration time.Duration) Option { - return wrappedOption{otlpconfig.WithTracesTimeout(duration)} -} - -// WithMetricsTimeout tells the driver the max waiting time for the backend to process -// each metrics batch. If unset, the default will be 10 seconds. -func WithMetricsTimeout(duration time.Duration) Option { - return wrappedOption{otlpconfig.WithMetricsTimeout(duration)} -} - -// WithRetry configures the retry policy for transient errors that may occurs when -// exporting metrics or traces. An exponential back-off algorithm is used to -// ensure endpoints are not overwhelmed with retries. If unset, the default -// retry policy will retry after 5 seconds and increase exponentially after each -// error for a total of 1 minute. -func WithRetry(settings otlp.RetrySettings) Option { - return wrappedOption{otlpconfig.WithRetry(settings)} -} diff --git a/exporters/otlp/otlpgrpc/otlp_integration_test.go b/exporters/otlp/otlpgrpc/otlp_integration_test.go deleted file mode 100644 index 80fcc5bcc..000000000 --- a/exporters/otlp/otlpgrpc/otlp_integration_test.go +++ /dev/null @@ -1,914 +0,0 @@ -// Copyright The OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package otlpgrpc_test - -import ( - "context" - "fmt" - "net" - "strings" - "testing" - "time" - - "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/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "google.golang.org/grpc" - "google.golang.org/grpc/encoding/gzip" - - "go.opentelemetry.io/otel/attribute" - "go.opentelemetry.io/otel/exporters/otlp" - "go.opentelemetry.io/otel/exporters/otlp/internal/otlptest" - "go.opentelemetry.io/otel/exporters/otlp/otlpgrpc" - sdktrace "go.opentelemetry.io/otel/sdk/trace" - "go.opentelemetry.io/otel/sdk/trace/tracetest" - commonpb "go.opentelemetry.io/proto/otlp/common/v1" -) - -var roSpans = tracetest.SpanStubs{{Name: "Span 0"}}.Snapshots() - -func TestNew_endToEnd(t *testing.T) { - tests := []struct { - name string - additionalOpts []otlpgrpc.Option - }{ - { - name: "StandardExporter", - }, - { - name: "WithCompressor", - additionalOpts: []otlpgrpc.Option{ - otlpgrpc.WithCompressor(gzip.Name), - }, - }, - { - name: "WithServiceConfig", - additionalOpts: []otlpgrpc.Option{ - otlpgrpc.WithServiceConfig("{}"), - }, - }, - { - name: "WithDialOptions", - additionalOpts: []otlpgrpc.Option{ - otlpgrpc.WithDialOption(grpc.WithBlock()), - }, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - newExporterEndToEndTest(t, test.additionalOpts) - }) - } -} - -func newGRPCExporter(t *testing.T, ctx context.Context, endpoint string, additionalOpts ...otlpgrpc.Option) *otlp.Exporter { - opts := []otlpgrpc.Option{ - otlpgrpc.WithInsecure(), - otlpgrpc.WithEndpoint(endpoint), - otlpgrpc.WithReconnectionPeriod(50 * time.Millisecond), - } - - opts = append(opts, additionalOpts...) - driver := otlpgrpc.NewDriver(opts...) - exp, err := otlp.New(ctx, driver) - if err != nil { - t.Fatalf("failed to create a new collector exporter: %v", err) - } - return exp -} - -func newExporterEndToEndTest(t *testing.T, additionalOpts []otlpgrpc.Option) { - mc := runMockCollectorAtEndpoint(t, "localhost:56561") - - defer func() { - _ = mc.stop() - }() - - <-time.After(5 * time.Millisecond) - - ctx := context.Background() - exp := newGRPCExporter(t, ctx, mc.endpoint, additionalOpts...) - defer func() { - ctx, cancel := context.WithTimeout(ctx, 10*time.Second) - defer cancel() - if err := exp.Shutdown(ctx); err != nil { - panic(err) - } - }() - - otlptest.RunEndToEndTest(ctx, t, exp, mc, mc) -} - -func TestNew_invokeStartThenStopManyTimes(t *testing.T) { - mc := runMockCollector(t) - defer func() { - _ = mc.stop() - }() - - ctx := context.Background() - exp := newGRPCExporter(t, ctx, mc.endpoint) - defer func() { - if err := exp.Shutdown(ctx); err != nil { - panic(err) - } - }() - - // Invoke Start numerous times, should return errAlreadyStarted - for i := 0; i < 10; i++ { - if err := exp.Start(ctx); err == nil || !strings.Contains(err.Error(), "already started") { - t.Fatalf("#%d unexpected Start error: %v", i, err) - } - } - - if err := exp.Shutdown(ctx); err != nil { - t.Fatalf("failed to Shutdown the exporter: %v", err) - } - // Invoke Shutdown numerous times - for i := 0; i < 10; i++ { - if err := exp.Shutdown(ctx); err != nil { - t.Fatalf(`#%d got error (%v) expected none`, i, err) - } - } -} - -func TestNew_collectorConnectionDiesThenReconnectsWhenInRestMode(t *testing.T) { - mc := runMockCollector(t) - - reconnectionPeriod := 20 * time.Millisecond - ctx := context.Background() - exp := newGRPCExporter(t, ctx, mc.endpoint, - otlpgrpc.WithRetry(otlp.RetrySettings{Enabled: false}), - otlpgrpc.WithReconnectionPeriod(reconnectionPeriod)) - defer func() { require.NoError(t, exp.Shutdown(ctx)) }() - - // Wait for a connection. - mc.ln.WaitForConn() - - // We'll now stop the collector right away to simulate a connection - // dying in the midst of communication or even not existing before. - require.NoError(t, mc.stop()) - - // first export, it will send disconnected message to the channel on export failure, - // trigger almost immediate reconnection - require.Error(t, exp.ExportSpans(ctx, roSpans)) - - // second export, it will detect connection issue, change state of exporter to disconnected and - // send message to disconnected channel but this time reconnection gouroutine will be in (rest mode, not listening to the disconnected channel) - require.Error(t, exp.ExportSpans(ctx, roSpans)) - - // as a result we have exporter in disconnected state waiting for disconnection message to reconnect - - // resurrect collector - nmc := runMockCollectorAtEndpoint(t, mc.endpoint) - - // make sure reconnection loop hits beginning and goes back to waiting mode - // after hitting beginning of the loop it should reconnect - nmc.ln.WaitForConn() - - n := 10 - for i := 0; i < n; i++ { - // when disconnected exp.ExportSpans doesnt send disconnected messages again - // it just quits and return last connection error - require.NoError(t, exp.ExportSpans(ctx, roSpans)) - } - - nmaSpans := nmc.getSpans() - - // Expecting 10 spans that were sampled, given that - if g, w := len(nmaSpans), n; g != w { - t.Fatalf("Connected collector: spans: got %d want %d", g, w) - } - - dSpans := mc.getSpans() - // Expecting 0 spans to have been received by the original but now dead collector - if g, w := len(dSpans), 0; g != w { - t.Fatalf("Disconnected collector: spans: got %d want %d", g, w) - } - - require.NoError(t, nmc.Stop()) -} - -func TestExporterExportFailureAndRecoveryModes(t *testing.T) { - tts := []struct { - name string - errors []error - rs otlp.RetrySettings - fn func(t *testing.T, ctx context.Context, exp *otlp.Exporter, mc *mockCollector) - opts []otlpgrpc.Option - }{ - { - name: "Do not retry if succeeded", - fn: func(t *testing.T, ctx context.Context, exp *otlp.Exporter, mc *mockCollector) { - require.NoError(t, exp.ExportSpans(ctx, roSpans)) - - span := mc.getSpans() - - require.Len(t, span, 1) - require.Equal(t, 1, mc.traceSvc.requests, "trace service must receive 1 success request.") - }, - }, - { - name: "Do not retry if 'error' is ok", - errors: []error{ - status.Error(codes.OK, ""), - }, - fn: func(t *testing.T, ctx context.Context, exp *otlp.Exporter, mc *mockCollector) { - require.NoError(t, exp.ExportSpans(ctx, roSpans)) - - span := mc.getSpans() - - require.Len(t, span, 0) - require.Equal(t, 1, mc.traceSvc.requests, "trace service must receive 1 error OK request.") - }, - }, - { - name: "Fail three times and succeed", - rs: otlp.RetrySettings{ - Enabled: true, - MaxElapsedTime: 300 * time.Millisecond, - InitialInterval: 2 * time.Millisecond, - MaxInterval: 10 * time.Millisecond, - }, - errors: []error{ - status.Error(codes.Unavailable, "backend under pressure"), - status.Error(codes.Unavailable, "backend under pressure"), - status.Error(codes.Unavailable, "backend under pressure"), - }, - fn: func(t *testing.T, ctx context.Context, exp *otlp.Exporter, mc *mockCollector) { - require.NoError(t, exp.ExportSpans(ctx, roSpans)) - - span := mc.getSpans() - - require.Len(t, span, 1) - require.Equal(t, 4, mc.traceSvc.requests, "trace service must receive 3 failure requests and 1 success request.") - }, - }, - { - name: "Permanent error should not be retried", - rs: otlp.RetrySettings{ - Enabled: true, - MaxElapsedTime: 300 * time.Millisecond, - InitialInterval: 2 * time.Millisecond, - MaxInterval: 10 * time.Millisecond, - }, - errors: []error{ - status.Error(codes.InvalidArgument, "invalid arguments"), - }, - fn: func(t *testing.T, ctx context.Context, exp *otlp.Exporter, mc *mockCollector) { - require.Error(t, exp.ExportSpans(ctx, roSpans)) - - span := mc.getSpans() - - require.Len(t, span, 0) - require.Equal(t, 1, mc.traceSvc.requests, "trace service must receive 1 error requests.") - }, - }, - { - name: "Test all transient errors and succeed", - rs: otlp.RetrySettings{ - Enabled: true, - MaxElapsedTime: 500 * time.Millisecond, - InitialInterval: 1 * time.Millisecond, - MaxInterval: 2 * time.Millisecond, - }, - errors: []error{ - status.Error(codes.Canceled, ""), - status.Error(codes.DeadlineExceeded, ""), - status.Error(codes.ResourceExhausted, ""), - status.Error(codes.Aborted, ""), - status.Error(codes.OutOfRange, ""), - status.Error(codes.Unavailable, ""), - status.Error(codes.DataLoss, ""), - }, - fn: func(t *testing.T, ctx context.Context, exp *otlp.Exporter, mc *mockCollector) { - require.NoError(t, exp.ExportSpans(ctx, roSpans)) - - span := mc.getSpans() - - require.Len(t, span, 1) - require.Equal(t, 8, mc.traceSvc.requests, "trace service must receive 9 failure requests and 1 success request.") - }, - }, - { - name: "Retry should honor server throttling", - rs: otlp.RetrySettings{ - Enabled: true, - MaxElapsedTime: time.Minute, - InitialInterval: time.Nanosecond, - MaxInterval: time.Nanosecond, - }, - opts: []otlpgrpc.Option{ - otlpgrpc.WithTimeout(time.Millisecond * 100), - }, - errors: []error{ - newThrottlingError(codes.ResourceExhausted, time.Second*30), - }, - fn: func(t *testing.T, ctx context.Context, exp *otlp.Exporter, mc *mockCollector) { - err := exp.ExportSpans(ctx, roSpans) - require.Error(t, err) - require.Equal(t, "context deadline exceeded", err.Error()) - - span := mc.getSpans() - - require.Len(t, span, 0) - require.Equal(t, 1, mc.traceSvc.requests, "trace service must receive 1 failure requests and 1 success request.") - }, - }, - { - name: "Retry should fail if server throttling is higher than the MaxElapsedTime", - rs: otlp.RetrySettings{ - Enabled: true, - MaxElapsedTime: time.Millisecond * 100, - InitialInterval: time.Nanosecond, - MaxInterval: time.Nanosecond, - }, - errors: []error{ - newThrottlingError(codes.ResourceExhausted, time.Minute), - }, - fn: func(t *testing.T, ctx context.Context, exp *otlp.Exporter, mc *mockCollector) { - err := exp.ExportSpans(ctx, roSpans) - require.Error(t, err) - require.Equal(t, "max elapsed time expired when respecting server throttle: rpc error: code = ResourceExhausted desc = ", err.Error()) - - span := mc.getSpans() - - require.Len(t, span, 0) - require.Equal(t, 1, mc.traceSvc.requests, "trace service must receive 1 failure requests and 1 success request.") - }, - }, - { - name: "Retry stops if takes too long", - rs: otlp.RetrySettings{ - Enabled: true, - MaxElapsedTime: time.Millisecond * 100, - InitialInterval: time.Millisecond * 50, - MaxInterval: time.Millisecond * 50, - }, - errors: []error{ - status.Error(codes.Unavailable, "unavailable"), - status.Error(codes.Unavailable, "unavailable"), - status.Error(codes.Unavailable, "unavailable"), - status.Error(codes.Unavailable, "unavailable"), - status.Error(codes.Unavailable, "unavailable"), - status.Error(codes.Unavailable, "unavailable"), - }, - fn: func(t *testing.T, ctx context.Context, exp *otlp.Exporter, mc *mockCollector) { - err := exp.ExportSpans(ctx, roSpans) - require.Error(t, err) - - require.Equal(t, "max elapsed time expired: rpc error: code = Unavailable desc = unavailable", err.Error()) - - span := mc.getSpans() - - require.Len(t, span, 0) - require.LessOrEqual(t, 1, mc.traceSvc.requests, "trace service must receive at least 1 failure requests.") - }, - }, - { - name: "Disabled retry", - rs: otlp.RetrySettings{ - Enabled: false, - }, - errors: []error{ - status.Error(codes.Unavailable, "unavailable"), - }, - fn: func(t *testing.T, ctx context.Context, exp *otlp.Exporter, mc *mockCollector) { - err := exp.ExportSpans(ctx, roSpans) - require.Error(t, err) - - require.Equal(t, "rpc error: code = Unavailable desc = unavailable", err.Error()) - - span := mc.getSpans() - - require.Len(t, span, 0) - require.Equal(t, 1, mc.traceSvc.requests, "trace service must receive 1 failure requests.") - }, - }, - } - - for _, tt := range tts { - t.Run(tt.name, func(t *testing.T) { - ctx := context.Background() - - mc := runMockCollectorWithConfig(t, &mockConfig{ - errors: tt.errors, - }) - - opts := []otlpgrpc.Option{ - otlpgrpc.WithRetry(tt.rs), - } - - if len(tt.opts) != 0 { - opts = append(opts, tt.opts...) - } - - exp := newGRPCExporter(t, ctx, mc.endpoint, opts...) - - tt.fn(t, ctx, exp, mc) - - require.NoError(t, mc.Stop()) - require.NoError(t, exp.Shutdown(ctx)) - }) - } - -} - -func TestPermanentErrorsShouldNotBeRetried(t *testing.T) { - permanentErrors := []*status.Status{ - status.New(codes.Unknown, "Unknown"), - status.New(codes.InvalidArgument, "InvalidArgument"), - status.New(codes.NotFound, "NotFound"), - status.New(codes.AlreadyExists, "AlreadyExists"), - status.New(codes.FailedPrecondition, "FailedPrecondition"), - status.New(codes.Unimplemented, "Unimplemented"), - status.New(codes.Internal, "Internal"), - status.New(codes.PermissionDenied, ""), - status.New(codes.Unauthenticated, ""), - } - - for _, sts := range permanentErrors { - t.Run(sts.Code().String(), func(t *testing.T) { - ctx := context.Background() - - mc := runMockCollectorWithConfig(t, &mockConfig{ - errors: []error{sts.Err()}, - }) - - exp := newGRPCExporter(t, ctx, mc.endpoint) - - err := exp.ExportSpans(ctx, roSpans) - require.Error(t, err) - require.Len(t, mc.getSpans(), 0) - require.Equal(t, 1, mc.traceSvc.requests, "trace service must receive 1 permanent error requests.") - - require.NoError(t, mc.Stop()) - require.NoError(t, exp.Shutdown(ctx)) - }) - } -} - -func newThrottlingError(code codes.Code, duration time.Duration) error { - s := status.New(code, "") - - s, _ = s.WithDetails(&errdetails.RetryInfo{RetryDelay: durationpb.New(duration)}) - - return s.Err() -} - -func TestNew_collectorConnectionDiesThenReconnects(t *testing.T) { - mc := runMockCollector(t) - - reconnectionPeriod := 50 * time.Millisecond - ctx := context.Background() - exp := newGRPCExporter(t, ctx, mc.endpoint, - otlpgrpc.WithRetry(otlp.RetrySettings{Enabled: false}), - otlpgrpc.WithReconnectionPeriod(reconnectionPeriod)) - defer func() { require.NoError(t, exp.Shutdown(ctx)) }() - - mc.ln.WaitForConn() - - // We'll now stop the collector right away to simulate a connection - // dying in the midst of communication or even not existing before. - require.NoError(t, mc.stop()) - - // In the test below, we'll stop the collector many times, - // while exporting traces and test to ensure that we can - // reconnect. - for j := 0; j < 3; j++ { - - // No endpoint up. - require.Error(t, exp.ExportSpans(ctx, roSpans)) - - // Now resurrect the collector by making a new one but reusing the - // old endpoint, and the collector should reconnect automatically. - nmc := runMockCollectorAtEndpoint(t, mc.endpoint) - - // Give the exporter sometime to reconnect - nmc.ln.WaitForConn() - - n := 10 - for i := 0; i < n; i++ { - require.NoError(t, exp.ExportSpans(ctx, roSpans)) - } - - nmaSpans := nmc.getSpans() - // Expecting 10 spans that were sampled, given that - if g, w := len(nmaSpans), n; g != w { - t.Fatalf("Round #%d: Connected collector: spans: got %d want %d", j, g, w) - } - - dSpans := mc.getSpans() - // Expecting 0 spans to have been received by the original but now dead collector - if g, w := len(dSpans), 0; g != w { - t.Fatalf("Round #%d: Disconnected collector: spans: got %d want %d", j, g, w) - } - - // Disconnect for the next try. - require.NoError(t, nmc.stop()) - } -} - -// This test takes a long time to run: to skip it, run tests using: -short -func TestNew_collectorOnBadConnection(t *testing.T) { - if testing.Short() { - t.Skipf("Skipping this long running test") - } - - ln, err := net.Listen("tcp", "localhost:0") - if err != nil { - t.Fatalf("Failed to grab an available port: %v", err) - } - // Firstly close the "collector's" channel: optimistically this endpoint won't get reused ASAP - // However, our goal of closing it is to simulate an unavailable connection - _ = ln.Close() - - _, collectorPortStr, _ := net.SplitHostPort(ln.Addr().String()) - - endpoint := fmt.Sprintf("localhost:%s", collectorPortStr) - ctx := context.Background() - exp := newGRPCExporter(t, ctx, endpoint) - _ = exp.Shutdown(ctx) -} - -func TestNew_withEndpoint(t *testing.T) { - mc := runMockCollector(t) - defer func() { - _ = mc.stop() - }() - - ctx := context.Background() - exp := newGRPCExporter(t, ctx, mc.endpoint) - _ = exp.Shutdown(ctx) -} - -func TestNew_withHeaders(t *testing.T) { - mc := runMockCollector(t) - defer func() { - _ = mc.stop() - }() - - ctx := context.Background() - exp := newGRPCExporter(t, ctx, mc.endpoint, - otlpgrpc.WithHeaders(map[string]string{"header1": "value1"})) - require.NoError(t, exp.ExportSpans(ctx, roSpans)) - - defer func() { - _ = exp.Shutdown(ctx) - }() - - headers := mc.getHeaders() - require.Len(t, headers.Get("header1"), 1) - assert.Equal(t, "value1", headers.Get("header1")[0]) -} - -func TestNew_WithTimeout(t *testing.T) { - tts := []struct { - name string - fn func(exp *otlp.Exporter) error - timeout time.Duration - metrics int - spans int - code codes.Code - delay bool - }{ - { - name: "Timeout Spans", - fn: func(exp *otlp.Exporter) error { - return exp.ExportSpans(context.Background(), roSpans) - }, - timeout: time.Millisecond * 100, - code: codes.DeadlineExceeded, - delay: true, - }, - { - name: "Timeout Metrics", - fn: func(exp *otlp.Exporter) error { - return exp.Export(context.Background(), otlptest.OneRecordCheckpointSet{}) - }, - timeout: time.Millisecond * 100, - code: codes.DeadlineExceeded, - delay: true, - }, - - { - name: "No Timeout Spans", - fn: func(exp *otlp.Exporter) error { - return exp.ExportSpans(context.Background(), roSpans) - }, - timeout: time.Minute, - spans: 1, - code: codes.OK, - }, - { - name: "No Timeout Metrics", - fn: func(exp *otlp.Exporter) error { - return exp.Export(context.Background(), otlptest.OneRecordCheckpointSet{}) - }, - timeout: time.Minute, - metrics: 1, - code: codes.OK, - }, - } - - for _, tt := range tts { - t.Run(tt.name, func(t *testing.T) { - - mc := runMockCollector(t) - if tt.delay { - mc.traceSvc.delay = time.Second * 10 - mc.metricSvc.delay = time.Second * 10 - } - defer func() { - _ = mc.stop() - }() - - ctx := context.Background() - exp := newGRPCExporter(t, ctx, mc.endpoint, otlpgrpc.WithTimeout(tt.timeout), otlpgrpc.WithRetry(otlp.RetrySettings{Enabled: false})) - defer func() { - _ = exp.Shutdown(ctx) - }() - - err := tt.fn(exp) - - if tt.code == codes.OK { - require.NoError(t, err) - } else { - require.Error(t, err) - } - - s := status.Convert(err) - require.Equal(t, tt.code, s.Code()) - - require.Len(t, mc.getSpans(), tt.spans) - require.Len(t, mc.getMetrics(), tt.metrics) - }) - } -} - -func TestNew_withInvalidSecurityConfiguration(t *testing.T) { - mc := runMockCollector(t) - defer func() { - _ = mc.stop() - }() - - ctx := context.Background() - driver := otlpgrpc.NewDriver(otlpgrpc.WithEndpoint(mc.endpoint)) - exp, err := otlp.New(ctx, driver) - if err != nil { - t.Fatalf("failed to create a new collector exporter: %v", err) - } - - err = exp.ExportSpans(ctx, roSpans) - - expectedErr := fmt.Sprintf("traces exporter is disconnected from the server %s: grpc: no transport security set (use grpc.WithInsecure() explicitly or set credentials)", mc.endpoint) - - require.Error(t, err) - require.Equal(t, expectedErr, err.Error()) - - defer func() { - _ = exp.Shutdown(ctx) - }() -} - -func TestNew_withMultipleAttributeTypes(t *testing.T) { - mc := runMockCollector(t) - - defer func() { - _ = mc.stop() - }() - - <-time.After(5 * time.Millisecond) - - ctx := context.Background() - exp := newGRPCExporter(t, ctx, mc.endpoint) - - defer func() { - _ = exp.Shutdown(ctx) - }() - - tp := sdktrace.NewTracerProvider( - sdktrace.WithSampler(sdktrace.AlwaysSample()), - sdktrace.WithBatcher( - exp, - // add following two options to ensure flush - sdktrace.WithBatchTimeout(5*time.Second), - sdktrace.WithMaxExportBatchSize(10), - ), - ) - defer func() { _ = tp.Shutdown(ctx) }() - - tr := tp.Tracer("test-tracer") - testKvs := []attribute.KeyValue{ - attribute.Int("Int", 1), - attribute.Int64("Int64", int64(3)), - attribute.Float64("Float64", 2.22), - attribute.Bool("Bool", true), - attribute.String("String", "test"), - } - _, span := tr.Start(ctx, "AlwaysSample") - span.SetAttributes(testKvs...) - span.End() - - // Flush and close. - func() { - ctx, cancel := context.WithTimeout(ctx, 10*time.Second) - defer cancel() - if err := tp.Shutdown(ctx); err != nil { - t.Fatalf("failed to shut down a tracer provider: %v", err) - } - }() - - // Wait >2 cycles. - <-time.After(40 * time.Millisecond) - - // Now shutdown the exporter - ctx, cancel := context.WithTimeout(ctx, 10*time.Second) - defer cancel() - if err := exp.Shutdown(ctx); err != nil { - t.Fatalf("failed to stop the exporter: %v", err) - } - - // Shutdown the collector too so that we can begin - // verification checks of expected data back. - _ = mc.stop() - - // Now verify that we only got one span - rss := mc.getSpans() - if got, want := len(rss), 1; got != want { - t.Fatalf("resource span count: got %d, want %d\n", got, want) - } - - expected := []*commonpb.KeyValue{ - { - Key: "Int", - Value: &commonpb.AnyValue{ - Value: &commonpb.AnyValue_IntValue{ - IntValue: 1, - }, - }, - }, - { - Key: "Int64", - Value: &commonpb.AnyValue{ - Value: &commonpb.AnyValue_IntValue{ - IntValue: 3, - }, - }, - }, - { - Key: "Float64", - Value: &commonpb.AnyValue{ - Value: &commonpb.AnyValue_DoubleValue{ - DoubleValue: 2.22, - }, - }, - }, - { - Key: "Bool", - Value: &commonpb.AnyValue{ - Value: &commonpb.AnyValue_BoolValue{ - BoolValue: true, - }, - }, - }, - { - Key: "String", - Value: &commonpb.AnyValue{ - Value: &commonpb.AnyValue_StringValue{ - StringValue: "test", - }, - }, - }, - } - - // Verify attributes - if !assert.Len(t, rss[0].Attributes, len(expected)) { - t.Fatalf("attributes count: got %d, want %d\n", len(rss[0].Attributes), len(expected)) - } - for i, actual := range rss[0].Attributes { - if a, ok := actual.Value.Value.(*commonpb.AnyValue_DoubleValue); ok { - e, ok := expected[i].Value.Value.(*commonpb.AnyValue_DoubleValue) - if !ok { - t.Errorf("expected AnyValue_DoubleValue, got %T", expected[i].Value.Value) - continue - } - if !assert.InDelta(t, e.DoubleValue, a.DoubleValue, 0.01) { - continue - } - e.DoubleValue = a.DoubleValue - } - assert.Equal(t, expected[i], actual) - } -} - -func TestDisconnected(t *testing.T) { - ctx := context.Background() - // The endpoint is whatever, we want to be disconnected. But we - // setting a blocking connection, so dialing to the invalid - // endpoint actually fails. - exp := newGRPCExporter(t, ctx, "invalid", - otlpgrpc.WithReconnectionPeriod(time.Hour), - otlpgrpc.WithDialOption( - grpc.WithBlock(), - grpc.FailOnNonTempDialError(true), - ), - ) - defer func() { - assert.NoError(t, exp.Shutdown(ctx)) - }() - - assert.Error(t, exp.Export(ctx, otlptest.OneRecordCheckpointSet{})) - assert.Error(t, exp.ExportSpans(ctx, otlptest.SingleReadOnlySpan())) -} - -func TestEmptyData(t *testing.T) { - mc := runMockCollectorAtEndpoint(t, "localhost:56561") - - defer func() { - _ = mc.stop() - }() - - <-time.After(5 * time.Millisecond) - - ctx := context.Background() - exp := newGRPCExporter(t, ctx, mc.endpoint) - defer func() { - assert.NoError(t, exp.Shutdown(ctx)) - }() - - assert.NoError(t, exp.ExportSpans(ctx, nil)) - assert.NoError(t, exp.Export(ctx, otlptest.EmptyCheckpointSet{})) -} - -func TestFailedMetricTransform(t *testing.T) { - mc := runMockCollectorAtEndpoint(t, "localhost:56561") - - defer func() { - _ = mc.stop() - }() - - <-time.After(5 * time.Millisecond) - - ctx := context.Background() - exp := newGRPCExporter(t, ctx, mc.endpoint) - defer func() { - assert.NoError(t, exp.Shutdown(ctx)) - }() - - assert.Error(t, exp.Export(ctx, otlptest.FailCheckpointSet{})) -} - -func TestMultiConnectionDriver(t *testing.T) { - mcTraces := runMockCollector(t) - mcMetrics := runMockCollector(t) - - defer func() { - _ = mcTraces.stop() - _ = mcMetrics.stop() - }() - - <-time.After(5 * time.Millisecond) - - commonOpts := []otlpgrpc.Option{ - otlpgrpc.WithInsecure(), - otlpgrpc.WithReconnectionPeriod(50 * time.Millisecond), - otlpgrpc.WithDialOption(grpc.WithBlock()), - } - optsTraces := append([]otlpgrpc.Option{ - otlpgrpc.WithEndpoint(mcTraces.endpoint), - }, commonOpts...) - optsMetrics := append([]otlpgrpc.Option{ - otlpgrpc.WithEndpoint(mcMetrics.endpoint), - }, commonOpts...) - - tracesDriver := otlpgrpc.NewDriver(optsTraces...) - metricsDriver := otlpgrpc.NewDriver(optsMetrics...) - driver := otlp.NewSplitDriver(otlp.WithMetricDriver(metricsDriver), otlp.WithTraceDriver(tracesDriver)) - ctx := context.Background() - exp, err := otlp.New(ctx, driver) - if err != nil { - t.Fatalf("failed to create a new collector exporter: %v", err) - } - defer func() { - assert.NoError(t, exp.Shutdown(ctx)) - }() - otlptest.RunEndToEndTest(ctx, t, exp, mcTraces, mcMetrics) -} diff --git a/exporters/otlp/otlphttp/certificate_test.go b/exporters/otlp/otlphttp/certificate_test.go deleted file mode 100644 index 04083232b..000000000 --- a/exporters/otlp/otlphttp/certificate_test.go +++ /dev/null @@ -1,92 +0,0 @@ -// Copyright The OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package otlphttp_test - -import ( - "bytes" - "crypto/ecdsa" - "crypto/elliptic" - cryptorand "crypto/rand" - "crypto/x509" - "crypto/x509/pkix" - "encoding/pem" - "math/big" - mathrand "math/rand" - "net" - "time" -) - -type mathRandReader struct{} - -func (mathRandReader) Read(p []byte) (n int, err error) { - return mathrand.Read(p) -} - -var randReader mathRandReader - -type pemCertificate struct { - Certificate []byte - PrivateKey []byte -} - -// Based on https://golang.org/src/crypto/tls/generate_cert.go, -// simplified and weakened. -func generateWeakCertificate() (*pemCertificate, error) { - priv, err := ecdsa.GenerateKey(elliptic.P256(), randReader) - if err != nil { - return nil, err - } - keyUsage := x509.KeyUsageDigitalSignature - notBefore := time.Now() - notAfter := notBefore.Add(time.Hour) - serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128) - serialNumber, err := cryptorand.Int(randReader, serialNumberLimit) - if err != nil { - return nil, err - } - template := x509.Certificate{ - SerialNumber: serialNumber, - Subject: pkix.Name{ - Organization: []string{"otel-go"}, - }, - NotBefore: notBefore, - NotAfter: notAfter, - KeyUsage: keyUsage, - ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, - BasicConstraintsValid: true, - DNSNames: []string{"localhost"}, - IPAddresses: []net.IP{net.IPv6loopback, net.IPv4(127, 0, 0, 1)}, - } - derBytes, err := x509.CreateCertificate(randReader, &template, &template, &priv.PublicKey, priv) - if err != nil { - return nil, err - } - certificateBuffer := new(bytes.Buffer) - if err := pem.Encode(certificateBuffer, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes}); err != nil { - return nil, err - } - privDERBytes, err := x509.MarshalPKCS8PrivateKey(priv) - if err != nil { - return nil, err - } - privBuffer := new(bytes.Buffer) - if err := pem.Encode(privBuffer, &pem.Block{Type: "PRIVATE KEY", Bytes: privDERBytes}); err != nil { - return nil, err - } - return &pemCertificate{ - Certificate: certificateBuffer.Bytes(), - PrivateKey: privBuffer.Bytes(), - }, nil -} diff --git a/exporters/otlp/otlphttp/doc.go b/exporters/otlp/otlphttp/doc.go deleted file mode 100644 index ee72adaee..000000000 --- a/exporters/otlp/otlphttp/doc.go +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright The OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -/* -Package otlphttp implements a protocol driver that sends traces and -metrics to the collector using HTTP with binary protobuf payloads. - -This package is currently in a pre-GA phase. Backwards incompatible -changes may be introduced in subsequent minor version releases as we -work to track the evolving OpenTelemetry specification and user -feedback. -*/ -package otlphttp // import "go.opentelemetry.io/otel/exporters/otlp/otlphttp" diff --git a/exporters/otlp/otlphttp/driver.go b/exporters/otlp/otlphttp/driver.go deleted file mode 100644 index e5c11297b..000000000 --- a/exporters/otlp/otlphttp/driver.go +++ /dev/null @@ -1,339 +0,0 @@ -// Copyright The OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package otlphttp - -import ( - "bytes" - "compress/gzip" - "context" - "fmt" - "io" - "io/ioutil" - "math/rand" - "net" - "net/http" - "path" - "strings" - "time" - - "go.opentelemetry.io/otel/exporters/otlp/internal/otlpconfig" - - jsonpb "google.golang.org/protobuf/encoding/protojson" - "google.golang.org/protobuf/proto" - - "go.opentelemetry.io/otel" - "go.opentelemetry.io/otel/exporters/otlp" - "go.opentelemetry.io/otel/exporters/otlp/internal/transform" - metricsdk "go.opentelemetry.io/otel/sdk/export/metric" - tracesdk "go.opentelemetry.io/otel/sdk/trace" - colmetricspb "go.opentelemetry.io/proto/otlp/collector/metrics/v1" - coltracepb "go.opentelemetry.io/proto/otlp/collector/trace/v1" -) - -const contentTypeProto = "application/x-protobuf" -const contentTypeJSON = "application/json" - -// Keep it in sync with golang's DefaultTransport from net/http! We -// have our own copy to avoid handling a situation where the -// DefaultTransport is overwritten with some different implementation -// of http.RoundTripper or it's modified by other package. -var ourTransport = &http.Transport{ - Proxy: http.ProxyFromEnvironment, - DialContext: (&net.Dialer{ - Timeout: 30 * time.Second, - KeepAlive: 30 * time.Second, - DualStack: true, - }).DialContext, - ForceAttemptHTTP2: true, - MaxIdleConns: 100, - IdleConnTimeout: 90 * time.Second, - TLSHandshakeTimeout: 10 * time.Second, - ExpectContinueTimeout: 1 * time.Second, -} - -type driver struct { - metricsDriver signalDriver - tracesDriver signalDriver - cfg otlpconfig.Config - - stopCh chan struct{} -} - -type signalDriver struct { - name string - cfg otlpconfig.SignalConfig - generalCfg otlpconfig.Config - client *http.Client - stopCh chan struct{} -} - -var _ otlp.ProtocolDriver = (*driver)(nil) - -// NewDriver creates a new HTTP driver. -func NewDriver(opts ...Option) otlp.ProtocolDriver { - cfg := otlpconfig.NewDefaultConfig() - otlpconfig.ApplyHTTPEnvConfigs(&cfg) - for _, opt := range opts { - opt.applyHTTPOption(&cfg) - } - - for pathPtr, defaultPath := range map[*string]string{ - &cfg.Traces.URLPath: DefaultTracesPath, - &cfg.Metrics.URLPath: DefaultMetricsPath, - } { - tmp := strings.TrimSpace(*pathPtr) - if tmp == "" { - tmp = defaultPath - } else { - tmp = path.Clean(tmp) - if !path.IsAbs(tmp) { - tmp = fmt.Sprintf("/%s", tmp) - } - } - *pathPtr = tmp - } - if cfg.MaxAttempts <= 0 { - cfg.MaxAttempts = DefaultMaxAttempts - } - if cfg.MaxAttempts > DefaultMaxAttempts { - cfg.MaxAttempts = DefaultMaxAttempts - } - if cfg.Backoff <= 0 { - cfg.Backoff = DefaultBackoff - } - - metricsClient := &http.Client{ - Transport: ourTransport, - Timeout: cfg.Metrics.Timeout, - } - if cfg.Metrics.TLSCfg != nil { - transport := ourTransport.Clone() - transport.TLSClientConfig = cfg.Metrics.TLSCfg - metricsClient.Transport = transport - } - - tracesClient := &http.Client{ - Transport: ourTransport, - Timeout: cfg.Traces.Timeout, - } - if cfg.Traces.TLSCfg != nil { - transport := ourTransport.Clone() - transport.TLSClientConfig = cfg.Traces.TLSCfg - tracesClient.Transport = transport - } - - stopCh := make(chan struct{}) - return &driver{ - tracesDriver: signalDriver{ - name: "traces", - cfg: cfg.Traces, - generalCfg: cfg, - stopCh: stopCh, - client: tracesClient, - }, - metricsDriver: signalDriver{ - name: "metrics", - cfg: cfg.Metrics, - generalCfg: cfg, - stopCh: stopCh, - client: metricsClient, - }, - cfg: cfg, - stopCh: stopCh, - } -} - -// Start implements otlp.ProtocolDriver. -func (d *driver) Start(ctx context.Context) error { - // nothing to do - return nil -} - -// Stop implements otlp.ProtocolDriver. -func (d *driver) Stop(ctx context.Context) error { - close(d.stopCh) - return nil -} - -// ExportMetrics implements otlp.ProtocolDriver. -func (d *driver) ExportMetrics(ctx context.Context, cps metricsdk.CheckpointSet, selector metricsdk.ExportKindSelector) error { - rms, err := transform.CheckpointSet(ctx, selector, cps, 1) - if err != nil { - return err - } - if len(rms) == 0 { - return nil - } - pbRequest := &colmetricspb.ExportMetricsServiceRequest{ - ResourceMetrics: rms, - } - rawRequest, err := d.marshal(pbRequest) - if err != nil { - return err - } - return d.metricsDriver.send(ctx, rawRequest) -} - -// ExportTraces implements otlp.ProtocolDriver. -func (d *driver) ExportTraces(ctx context.Context, ss []tracesdk.ReadOnlySpan) error { - protoSpans := transform.Spans(ss) - if len(protoSpans) == 0 { - return nil - } - pbRequest := &coltracepb.ExportTraceServiceRequest{ - ResourceSpans: protoSpans, - } - rawRequest, err := d.marshal(pbRequest) - if err != nil { - return err - } - return d.tracesDriver.send(ctx, rawRequest) -} - -func (d *driver) marshal(msg proto.Message) ([]byte, error) { - if d.cfg.Marshaler == otlp.MarshalJSON { - return jsonpb.Marshal(msg) - } - return proto.Marshal(msg) -} - -func (d *signalDriver) send(ctx context.Context, rawRequest []byte) error { - address := fmt.Sprintf("%s://%s%s", d.getScheme(), d.cfg.Endpoint, d.cfg.URLPath) - var cancel context.CancelFunc - ctx, cancel = d.contextWithStop(ctx) - defer cancel() - for i := 0; i < d.generalCfg.MaxAttempts; i++ { - response, err := d.singleSend(ctx, rawRequest, address) - if err != nil { - return err - } - // We don't care about the body, so try to read it - // into /dev/null and close it immediately. The - // reading part is to facilitate connection reuse. - _, _ = io.Copy(ioutil.Discard, response.Body) - _ = response.Body.Close() - switch response.StatusCode { - case http.StatusOK: - return nil - case http.StatusTooManyRequests: - fallthrough - case http.StatusServiceUnavailable: - select { - case <-time.After(getWaitDuration(d.generalCfg.Backoff, i)): - continue - case <-ctx.Done(): - return ctx.Err() - } - default: - return fmt.Errorf("failed to send %s to %s with HTTP status %s", d.name, address, response.Status) - } - } - return fmt.Errorf("failed to send data to %s after %d tries", address, d.generalCfg.MaxAttempts) -} - -func (d *signalDriver) getScheme() string { - if d.cfg.Insecure { - return "http" - } - return "https" -} - -func getWaitDuration(backoff time.Duration, i int) time.Duration { - // Strategy: after nth failed attempt, attempt resending after - // k * initialBackoff + jitter, where k is a random number in - // range [0, 2^n-1), and jitter is a random percentage of - // initialBackoff from [-5%, 5%). - // - // Based on - // https://en.wikipedia.org/wiki/Exponential_backoff#Example_exponential_backoff_algorithm - // - // Jitter is our addition. - - // There won't be an overflow, since i is capped to - // DefaultMaxAttempts (5). - upperK := (int64)(1) << (i + 1) - jitterPercent := (rand.Float64() - 0.5) / 10. - jitter := jitterPercent * (float64)(backoff) - k := rand.Int63n(upperK) - return (time.Duration)(k)*backoff + (time.Duration)(jitter) -} - -func (d *signalDriver) contextWithStop(ctx context.Context) (context.Context, context.CancelFunc) { - // Unify the parent context Done signal with the driver's stop - // channel. - ctx, cancel := context.WithCancel(ctx) - go func(ctx context.Context, cancel context.CancelFunc) { - select { - case <-ctx.Done(): - // Nothing to do, either cancelled or deadline - // happened. - case <-d.stopCh: - cancel() - } - }(ctx, cancel) - return ctx, cancel -} - -func (d *signalDriver) singleSend(ctx context.Context, rawRequest []byte, address string) (*http.Response, error) { - request, err := http.NewRequestWithContext(ctx, http.MethodPost, address, nil) - if err != nil { - return nil, err - } - bodyReader, contentLength, headers := d.prepareBody(rawRequest) - // Not closing bodyReader through defer, the HTTP Client's - // Transport will do it for us - request.Body = bodyReader - request.ContentLength = contentLength - for key, values := range headers { - for _, value := range values { - request.Header.Add(key, value) - } - } - return d.client.Do(request) -} - -func (d *signalDriver) prepareBody(rawRequest []byte) (io.ReadCloser, int64, http.Header) { - var bodyReader io.ReadCloser - headers := http.Header{} - for k, v := range d.cfg.Headers { - headers.Set(k, v) - } - contentLength := (int64)(len(rawRequest)) - if d.generalCfg.Marshaler == otlp.MarshalJSON { - headers.Set("Content-Type", contentTypeJSON) - } else { - headers.Set("Content-Type", contentTypeProto) - } - requestReader := bytes.NewBuffer(rawRequest) - switch d.cfg.Compression { - case otlp.NoCompression: - bodyReader = ioutil.NopCloser(requestReader) - case otlp.GzipCompression: - preader, pwriter := io.Pipe() - go func() { - defer pwriter.Close() - gzipper := gzip.NewWriter(pwriter) - defer gzipper.Close() - _, err := io.Copy(gzipper, requestReader) - if err != nil { - otel.Handle(fmt.Errorf("otlphttp: failed to gzip request: %v", err)) - } - }() - headers.Set("Content-Encoding", "gzip") - bodyReader = preader - contentLength = -1 - } - return bodyReader, contentLength, headers -} diff --git a/exporters/otlp/otlphttp/driver_test.go b/exporters/otlp/otlphttp/driver_test.go deleted file mode 100644 index abd4355e3..000000000 --- a/exporters/otlp/otlphttp/driver_test.go +++ /dev/null @@ -1,449 +0,0 @@ -// Copyright The OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package otlphttp_test - -import ( - "context" - "fmt" - "net/http" - "os" - "testing" - "time" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "go.opentelemetry.io/otel/exporters/otlp" - "go.opentelemetry.io/otel/exporters/otlp/internal/otlptest" - "go.opentelemetry.io/otel/exporters/otlp/otlphttp" -) - -const ( - relOtherMetricsPath = "post/metrics/here" - relOtherTracesPath = "post/traces/here" - otherMetricsPath = "/post/metrics/here" - otherTracesPath = "/post/traces/here" -) - -var ( - testHeaders = map[string]string{ - "Otel-Go-Key-1": "somevalue", - "Otel-Go-Key-2": "someothervalue", - } -) - -func TestEndToEnd(t *testing.T) { - tests := []struct { - name string - opts []otlphttp.Option - mcCfg mockCollectorConfig - tls bool - }{ - { - name: "no extra options", - opts: nil, - }, - { - name: "with gzip compression", - opts: []otlphttp.Option{ - otlphttp.WithCompression(otlp.GzipCompression), - }, - }, - { - name: "with empty paths (forced to defaults)", - opts: []otlphttp.Option{ - otlphttp.WithMetricsURLPath(""), - otlphttp.WithTracesURLPath(""), - }, - }, - { - name: "with different paths", - opts: []otlphttp.Option{ - otlphttp.WithMetricsURLPath(otherMetricsPath), - otlphttp.WithTracesURLPath(otherTracesPath), - }, - mcCfg: mockCollectorConfig{ - MetricsURLPath: otherMetricsPath, - TracesURLPath: otherTracesPath, - }, - }, - { - name: "with relative paths", - opts: []otlphttp.Option{ - otlphttp.WithMetricsURLPath(relOtherMetricsPath), - otlphttp.WithTracesURLPath(relOtherTracesPath), - }, - mcCfg: mockCollectorConfig{ - MetricsURLPath: otherMetricsPath, - TracesURLPath: otherTracesPath, - }, - }, - { - name: "with TLS", - opts: nil, - mcCfg: mockCollectorConfig{ - WithTLS: true, - }, - tls: true, - }, - { - name: "with extra headers", - opts: []otlphttp.Option{ - otlphttp.WithHeaders(testHeaders), - }, - mcCfg: mockCollectorConfig{ - ExpectedHeaders: testHeaders, - }, - }, - { - name: "with json encoding", - opts: []otlphttp.Option{ - otlphttp.WithMarshal(otlp.MarshalJSON), - }, - }, - } - - for _, tc := range tests { - t.Run(tc.name, func(t *testing.T) { - mc := runMockCollector(t, tc.mcCfg) - defer mc.MustStop(t) - allOpts := []otlphttp.Option{ - otlphttp.WithEndpoint(mc.Endpoint()), - } - if tc.tls { - tlsConfig := mc.ClientTLSConfig() - require.NotNil(t, tlsConfig) - allOpts = append(allOpts, otlphttp.WithTLSClientConfig(tlsConfig)) - } else { - allOpts = append(allOpts, otlphttp.WithInsecure()) - } - allOpts = append(allOpts, tc.opts...) - driver := otlphttp.NewDriver(allOpts...) - ctx := context.Background() - exporter, err := otlp.New(ctx, driver) - if assert.NoError(t, err) { - defer func() { - assert.NoError(t, exporter.Shutdown(ctx)) - }() - otlptest.RunEndToEndTest(ctx, t, exporter, mc, mc) - } - }) - } -} - -func TestRetry(t *testing.T) { - statuses := []int{ - http.StatusTooManyRequests, - http.StatusServiceUnavailable, - } - mcCfg := mockCollectorConfig{ - InjectHTTPStatus: statuses, - } - mc := runMockCollector(t, mcCfg) - defer mc.MustStop(t) - driver := otlphttp.NewDriver( - otlphttp.WithEndpoint(mc.Endpoint()), - otlphttp.WithTracesEndpoint(mc.Endpoint()), - otlphttp.WithInsecure(), - otlphttp.WithMaxAttempts(len(statuses)+1), - ) - ctx := context.Background() - exporter, err := otlp.New(ctx, driver) - require.NoError(t, err) - defer func() { - assert.NoError(t, exporter.Shutdown(ctx)) - }() - err = exporter.ExportSpans(ctx, otlptest.SingleReadOnlySpan()) - assert.NoError(t, err) - assert.Len(t, mc.GetSpans(), 1) -} - -func TestTimeout(t *testing.T) { - delay := make(chan struct{}) - mcCfg := mockCollectorConfig{Delay: delay} - mc := runMockCollector(t, mcCfg) - defer mc.MustStop(t) - defer func() { close(delay) }() - driver := otlphttp.NewDriver( - otlphttp.WithEndpoint(mc.Endpoint()), - otlphttp.WithInsecure(), - otlphttp.WithTimeout(time.Nanosecond), - ) - ctx := context.Background() - exporter, err := otlp.New(ctx, driver) - require.NoError(t, err) - defer func() { - assert.NoError(t, exporter.Shutdown(ctx)) - }() - err = exporter.ExportSpans(ctx, otlptest.SingleReadOnlySpan()) - assert.Equalf(t, true, os.IsTimeout(err), "expected timeout error, got: %v", err) -} - -func TestRetryFailed(t *testing.T) { - statuses := []int{ - http.StatusTooManyRequests, - http.StatusServiceUnavailable, - } - mcCfg := mockCollectorConfig{ - InjectHTTPStatus: statuses, - } - mc := runMockCollector(t, mcCfg) - defer mc.MustStop(t) - driver := otlphttp.NewDriver( - otlphttp.WithEndpoint(mc.Endpoint()), - otlphttp.WithInsecure(), - otlphttp.WithMaxAttempts(1), - ) - ctx := context.Background() - exporter, err := otlp.New(ctx, driver) - require.NoError(t, err) - defer func() { - assert.NoError(t, exporter.Shutdown(ctx)) - }() - err = exporter.ExportSpans(ctx, otlptest.SingleReadOnlySpan()) - assert.Error(t, err) - assert.Empty(t, mc.GetSpans()) -} - -func TestNoRetry(t *testing.T) { - statuses := []int{ - http.StatusBadRequest, - } - mcCfg := mockCollectorConfig{ - InjectHTTPStatus: statuses, - } - mc := runMockCollector(t, mcCfg) - defer mc.MustStop(t) - driver := otlphttp.NewDriver( - otlphttp.WithEndpoint(mc.Endpoint()), - otlphttp.WithInsecure(), - otlphttp.WithMaxAttempts(len(statuses)+1), - ) - ctx := context.Background() - exporter, err := otlp.New(ctx, driver) - require.NoError(t, err) - defer func() { - assert.NoError(t, exporter.Shutdown(ctx)) - }() - err = exporter.ExportSpans(ctx, otlptest.SingleReadOnlySpan()) - assert.Error(t, err) - assert.Equal(t, fmt.Sprintf("failed to send traces to http://%s/v1/traces with HTTP status 400 Bad Request", mc.endpoint), err.Error()) - assert.Empty(t, mc.GetSpans()) -} - -func TestFailedCheckpoint(t *testing.T) { - mcCfg := mockCollectorConfig{} - mc := runMockCollector(t, mcCfg) - defer mc.MustStop(t) - driver := otlphttp.NewDriver( - otlphttp.WithEndpoint(mc.Endpoint()), - otlphttp.WithInsecure(), - ) - ctx := context.Background() - exporter, err := otlp.New(ctx, driver) - require.NoError(t, err) - defer func() { - assert.NoError(t, exporter.Shutdown(ctx)) - }() - err = exporter.Export(ctx, otlptest.FailCheckpointSet{}) - assert.Error(t, err) - assert.Empty(t, mc.GetMetrics()) -} - -func TestEmptyData(t *testing.T) { - mcCfg := mockCollectorConfig{} - mc := runMockCollector(t, mcCfg) - defer mc.MustStop(t) - driver := otlphttp.NewDriver( - otlphttp.WithEndpoint(mc.Endpoint()), - otlphttp.WithInsecure(), - ) - ctx := context.Background() - exporter, err := otlp.New(ctx, driver) - require.NoError(t, err) - defer func() { - assert.NoError(t, exporter.Shutdown(ctx)) - }() - err = exporter.Export(ctx, otlptest.EmptyCheckpointSet{}) - assert.NoError(t, err) - err = exporter.ExportSpans(ctx, nil) - assert.NoError(t, err) - assert.Empty(t, mc.GetMetrics()) - assert.Empty(t, mc.GetSpans()) -} - -func TestUnreasonableMaxAttempts(t *testing.T) { - // Max attempts is 5, we set collector to fail 7 times and try - // to configure max attempts to be either negative or too - // large. Since we set max attempts to 5 in such cases, - // exporting to the collector should fail. - type testcase struct { - name string - maxAttempts int - } - for _, tc := range []testcase{ - { - name: "negative max attempts", - maxAttempts: -3, - }, - { - name: "too large max attempts", - maxAttempts: 10, - }, - } { - t.Run(tc.name, func(t *testing.T) { - statuses := make([]int, 0, 7) - for i := 0; i < cap(statuses); i++ { - statuses = append(statuses, http.StatusTooManyRequests) - } - mcCfg := mockCollectorConfig{ - InjectHTTPStatus: statuses, - } - mc := runMockCollector(t, mcCfg) - defer mc.MustStop(t) - driver := otlphttp.NewDriver( - otlphttp.WithEndpoint(mc.Endpoint()), - otlphttp.WithInsecure(), - otlphttp.WithMaxAttempts(tc.maxAttempts), - otlphttp.WithBackoff(time.Millisecond), - ) - ctx := context.Background() - exporter, err := otlp.New(ctx, driver) - require.NoError(t, err) - defer func() { - assert.NoError(t, exporter.Shutdown(ctx)) - }() - err = exporter.ExportSpans(ctx, otlptest.SingleReadOnlySpan()) - assert.Error(t, err) - assert.Empty(t, mc.GetSpans()) - }) - } -} - -func TestUnreasonableBackoff(t *testing.T) { - // This sets backoff to negative value, which gets corrected - // to default backoff instead of being used. Default max - // attempts is 5, so we set the collector to fail 4 times, but - // we set the deadline to 3 times of the default backoff, so - // this should show that deadline is not met, meaning that the - // retries weren't immediate (as negative backoff could - // imply). - statuses := make([]int, 0, 4) - for i := 0; i < cap(statuses); i++ { - statuses = append(statuses, http.StatusTooManyRequests) - } - mcCfg := mockCollectorConfig{ - InjectHTTPStatus: statuses, - } - mc := runMockCollector(t, mcCfg) - defer mc.MustStop(t) - driver := otlphttp.NewDriver( - otlphttp.WithEndpoint(mc.Endpoint()), - otlphttp.WithInsecure(), - otlphttp.WithBackoff(-time.Millisecond), - ) - ctx, cancel := context.WithTimeout(context.Background(), 3*otlphttp.DefaultBackoff) - defer cancel() - exporter, err := otlp.New(ctx, driver) - require.NoError(t, err) - defer func() { - assert.NoError(t, exporter.Shutdown(ctx)) - }() - err = exporter.ExportSpans(ctx, otlptest.SingleReadOnlySpan()) - assert.Error(t, err) - assert.Empty(t, mc.GetSpans()) -} - -func TestCancelledContext(t *testing.T) { - mcCfg := mockCollectorConfig{} - mc := runMockCollector(t, mcCfg) - defer mc.MustStop(t) - driver := otlphttp.NewDriver( - otlphttp.WithEndpoint(mc.Endpoint()), - otlphttp.WithInsecure(), - ) - ctx, cancel := context.WithCancel(context.Background()) - exporter, err := otlp.New(ctx, driver) - require.NoError(t, err) - defer func() { - assert.NoError(t, exporter.Shutdown(ctx)) - }() - cancel() - err = exporter.ExportSpans(ctx, otlptest.SingleReadOnlySpan()) - assert.Error(t, err) - assert.Empty(t, mc.GetSpans()) -} - -func TestDeadlineContext(t *testing.T) { - statuses := make([]int, 0, 5) - for i := 0; i < cap(statuses); i++ { - statuses = append(statuses, http.StatusTooManyRequests) - } - mcCfg := mockCollectorConfig{ - InjectHTTPStatus: statuses, - } - mc := runMockCollector(t, mcCfg) - defer mc.MustStop(t) - driver := otlphttp.NewDriver( - otlphttp.WithEndpoint(mc.Endpoint()), - otlphttp.WithInsecure(), - otlphttp.WithBackoff(time.Minute), - ) - ctx := context.Background() - exporter, err := otlp.New(ctx, driver) - require.NoError(t, err) - defer func() { - assert.NoError(t, exporter.Shutdown(ctx)) - }() - ctx, cancel := context.WithTimeout(ctx, time.Second) - defer cancel() - err = exporter.ExportSpans(ctx, otlptest.SingleReadOnlySpan()) - assert.Error(t, err) - assert.Empty(t, mc.GetSpans()) -} - -func TestStopWhileExporting(t *testing.T) { - statuses := make([]int, 0, 5) - for i := 0; i < cap(statuses); i++ { - statuses = append(statuses, http.StatusTooManyRequests) - } - mcCfg := mockCollectorConfig{ - InjectHTTPStatus: statuses, - } - mc := runMockCollector(t, mcCfg) - defer mc.MustStop(t) - driver := otlphttp.NewDriver( - otlphttp.WithEndpoint(mc.Endpoint()), - otlphttp.WithInsecure(), - otlphttp.WithBackoff(time.Minute), - ) - ctx := context.Background() - exporter, err := otlp.New(ctx, driver) - require.NoError(t, err) - defer func() { - assert.NoError(t, exporter.Shutdown(ctx)) - }() - doneCh := make(chan struct{}) - go func() { - err := exporter.ExportSpans(ctx, otlptest.SingleReadOnlySpan()) - assert.Error(t, err) - assert.Empty(t, mc.GetSpans()) - close(doneCh) - }() - <-time.After(time.Second) - err = exporter.Shutdown(ctx) - assert.NoError(t, err) - <-doneCh -} diff --git a/exporters/otlp/otlphttp/mock_collector_test.go b/exporters/otlp/otlphttp/mock_collector_test.go deleted file mode 100644 index 2575b7696..000000000 --- a/exporters/otlp/otlphttp/mock_collector_test.go +++ /dev/null @@ -1,314 +0,0 @@ -// Copyright The OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package otlphttp_test - -import ( - "bytes" - "compress/gzip" - "context" - "crypto/tls" - "fmt" - "io" - "io/ioutil" - "net" - "net/http" - "sync" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - jsonpb "google.golang.org/protobuf/encoding/protojson" - "google.golang.org/protobuf/proto" - - "go.opentelemetry.io/otel/exporters/otlp/internal/otlptest" - "go.opentelemetry.io/otel/exporters/otlp/otlphttp" - collectormetricpb "go.opentelemetry.io/proto/otlp/collector/metrics/v1" - collectortracepb "go.opentelemetry.io/proto/otlp/collector/trace/v1" - metricpb "go.opentelemetry.io/proto/otlp/metrics/v1" - tracepb "go.opentelemetry.io/proto/otlp/trace/v1" -) - -type mockCollector struct { - endpoint string - server *http.Server - - spanLock sync.Mutex - spansStorage otlptest.SpansStorage - - metricLock sync.Mutex - metricsStorage otlptest.MetricsStorage - - injectHTTPStatus []int - injectContentType string - delay <-chan struct{} - - clientTLSConfig *tls.Config - expectedHeaders map[string]string -} - -func (c *mockCollector) Stop() error { - return c.server.Shutdown(context.Background()) -} - -func (c *mockCollector) MustStop(t *testing.T) { - assert.NoError(t, c.server.Shutdown(context.Background())) -} - -func (c *mockCollector) GetSpans() []*tracepb.Span { - c.spanLock.Lock() - defer c.spanLock.Unlock() - return c.spansStorage.GetSpans() -} - -func (c *mockCollector) GetResourceSpans() []*tracepb.ResourceSpans { - c.spanLock.Lock() - defer c.spanLock.Unlock() - return c.spansStorage.GetResourceSpans() -} - -func (c *mockCollector) GetMetrics() []*metricpb.Metric { - c.metricLock.Lock() - defer c.metricLock.Unlock() - return c.metricsStorage.GetMetrics() -} - -func (c *mockCollector) Endpoint() string { - return c.endpoint -} - -func (c *mockCollector) ClientTLSConfig() *tls.Config { - return c.clientTLSConfig -} - -func (c *mockCollector) serveMetrics(w http.ResponseWriter, r *http.Request) { - if c.delay != nil { - select { - case <-c.delay: - case <-r.Context().Done(): - return - } - } - - if !c.checkHeaders(r) { - w.WriteHeader(http.StatusBadRequest) - return - } - response := collectormetricpb.ExportMetricsServiceResponse{} - rawResponse, err := proto.Marshal(&response) - if err != nil { - w.WriteHeader(http.StatusInternalServerError) - return - } - if injectedStatus := c.getInjectHTTPStatus(); injectedStatus != 0 { - writeReply(w, rawResponse, injectedStatus, c.injectContentType) - return - } - rawRequest, err := readRequest(r) - if err != nil { - w.WriteHeader(http.StatusInternalServerError) - return - } - request, err := unmarshalMetricsRequest(rawRequest, r.Header.Get("content-type")) - if err != nil { - w.WriteHeader(http.StatusBadRequest) - return - } - writeReply(w, rawResponse, 0, c.injectContentType) - c.metricLock.Lock() - defer c.metricLock.Unlock() - c.metricsStorage.AddMetrics(request) -} - -func unmarshalMetricsRequest(rawRequest []byte, contentType string) (*collectormetricpb.ExportMetricsServiceRequest, error) { - request := &collectormetricpb.ExportMetricsServiceRequest{} - if contentType == "application/json" { - err := jsonpb.Unmarshal(rawRequest, request) - return request, err - } - err := proto.Unmarshal(rawRequest, request) - return request, err -} - -func (c *mockCollector) serveTraces(w http.ResponseWriter, r *http.Request) { - if c.delay != nil { - select { - case <-c.delay: - case <-r.Context().Done(): - return - } - } - - if !c.checkHeaders(r) { - w.WriteHeader(http.StatusBadRequest) - return - } - response := collectortracepb.ExportTraceServiceResponse{} - rawResponse, err := proto.Marshal(&response) - if err != nil { - w.WriteHeader(http.StatusInternalServerError) - return - } - if injectedStatus := c.getInjectHTTPStatus(); injectedStatus != 0 { - writeReply(w, rawResponse, injectedStatus, c.injectContentType) - return - } - rawRequest, err := readRequest(r) - if err != nil { - w.WriteHeader(http.StatusInternalServerError) - return - } - - request, err := unmarshalTraceRequest(rawRequest, r.Header.Get("content-type")) - if err != nil { - w.WriteHeader(http.StatusBadRequest) - return - } - writeReply(w, rawResponse, 0, c.injectContentType) - c.spanLock.Lock() - defer c.spanLock.Unlock() - c.spansStorage.AddSpans(request) -} - -func unmarshalTraceRequest(rawRequest []byte, contentType string) (*collectortracepb.ExportTraceServiceRequest, error) { - request := &collectortracepb.ExportTraceServiceRequest{} - if contentType == "application/json" { - err := jsonpb.Unmarshal(rawRequest, request) - return request, err - } - err := proto.Unmarshal(rawRequest, request) - return request, err -} - -func (c *mockCollector) checkHeaders(r *http.Request) bool { - for k, v := range c.expectedHeaders { - got := r.Header.Get(k) - if got != v { - return false - } - } - return true -} - -func (c *mockCollector) getInjectHTTPStatus() int { - if len(c.injectHTTPStatus) == 0 { - return 0 - } - status := c.injectHTTPStatus[0] - c.injectHTTPStatus = c.injectHTTPStatus[1:] - if len(c.injectHTTPStatus) == 0 { - c.injectHTTPStatus = nil - } - return status -} - -func readRequest(r *http.Request) ([]byte, error) { - if r.Header.Get("Content-Encoding") == "gzip" { - return readGzipBody(r.Body) - } - return ioutil.ReadAll(r.Body) -} - -func readGzipBody(body io.Reader) ([]byte, error) { - rawRequest := bytes.Buffer{} - gunzipper, err := gzip.NewReader(body) - if err != nil { - return nil, err - } - defer gunzipper.Close() - _, err = io.Copy(&rawRequest, gunzipper) - if err != nil { - return nil, err - } - return rawRequest.Bytes(), nil -} - -func writeReply(w http.ResponseWriter, rawResponse []byte, injectHTTPStatus int, injectContentType string) { - status := http.StatusOK - if injectHTTPStatus != 0 { - status = injectHTTPStatus - } - contentType := "application/x-protobuf" - if injectContentType != "" { - contentType = injectContentType - } - w.Header().Set("Content-Type", contentType) - w.WriteHeader(status) - _, _ = w.Write(rawResponse) -} - -type mockCollectorConfig struct { - MetricsURLPath string - TracesURLPath string - Port int - InjectHTTPStatus []int - InjectContentType string - Delay <-chan struct{} - WithTLS bool - ExpectedHeaders map[string]string -} - -func (c *mockCollectorConfig) fillInDefaults() { - if c.MetricsURLPath == "" { - c.MetricsURLPath = otlphttp.DefaultMetricsPath - } - if c.TracesURLPath == "" { - c.TracesURLPath = otlphttp.DefaultTracesPath - } -} - -func runMockCollector(t *testing.T, cfg mockCollectorConfig) *mockCollector { - cfg.fillInDefaults() - ln, err := net.Listen("tcp", fmt.Sprintf("localhost:%d", cfg.Port)) - require.NoError(t, err) - _, portStr, err := net.SplitHostPort(ln.Addr().String()) - require.NoError(t, err) - m := &mockCollector{ - endpoint: fmt.Sprintf("localhost:%s", portStr), - spansStorage: otlptest.NewSpansStorage(), - metricsStorage: otlptest.NewMetricsStorage(), - injectHTTPStatus: cfg.InjectHTTPStatus, - injectContentType: cfg.InjectContentType, - delay: cfg.Delay, - expectedHeaders: cfg.ExpectedHeaders, - } - mux := http.NewServeMux() - mux.Handle(cfg.MetricsURLPath, http.HandlerFunc(m.serveMetrics)) - mux.Handle(cfg.TracesURLPath, http.HandlerFunc(m.serveTraces)) - server := &http.Server{ - Handler: mux, - } - if cfg.WithTLS { - pem, err := generateWeakCertificate() - require.NoError(t, err) - tlsCertificate, err := tls.X509KeyPair(pem.Certificate, pem.PrivateKey) - require.NoError(t, err) - server.TLSConfig = &tls.Config{ - Certificates: []tls.Certificate{tlsCertificate}, - } - - m.clientTLSConfig = &tls.Config{ - InsecureSkipVerify: true, - } - } - go func() { - if cfg.WithTLS { - _ = server.ServeTLS(ln, "", "") - } else { - _ = server.Serve(ln) - } - }() - m.server = server - return m -} diff --git a/exporters/otlp/otlphttp/options.go b/exporters/otlp/otlphttp/options.go deleted file mode 100644 index 7ed35b284..000000000 --- a/exporters/otlp/otlphttp/options.go +++ /dev/null @@ -1,207 +0,0 @@ -// Copyright The OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package otlphttp - -import ( - "crypto/tls" - "time" - - "go.opentelemetry.io/otel/exporters/otlp" - "go.opentelemetry.io/otel/exporters/otlp/internal/otlpconfig" -) - -const ( - // DefaultMaxAttempts describes how many times the driver - // should retry the sending of the payload in case of a - // retryable error. - DefaultMaxAttempts int = 5 - // DefaultTracesPath is a default URL path for endpoint that - // receives spans. - DefaultTracesPath string = "/v1/traces" - // DefaultMetricsPath is a default URL path for endpoint that - // receives metrics. - DefaultMetricsPath string = "/v1/metrics" - // DefaultBackoff is a default base backoff time used in the - // exponential backoff strategy. - DefaultBackoff time.Duration = 300 * time.Millisecond - // DefaultTimeout is a default max waiting time for the backend to process - // each span or metrics batch. - DefaultTimeout time.Duration = 10 * time.Second -) - -// Option applies an option to the HTTP driver. -type Option interface { - applyHTTPOption(*otlpconfig.Config) -} - -type wrappedOption struct { - otlpconfig.HTTPOption -} - -func (w wrappedOption) applyHTTPOption(cfg *otlpconfig.Config) { - w.ApplyHTTPOption(cfg) -} - -// WithEndpoint allows one to set the address of the collector -// endpoint that the driver will use to send metrics and spans. If -// unset, it will instead try to use -// DefaultCollectorHost:DefaultCollectorPort. Note that the endpoint -// must not contain any URL path. -func WithEndpoint(endpoint string) Option { - return wrappedOption{otlpconfig.WithEndpoint(endpoint)} -} - -// WithTracesEndpoint allows one to set the address of the collector -// endpoint that the driver will use to send spans. If -// unset, it will instead try to use the Endpoint configuration. -// Note that the endpoint must not contain any URL path. -func WithTracesEndpoint(endpoint string) Option { - return wrappedOption{otlpconfig.WithTracesEndpoint(endpoint)} -} - -// WithMetricsEndpoint allows one to set the address of the collector -// endpoint that the driver will use to send metrics. If -// unset, it will instead try to use the Endpoint configuration. -// Note that the endpoint must not contain any URL path. -func WithMetricsEndpoint(endpoint string) Option { - return wrappedOption{otlpconfig.WithMetricsEndpoint(endpoint)} -} - -// WithCompression tells the driver to compress the sent data. -func WithCompression(compression otlp.Compression) Option { - return wrappedOption{otlpconfig.WithCompression(compression)} -} - -// WithTracesCompression tells the driver to compress the sent traces data. -func WithTracesCompression(compression otlp.Compression) Option { - return wrappedOption{otlpconfig.WithTracesCompression(compression)} -} - -// WithMetricsCompression tells the driver to compress the sent metrics data. -func WithMetricsCompression(compression otlp.Compression) Option { - return wrappedOption{otlpconfig.WithMetricsCompression(compression)} -} - -// WithTracesURLPath allows one to override the default URL path used -// for sending traces. If unset, DefaultTracesPath will be used. -func WithTracesURLPath(urlPath string) Option { - return wrappedOption{otlpconfig.WithTracesURLPath(urlPath)} -} - -// WithMetricsURLPath allows one to override the default URL path used -// for sending metrics. If unset, DefaultMetricsPath will be used. -func WithMetricsURLPath(urlPath string) Option { - return wrappedOption{otlpconfig.WithMetricsURLPath(urlPath)} -} - -// WithMaxAttempts allows one to override how many times the driver -// will try to send the payload in case of retryable errors. If unset, -// DefaultMaxAttempts will be used. -func WithMaxAttempts(maxAttempts int) Option { - return wrappedOption{otlpconfig.WithMaxAttempts(maxAttempts)} -} - -// WithBackoff tells the driver to use the duration as a base of the -// exponential backoff strategy. If unset, DefaultBackoff will be -// used. -func WithBackoff(duration time.Duration) Option { - return wrappedOption{otlpconfig.WithBackoff(duration)} -} - -// WithTLSClientConfig can be used to set up a custom TLS -// configuration for the client used to send payloads to the -// collector. Use it if you want to use a custom certificate. -func WithTLSClientConfig(tlsCfg *tls.Config) Option { - return wrappedOption{otlpconfig.WithTLSClientConfig(tlsCfg)} -} - -// WithTracesTLSClientConfig can be used to set up a custom TLS -// configuration for the client used to send traces. -// Use it if you want to use a custom certificate. -func WithTracesTLSClientConfig(tlsCfg *tls.Config) Option { - return wrappedOption{otlpconfig.WithTracesTLSClientConfig(tlsCfg)} -} - -// WithMetricsTLSClientConfig can be used to set up a custom TLS -// configuration for the client used to send metrics. -// Use it if you want to use a custom certificate. -func WithMetricsTLSClientConfig(tlsCfg *tls.Config) Option { - return wrappedOption{otlpconfig.WithMetricsTLSClientConfig(tlsCfg)} -} - -// WithInsecure tells the driver to connect to the collector using the -// HTTP scheme, instead of HTTPS. -func WithInsecure() Option { - return wrappedOption{otlpconfig.WithInsecure()} -} - -// WithInsecureTraces tells the driver to connect to the traces collector using the -// HTTP scheme, instead of HTTPS. -func WithInsecureTraces() Option { - return wrappedOption{otlpconfig.WithInsecureTraces()} -} - -// WithInsecureMetrics tells the driver to connect to the metrics collector using the -// HTTP scheme, instead of HTTPS. -func WithInsecureMetrics() Option { - return wrappedOption{otlpconfig.WithInsecureMetrics()} -} - -// WithHeaders allows one to tell the driver to send additional HTTP -// headers with the payloads. Specifying headers like Content-Length, -// Content-Encoding and Content-Type may result in a broken driver. -func WithHeaders(headers map[string]string) Option { - return wrappedOption{otlpconfig.WithHeaders(headers)} -} - -// WithTracesHeaders allows one to tell the driver to send additional HTTP -// headers with the trace payloads. Specifying headers like Content-Length, -// Content-Encoding and Content-Type may result in a broken driver. -func WithTracesHeaders(headers map[string]string) Option { - return wrappedOption{otlpconfig.WithTracesHeaders(headers)} -} - -// WithMetricsHeaders allows one to tell the driver to send additional HTTP -// headers with the metrics payloads. Specifying headers like Content-Length, -// Content-Encoding and Content-Type may result in a broken driver. -func WithMetricsHeaders(headers map[string]string) Option { - return wrappedOption{otlpconfig.WithMetricsHeaders(headers)} -} - -// WithMarshal tells the driver which wire format to use when sending to the -// collector. If unset, MarshalProto will be used -func WithMarshal(m otlp.Marshaler) Option { - return wrappedOption{otlpconfig.NewHTTPOption(func(cfg *otlpconfig.Config) { - cfg.Marshaler = m - })} -} - -// WithTimeout tells the driver the max waiting time for the backend to process -// each spans or metrics batch. If unset, the default will be 10 seconds. -func WithTimeout(duration time.Duration) Option { - return wrappedOption{otlpconfig.WithTimeout(duration)} -} - -// WithTracesTimeout tells the driver the max waiting time for the backend to process -// each spans batch. If unset, the default will be 10 seconds. -func WithTracesTimeout(duration time.Duration) Option { - return wrappedOption{otlpconfig.WithTracesTimeout(duration)} -} - -// WithMetricsTimeout tells the driver the max waiting time for the backend to process -// each metrics batch. If unset, the default will be 10 seconds. -func WithMetricsTimeout(duration time.Duration) Option { - return wrappedOption{otlpconfig.WithMetricsTimeout(duration)} -} diff --git a/exporters/otlp/otlptrace/go.mod b/exporters/otlp/otlptrace/go.mod index 64c4366c8..dcdc10d34 100644 --- a/exporters/otlp/otlptrace/go.mod +++ b/exporters/otlp/otlptrace/go.mod @@ -20,8 +20,6 @@ replace go.opentelemetry.io/otel => ../../.. replace go.opentelemetry.io/otel/sdk => ../../../sdk -replace go.opentelemetry.io/otel/exporters/otlp => ../ - replace go.opentelemetry.io/otel/metric => ../../../metric replace go.opentelemetry.io/otel/oteltest => ../../../oteltest diff --git a/exporters/otlp/otlptrace/otlptracegrpc/go.mod b/exporters/otlp/otlptrace/otlptracegrpc/go.mod index 9a3b5cd8c..1270862d6 100644 --- a/exporters/otlp/otlptrace/otlptracegrpc/go.mod +++ b/exporters/otlp/otlptrace/otlptracegrpc/go.mod @@ -17,8 +17,6 @@ replace go.opentelemetry.io/otel => ../../../.. replace go.opentelemetry.io/otel/sdk => ../../../../sdk -replace go.opentelemetry.io/otel/exporters/otlp => ../.. - replace go.opentelemetry.io/otel/exporters/otlp/otlptrace => ../ replace go.opentelemetry.io/otel/metric => ../../../../metric diff --git a/exporters/otlp/otlptrace/otlptracehttp/go.mod b/exporters/otlp/otlptrace/otlptracehttp/go.mod index f9ecba64c..0bb84a9ae 100644 --- a/exporters/otlp/otlptrace/otlptracehttp/go.mod +++ b/exporters/otlp/otlptrace/otlptracehttp/go.mod @@ -36,8 +36,6 @@ replace go.opentelemetry.io/otel/example/zipkin => ../../../../example/zipkin replace go.opentelemetry.io/otel/exporters/metric/prometheus => ../../../metric/prometheus -replace go.opentelemetry.io/otel/exporters/otlp => ../.. - replace go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc => ../otlptracegrpc replace go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp => ./ diff --git a/exporters/otlp/protocoldriver.go b/exporters/otlp/protocoldriver.go deleted file mode 100644 index 21c38bbf2..000000000 --- a/exporters/otlp/protocoldriver.go +++ /dev/null @@ -1,176 +0,0 @@ -// Copyright The OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package otlp // import "go.opentelemetry.io/otel/exporters/otlp" - -import ( - "context" - "sync" - - metricsdk "go.opentelemetry.io/otel/sdk/export/metric" - tracesdk "go.opentelemetry.io/otel/sdk/trace" -) - -// ProtocolDriver is an interface used by OTLP exporter. It's -// responsible for connecting to and disconnecting from the collector, -// and for transforming traces and metrics into wire format and -// transmitting them to the collector. -type ProtocolDriver interface { - // Start should establish connection(s) to endpoint(s). It is - // called just once by the exporter, so the implementation - // does not need to worry about idempotence and locking. - Start(ctx context.Context) error - // Stop should close the connections. The function is called - // only once by the exporter, so the implementation does not - // need to worry about idempotence, but it may be called - // concurrently with ExportMetrics or ExportTraces, so proper - // locking is required. The function serves as a - // synchronization point - after the function returns, the - // process of closing connections is assumed to be finished. - Stop(ctx context.Context) error - // ExportMetrics should transform the passed metrics to the - // wire format and send it to the collector. May be called - // concurrently with ExportTraces, so the manager needs to - // take this into account by doing proper locking. - ExportMetrics(ctx context.Context, cps metricsdk.CheckpointSet, selector metricsdk.ExportKindSelector) error - // ExportTraces should transform the passed traces to the wire - // format and send it to the collector. May be called - // concurrently with ExportMetrics, so the manager needs to - // take this into account by doing proper locking. - ExportTraces(ctx context.Context, ss []tracesdk.ReadOnlySpan) error -} - -// SplitConfig is used to configure a split driver. -type SplitConfig struct { - // ForMetrics driver will be used for sending metrics to the - // collector. - ForMetrics ProtocolDriver - // ForTraces driver will be used for sending spans to the - // collector. - ForTraces ProtocolDriver -} - -type splitDriver struct { - metric ProtocolDriver - trace ProtocolDriver -} - -// noopDriver implements the ProtocolDriver interface and -// is used internally to implement split drivers that do not have -// all drivers configured. -type noopDriver struct{} - -var _ ProtocolDriver = (*noopDriver)(nil) - -var _ ProtocolDriver = (*splitDriver)(nil) - -// NewSplitDriver creates a protocol driver which contains two other -// protocol drivers and will forward traces to one of them and metrics -// to another. -func NewSplitDriver(opts ...SplitDriverOption) ProtocolDriver { - driver := splitDriver{ - metric: &noopDriver{}, - trace: &noopDriver{}, - } - for _, opt := range opts { - opt.apply(&driver) - } - return &driver -} - -// Start implements ProtocolDriver. It starts both drivers at the same -// time. -func (d *splitDriver) Start(ctx context.Context) error { - wg := sync.WaitGroup{} - wg.Add(2) - var ( - metricErr error - traceErr error - ) - go func() { - defer wg.Done() - metricErr = d.metric.Start(ctx) - }() - go func() { - defer wg.Done() - traceErr = d.trace.Start(ctx) - }() - wg.Wait() - if metricErr != nil { - return metricErr - } - if traceErr != nil { - return traceErr - } - return nil -} - -// Stop implements ProtocolDriver. It stops both drivers at the same -// time. -func (d *splitDriver) Stop(ctx context.Context) error { - wg := sync.WaitGroup{} - wg.Add(2) - var ( - metricErr error - traceErr error - ) - go func() { - defer wg.Done() - metricErr = d.metric.Stop(ctx) - }() - go func() { - defer wg.Done() - traceErr = d.trace.Stop(ctx) - }() - wg.Wait() - if metricErr != nil { - return metricErr - } - if traceErr != nil { - return traceErr - } - return nil -} - -// ExportMetrics implements ProtocolDriver. It forwards the call to -// the driver used for sending metrics. -func (d *splitDriver) ExportMetrics(ctx context.Context, cps metricsdk.CheckpointSet, selector metricsdk.ExportKindSelector) error { - return d.metric.ExportMetrics(ctx, cps, selector) -} - -// ExportTraces implements ProtocolDriver. It forwards the call to the -// driver used for sending spans. -func (d *splitDriver) ExportTraces(ctx context.Context, ss []tracesdk.ReadOnlySpan) error { - return d.trace.ExportTraces(ctx, ss) -} - -// Start does nothing. -func (d *noopDriver) Start(ctx context.Context) error { - return nil -} - -// Stop does nothing. -func (d *noopDriver) Stop(ctx context.Context) error { - return nil -} - -// ExportMetrics does nothing. -func (d *noopDriver) ExportMetrics(ctx context.Context, cps metricsdk.CheckpointSet, selector metricsdk.ExportKindSelector) error { - return nil -} - -// ExportTraces does nothing. -func (d *noopDriver) ExportTraces(ctx context.Context, ss []tracesdk.ReadOnlySpan) error { - return nil -} diff --git a/exporters/otlp/retry.go b/exporters/otlp/retry.go deleted file mode 100644 index 65c918ec2..000000000 --- a/exporters/otlp/retry.go +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright The OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package otlp - -import ( - "time" -) - -// RetrySettings defines configuration for retrying batches in case of export failure -// using an exponential backoff. -type RetrySettings struct { - // Enabled indicates whether to not retry sending batches in case of export failure. - Enabled bool - // InitialInterval the time to wait after the first failure before retrying. - InitialInterval time.Duration - // MaxInterval is the upper bound on backoff interval. Once this value is reached the delay between - // consecutive retries will always be `MaxInterval`. - MaxInterval time.Duration - // MaxElapsedTime is the maximum amount of time (including retries) spent trying to send a request/batch. - // Once this value is reached, the data is discarded. - MaxElapsedTime time.Duration -} diff --git a/exporters/stdout/go.mod b/exporters/stdout/go.mod index 94fef1a75..53de6975b 100644 --- a/exporters/stdout/go.mod +++ b/exporters/stdout/go.mod @@ -38,8 +38,6 @@ replace go.opentelemetry.io/otel/example/zipkin => ../../example/zipkin replace go.opentelemetry.io/otel/exporters/metric/prometheus => ../metric/prometheus -replace go.opentelemetry.io/otel/exporters/otlp => ../otlp - replace go.opentelemetry.io/otel/exporters/stdout => ./ replace go.opentelemetry.io/otel/exporters/trace/jaeger => ../trace/jaeger diff --git a/exporters/trace/jaeger/go.mod b/exporters/trace/jaeger/go.mod index 045826d53..32fadd493 100644 --- a/exporters/trace/jaeger/go.mod +++ b/exporters/trace/jaeger/go.mod @@ -35,8 +35,6 @@ replace go.opentelemetry.io/otel/example/zipkin => ../../../example/zipkin replace go.opentelemetry.io/otel/exporters/metric/prometheus => ../../metric/prometheus -replace go.opentelemetry.io/otel/exporters/otlp => ../../otlp - replace go.opentelemetry.io/otel/exporters/stdout => ../../stdout replace go.opentelemetry.io/otel/exporters/trace/jaeger => ./ diff --git a/exporters/trace/zipkin/go.mod b/exporters/trace/zipkin/go.mod index 921db6c49..152e6a3b8 100644 --- a/exporters/trace/zipkin/go.mod +++ b/exporters/trace/zipkin/go.mod @@ -36,8 +36,6 @@ replace go.opentelemetry.io/otel/example/zipkin => ../../../example/zipkin replace go.opentelemetry.io/otel/exporters/metric/prometheus => ../../metric/prometheus -replace go.opentelemetry.io/otel/exporters/otlp => ../../otlp - replace go.opentelemetry.io/otel/exporters/stdout => ../../stdout replace go.opentelemetry.io/otel/exporters/trace/jaeger => ../jaeger diff --git a/go.mod b/go.mod index 9dad82a25..5f458e7ac 100644 --- a/go.mod +++ b/go.mod @@ -31,8 +31,6 @@ replace go.opentelemetry.io/otel/example/zipkin => ./example/zipkin replace go.opentelemetry.io/otel/exporters/metric/prometheus => ./exporters/metric/prometheus -replace go.opentelemetry.io/otel/exporters/otlp => ./exporters/otlp - replace go.opentelemetry.io/otel/exporters/stdout => ./exporters/stdout replace go.opentelemetry.io/otel/exporters/trace/jaeger => ./exporters/trace/jaeger diff --git a/internal/metric/go.mod b/internal/metric/go.mod index a605bfe85..e84d5b71a 100644 --- a/internal/metric/go.mod +++ b/internal/metric/go.mod @@ -36,8 +36,6 @@ replace go.opentelemetry.io/otel/example/zipkin => ../../example/zipkin replace go.opentelemetry.io/otel/exporters/metric/prometheus => ../../exporters/metric/prometheus -replace go.opentelemetry.io/otel/exporters/otlp => ../../exporters/otlp - replace go.opentelemetry.io/otel/exporters/otlp/otlptrace => ../../exporters/otlp/otlptrace replace go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc => ../../exporters/otlp/otlptrace/otlptracegrpc diff --git a/internal/tools/go.mod b/internal/tools/go.mod index a2850a14b..c684d4690 100644 --- a/internal/tools/go.mod +++ b/internal/tools/go.mod @@ -34,8 +34,6 @@ replace go.opentelemetry.io/otel/example/zipkin => ../../example/zipkin replace go.opentelemetry.io/otel/exporters/metric/prometheus => ../../exporters/metric/prometheus -replace go.opentelemetry.io/otel/exporters/otlp => ../../exporters/otlp - replace go.opentelemetry.io/otel/exporters/stdout => ../../exporters/stdout replace go.opentelemetry.io/otel/exporters/trace/jaeger => ../../exporters/trace/jaeger diff --git a/metric/go.mod b/metric/go.mod index 096fd4609..023feca59 100644 --- a/metric/go.mod +++ b/metric/go.mod @@ -24,8 +24,6 @@ replace go.opentelemetry.io/otel/example/zipkin => ../example/zipkin replace go.opentelemetry.io/otel/exporters/metric/prometheus => ../exporters/metric/prometheus -replace go.opentelemetry.io/otel/exporters/otlp => ../exporters/otlp - replace go.opentelemetry.io/otel/exporters/stdout => ../exporters/stdout replace go.opentelemetry.io/otel/exporters/trace/jaeger => ../exporters/trace/jaeger diff --git a/oteltest/go.mod b/oteltest/go.mod index c6e2342f6..acca17293 100644 --- a/oteltest/go.mod +++ b/oteltest/go.mod @@ -24,8 +24,6 @@ replace go.opentelemetry.io/otel/example/zipkin => ../example/zipkin replace go.opentelemetry.io/otel/exporters/metric/prometheus => ../exporters/metric/prometheus -replace go.opentelemetry.io/otel/exporters/otlp => ../exporters/otlp - replace go.opentelemetry.io/otel/exporters/stdout => ../exporters/stdout replace go.opentelemetry.io/otel/exporters/trace/jaeger => ../exporters/trace/jaeger diff --git a/sdk/export/metric/go.mod b/sdk/export/metric/go.mod index 422805c7a..a40ea9a47 100644 --- a/sdk/export/metric/go.mod +++ b/sdk/export/metric/go.mod @@ -24,8 +24,6 @@ replace go.opentelemetry.io/otel/example/zipkin => ../../../example/zipkin replace go.opentelemetry.io/otel/exporters/metric/prometheus => ../../../exporters/metric/prometheus -replace go.opentelemetry.io/otel/exporters/otlp => ../../../exporters/otlp - replace go.opentelemetry.io/otel/exporters/stdout => ../../../exporters/stdout replace go.opentelemetry.io/otel/exporters/trace/jaeger => ../../../exporters/trace/jaeger diff --git a/sdk/go.mod b/sdk/go.mod index 42c0abfcb..b6d328b08 100644 --- a/sdk/go.mod +++ b/sdk/go.mod @@ -32,8 +32,6 @@ replace go.opentelemetry.io/otel/example/zipkin => ../example/zipkin replace go.opentelemetry.io/otel/exporters/metric/prometheus => ../exporters/metric/prometheus -replace go.opentelemetry.io/otel/exporters/otlp => ../exporters/otlp - replace go.opentelemetry.io/otel/exporters/stdout => ../exporters/stdout replace go.opentelemetry.io/otel/exporters/trace/jaeger => ../exporters/trace/jaeger diff --git a/sdk/metric/go.mod b/sdk/metric/go.mod index e2e908ccf..f7c1f0081 100644 --- a/sdk/metric/go.mod +++ b/sdk/metric/go.mod @@ -24,8 +24,6 @@ replace go.opentelemetry.io/otel/example/zipkin => ../../example/zipkin replace go.opentelemetry.io/otel/exporters/metric/prometheus => ../../exporters/metric/prometheus -replace go.opentelemetry.io/otel/exporters/otlp => ../../exporters/otlp - replace go.opentelemetry.io/otel/exporters/stdout => ../../exporters/stdout replace go.opentelemetry.io/otel/exporters/trace/jaeger => ../../exporters/trace/jaeger diff --git a/trace/go.mod b/trace/go.mod index 01069d8ca..06ce2366e 100644 --- a/trace/go.mod +++ b/trace/go.mod @@ -24,8 +24,6 @@ replace go.opentelemetry.io/otel/example/zipkin => ../example/zipkin replace go.opentelemetry.io/otel/exporters/metric/prometheus => ../exporters/metric/prometheus -replace go.opentelemetry.io/otel/exporters/otlp => ../exporters/otlp - replace go.opentelemetry.io/otel/exporters/stdout => ../exporters/stdout replace go.opentelemetry.io/otel/exporters/trace/jaeger => ../exporters/trace/jaeger