From ae920e8ac49ce090f6bed26cdf3c72df6edf155e Mon Sep 17 00:00:00 2001 From: Alex Boten <223565+codeboten@users.noreply.github.com> Date: Tue, 9 Apr 2024 11:39:25 -0700 Subject: [PATCH] remove deprecated dynatrace exporter Signed-off-by: Alex Boten <223565+codeboten@users.noreply.github.com> --- .github/CODEOWNERS | 1 - .github/ISSUE_TEMPLATE/bug_report.yaml | 1 - .github/ISSUE_TEMPLATE/feature_request.yaml | 1 - .github/ISSUE_TEMPLATE/other.yaml | 1 - cmd/configschema/go.mod | 4 - cmd/configschema/go.sum | 2 - cmd/otelcontribcol/builder-config.yaml | 2 - cmd/otelcontribcol/components.go | 2 - cmd/otelcontribcol/exporters_test.go | 14 - cmd/otelcontribcol/go.mod | 4 - cmd/otelcontribcol/go.sum | 2 - exporter/dynatraceexporter/Makefile | 1 - exporter/dynatraceexporter/README.md | 361 --------- exporter/dynatraceexporter/config/config.go | 70 -- .../dynatraceexporter/config/config_test.go | 43 - .../dynatraceexporter/config/package_test.go | 14 - exporter/dynatraceexporter/doc.go | 5 - exporter/dynatraceexporter/factory.go | 70 -- exporter/dynatraceexporter/factory_test.go | 144 ---- .../generated_component_test.go | 126 --- exporter/dynatraceexporter/go.mod | 96 --- exporter/dynatraceexporter/go.sum | 184 ----- .../internal/metadata/generated_status.go | 25 - .../internal/serialization/gauge.go | 71 -- .../internal/serialization/gauge_test.go | 180 ---- .../internal/serialization/histogram.go | 184 ----- .../internal/serialization/histogram_test.go | 398 --------- .../internal/serialization/package_test.go | 14 - .../internal/serialization/serialization.go | 59 -- .../serialization/serialization_test.go | 167 ---- .../internal/serialization/sum.go | 209 ----- .../internal/serialization/sum_test.go | 394 --------- exporter/dynatraceexporter/metadata.yaml | 13 - .../dynatraceexporter/metrics_exporter.go | 340 -------- .../metrics_exporter_test.go | 766 ------------------ .../dynatraceexporter/testdata/config.yml | 22 - go.mod | 4 - go.sum | 2 - internal/components/components.go | 2 - reports/distributions/contrib.yaml | 1 - versions.yaml | 1 - 41 files changed, 4000 deletions(-) delete mode 100644 exporter/dynatraceexporter/Makefile delete mode 100644 exporter/dynatraceexporter/README.md delete mode 100644 exporter/dynatraceexporter/config/config.go delete mode 100644 exporter/dynatraceexporter/config/config_test.go delete mode 100644 exporter/dynatraceexporter/config/package_test.go delete mode 100644 exporter/dynatraceexporter/doc.go delete mode 100644 exporter/dynatraceexporter/factory.go delete mode 100644 exporter/dynatraceexporter/factory_test.go delete mode 100644 exporter/dynatraceexporter/generated_component_test.go delete mode 100644 exporter/dynatraceexporter/go.mod delete mode 100644 exporter/dynatraceexporter/go.sum delete mode 100644 exporter/dynatraceexporter/internal/metadata/generated_status.go delete mode 100644 exporter/dynatraceexporter/internal/serialization/gauge.go delete mode 100644 exporter/dynatraceexporter/internal/serialization/gauge_test.go delete mode 100644 exporter/dynatraceexporter/internal/serialization/histogram.go delete mode 100644 exporter/dynatraceexporter/internal/serialization/histogram_test.go delete mode 100644 exporter/dynatraceexporter/internal/serialization/package_test.go delete mode 100644 exporter/dynatraceexporter/internal/serialization/serialization.go delete mode 100644 exporter/dynatraceexporter/internal/serialization/serialization_test.go delete mode 100644 exporter/dynatraceexporter/internal/serialization/sum.go delete mode 100644 exporter/dynatraceexporter/internal/serialization/sum_test.go delete mode 100644 exporter/dynatraceexporter/metadata.yaml delete mode 100644 exporter/dynatraceexporter/metrics_exporter.go delete mode 100644 exporter/dynatraceexporter/metrics_exporter_test.go delete mode 100644 exporter/dynatraceexporter/testdata/config.yml diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 1296430b98df..e0db1c3d7417 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -51,7 +51,6 @@ exporter/clickhouseexporter/ @open-telemetry/collect exporter/coralogixexporter/ @open-telemetry/collector-contrib-approvers @povilasv @matej-g exporter/datadogexporter/ @open-telemetry/collector-contrib-approvers @mx-psi @dineshg13 @liustanley @songy23 @mackjmr exporter/datasetexporter/ @open-telemetry/collector-contrib-approvers @atoulme @martin-majlis-s1 @zdaratom-s1 @tomaz-s1 -exporter/dynatraceexporter/ @open-telemetry/collector-contrib-approvers @dyladan @arminru @evan-bradley exporter/elasticsearchexporter/ @open-telemetry/collector-contrib-approvers @JaredTan95 @ycombinator exporter/fileexporter/ @open-telemetry/collector-contrib-approvers @atingchen exporter/googlecloudexporter/ @open-telemetry/collector-contrib-approvers @aabmass @dashpole @jsuereth @punya @damemi @psx95 diff --git a/.github/ISSUE_TEMPLATE/bug_report.yaml b/.github/ISSUE_TEMPLATE/bug_report.yaml index ab7dd05c4e46..0f6490b1892f 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yaml +++ b/.github/ISSUE_TEMPLATE/bug_report.yaml @@ -50,7 +50,6 @@ body: - exporter/coralogix - exporter/datadog - exporter/dataset - - exporter/dynatrace - exporter/elasticsearch - exporter/file - exporter/googlecloud diff --git a/.github/ISSUE_TEMPLATE/feature_request.yaml b/.github/ISSUE_TEMPLATE/feature_request.yaml index 70e40ed468a2..26968d45d41b 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.yaml +++ b/.github/ISSUE_TEMPLATE/feature_request.yaml @@ -44,7 +44,6 @@ body: - exporter/coralogix - exporter/datadog - exporter/dataset - - exporter/dynatrace - exporter/elasticsearch - exporter/file - exporter/googlecloud diff --git a/.github/ISSUE_TEMPLATE/other.yaml b/.github/ISSUE_TEMPLATE/other.yaml index 2f451345e550..799561e59100 100644 --- a/.github/ISSUE_TEMPLATE/other.yaml +++ b/.github/ISSUE_TEMPLATE/other.yaml @@ -44,7 +44,6 @@ body: - exporter/coralogix - exporter/datadog - exporter/dataset - - exporter/dynatrace - exporter/elasticsearch - exporter/file - exporter/googlecloud diff --git a/cmd/configschema/go.mod b/cmd/configschema/go.mod index 9087454f4f4a..7239f03447ec 100644 --- a/cmd/configschema/go.mod +++ b/cmd/configschema/go.mod @@ -40,7 +40,6 @@ require ( github.com/open-telemetry/opentelemetry-collector-contrib/exporter/clickhouseexporter v0.98.0 // indirect github.com/open-telemetry/opentelemetry-collector-contrib/exporter/coralogixexporter v0.98.0 // indirect github.com/open-telemetry/opentelemetry-collector-contrib/exporter/datasetexporter v0.98.0 // indirect - github.com/open-telemetry/opentelemetry-collector-contrib/exporter/dynatraceexporter v0.98.0 // indirect github.com/open-telemetry/opentelemetry-collector-contrib/exporter/elasticsearchexporter v0.98.0 // indirect github.com/open-telemetry/opentelemetry-collector-contrib/exporter/fileexporter v0.98.0 // indirect github.com/open-telemetry/opentelemetry-collector-contrib/exporter/influxdbexporter v0.98.0 // indirect @@ -314,7 +313,6 @@ require ( github.com/docker/go-units v0.5.0 // indirect github.com/dustin/go-humanize v1.0.1 // indirect github.com/dvsekhvalnov/jose2go v1.6.0 // indirect - github.com/dynatrace-oss/dynatrace-metric-utils-go v0.5.0 // indirect github.com/eapache/go-resiliency v1.6.0 // indirect github.com/eapache/go-xerial-snappy v0.0.0-20230731223053-c322873962e3 // indirect github.com/eapache/queue v1.1.0 // indirect @@ -776,8 +774,6 @@ replace github.com/open-telemetry/opentelemetry-collector-contrib/exporter/datad replace github.com/open-telemetry/opentelemetry-collector-contrib/exporter/datasetexporter => ../../exporter/datasetexporter -replace github.com/open-telemetry/opentelemetry-collector-contrib/exporter/dynatraceexporter => ../../exporter/dynatraceexporter - replace github.com/open-telemetry/opentelemetry-collector-contrib/exporter/elasticsearchexporter => ../../exporter/elasticsearchexporter replace github.com/open-telemetry/opentelemetry-collector-contrib/exporter/fileexporter => ../../exporter/fileexporter diff --git a/cmd/configschema/go.sum b/cmd/configschema/go.sum index af0bff6116bd..47440ca3a149 100644 --- a/cmd/configschema/go.sum +++ b/cmd/configschema/go.sum @@ -540,8 +540,6 @@ github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+m github.com/dvsekhvalnov/jose2go v0.0.0-20200901110807-248326c1351b/go.mod h1:7BvyPhdbLxMXIYTFPLsyJRFMsKmOZnQmzh6Gb+uquuM= github.com/dvsekhvalnov/jose2go v1.6.0 h1:Y9gnSnP4qEI0+/uQkHvFXeD2PLPJeXEL+ySMEA2EjTY= github.com/dvsekhvalnov/jose2go v1.6.0/go.mod h1:QsHjhyTlD/lAVqn/NSbVZmSCGeDehTB/mPZadG+mhXU= -github.com/dynatrace-oss/dynatrace-metric-utils-go v0.5.0 h1:wHGPJSXvwKQVf/XfhjUPyrhpcPKWNy8F3ikH+eiwoBg= -github.com/dynatrace-oss/dynatrace-metric-utils-go v0.5.0/go.mod h1:PseHFo8Leko7J4A/TfZ6kkHdkzKBLUta6hRZR/OEbbc= github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= github.com/eapache/go-resiliency v1.6.0 h1:CqGDTLtpwuWKn6Nj3uNUdflaq+/kIPsg0gfNzHton30= github.com/eapache/go-resiliency v1.6.0/go.mod h1:5yPzW0MIvSe0JDsv0v+DvcjEv2FyD6iZYSs1ZI+iQho= diff --git a/cmd/otelcontribcol/builder-config.yaml b/cmd/otelcontribcol/builder-config.yaml index c71a4b95bf0a..9dfffe80860d 100644 --- a/cmd/otelcontribcol/builder-config.yaml +++ b/cmd/otelcontribcol/builder-config.yaml @@ -62,7 +62,6 @@ exporters: - gomod: github.com/open-telemetry/opentelemetry-collector-contrib/exporter/coralogixexporter v0.98.0 - gomod: github.com/open-telemetry/opentelemetry-collector-contrib/exporter/datadogexporter v0.98.0 - gomod: github.com/open-telemetry/opentelemetry-collector-contrib/exporter/datasetexporter v0.98.0 - - gomod: github.com/open-telemetry/opentelemetry-collector-contrib/exporter/dynatraceexporter v0.98.0 - gomod: github.com/open-telemetry/opentelemetry-collector-contrib/exporter/elasticsearchexporter v0.98.0 - gomod: github.com/open-telemetry/opentelemetry-collector-contrib/exporter/fileexporter v0.98.0 - gomod: github.com/open-telemetry/opentelemetry-collector-contrib/exporter/googlecloudexporter v0.98.0 @@ -231,7 +230,6 @@ replaces: - github.com/open-telemetry/opentelemetry-collector-contrib/extension/observer/dockerobserver => ../../extension/observer/dockerobserver - github.com/open-telemetry/opentelemetry-collector-contrib/extension/observer/k8sobserver => ../../extension/observer/k8sobserver - github.com/open-telemetry/opentelemetry-collector-contrib/exporter/sentryexporter => ../../exporter/sentryexporter - - github.com/open-telemetry/opentelemetry-collector-contrib/exporter/dynatraceexporter => ../../exporter/dynatraceexporter - github.com/open-telemetry/opentelemetry-collector-contrib/receiver/nsxtreceiver => ../../receiver/nsxtreceiver - github.com/open-telemetry/opentelemetry-collector-contrib/receiver/kubeletstatsreceiver => ../../receiver/kubeletstatsreceiver - github.com/open-telemetry/opentelemetry-collector-contrib/receiver/dockerstatsreceiver => ../../receiver/dockerstatsreceiver diff --git a/cmd/otelcontribcol/components.go b/cmd/otelcontribcol/components.go index 62cb58b6d5a4..a1382796c1da 100644 --- a/cmd/otelcontribcol/components.go +++ b/cmd/otelcontribcol/components.go @@ -43,7 +43,6 @@ import ( coralogixexporter "github.com/open-telemetry/opentelemetry-collector-contrib/exporter/coralogixexporter" datadogexporter "github.com/open-telemetry/opentelemetry-collector-contrib/exporter/datadogexporter" datasetexporter "github.com/open-telemetry/opentelemetry-collector-contrib/exporter/datasetexporter" - dynatraceexporter "github.com/open-telemetry/opentelemetry-collector-contrib/exporter/dynatraceexporter" elasticsearchexporter "github.com/open-telemetry/opentelemetry-collector-contrib/exporter/elasticsearchexporter" fileexporter "github.com/open-telemetry/opentelemetry-collector-contrib/exporter/fileexporter" googlecloudexporter "github.com/open-telemetry/opentelemetry-collector-contrib/exporter/googlecloudexporter" @@ -362,7 +361,6 @@ func components() (otelcol.Factories, error) { coralogixexporter.NewFactory(), datadogexporter.NewFactory(), datasetexporter.NewFactory(), - dynatraceexporter.NewFactory(), elasticsearchexporter.NewFactory(), fileexporter.NewFactory(), googlecloudexporter.NewFactory(), diff --git a/cmd/otelcontribcol/exporters_test.go b/cmd/otelcontribcol/exporters_test.go index 6355a1e4d328..4ced0cc48e51 100644 --- a/cmd/otelcontribcol/exporters_test.go +++ b/cmd/otelcontribcol/exporters_test.go @@ -40,7 +40,6 @@ import ( "github.com/open-telemetry/opentelemetry-collector-contrib/exporter/coralogixexporter" "github.com/open-telemetry/opentelemetry-collector-contrib/exporter/datadogexporter" "github.com/open-telemetry/opentelemetry-collector-contrib/exporter/datasetexporter" - dtconf "github.com/open-telemetry/opentelemetry-collector-contrib/exporter/dynatraceexporter/config" "github.com/open-telemetry/opentelemetry-collector-contrib/exporter/elasticsearchexporter" "github.com/open-telemetry/opentelemetry-collector-contrib/exporter/fileexporter" "github.com/open-telemetry/opentelemetry-collector-contrib/exporter/honeycombmarkerexporter" @@ -400,19 +399,6 @@ func TestDefaultExporters(t *testing.T) { expectConsumeErr: true, skipLifecycle: true, // shutdown fails if there is buffered data }, - { - exporter: "dynatrace", - getConfigFn: func() component.Config { - cfg := expFactories["dynatrace"].CreateDefaultConfig().(*dtconf.Config) - cfg.Endpoint = "http://" + endpoint - cfg.APIToken = "dynamictracing" - // disable queue/retry to validate passing the test data synchronously - cfg.QueueSettings.Enabled = false - cfg.BackOffConfig.Enabled = false - return cfg - }, - expectConsumeErr: true, - }, { exporter: "elasticsearch", getConfigFn: func() component.Config { diff --git a/cmd/otelcontribcol/go.mod b/cmd/otelcontribcol/go.mod index 94dea912d45d..114b7a4eb924 100644 --- a/cmd/otelcontribcol/go.mod +++ b/cmd/otelcontribcol/go.mod @@ -27,7 +27,6 @@ require ( github.com/open-telemetry/opentelemetry-collector-contrib/exporter/coralogixexporter v0.98.0 github.com/open-telemetry/opentelemetry-collector-contrib/exporter/datadogexporter v0.98.0 github.com/open-telemetry/opentelemetry-collector-contrib/exporter/datasetexporter v0.98.0 - github.com/open-telemetry/opentelemetry-collector-contrib/exporter/dynatraceexporter v0.98.0 github.com/open-telemetry/opentelemetry-collector-contrib/exporter/elasticsearchexporter v0.98.0 github.com/open-telemetry/opentelemetry-collector-contrib/exporter/fileexporter v0.98.0 github.com/open-telemetry/opentelemetry-collector-contrib/exporter/googlecloudexporter v0.98.0 @@ -368,7 +367,6 @@ require ( github.com/docker/go-units v0.5.0 // indirect github.com/dustin/go-humanize v1.0.1 // indirect github.com/dvsekhvalnov/jose2go v1.6.0 // indirect - github.com/dynatrace-oss/dynatrace-metric-utils-go v0.5.0 // indirect github.com/eapache/go-resiliency v1.6.0 // indirect github.com/eapache/go-xerial-snappy v0.0.0-20230731223053-c322873962e3 // indirect github.com/eapache/queue v1.1.0 // indirect @@ -761,8 +759,6 @@ replace github.com/open-telemetry/opentelemetry-collector-contrib/extension/obse replace github.com/open-telemetry/opentelemetry-collector-contrib/exporter/sentryexporter => ../../exporter/sentryexporter -replace github.com/open-telemetry/opentelemetry-collector-contrib/exporter/dynatraceexporter => ../../exporter/dynatraceexporter - replace github.com/open-telemetry/opentelemetry-collector-contrib/receiver/nsxtreceiver => ../../receiver/nsxtreceiver replace github.com/open-telemetry/opentelemetry-collector-contrib/receiver/kubeletstatsreceiver => ../../receiver/kubeletstatsreceiver diff --git a/cmd/otelcontribcol/go.sum b/cmd/otelcontribcol/go.sum index b4b6dd1320f0..60d6d95cb578 100644 --- a/cmd/otelcontribcol/go.sum +++ b/cmd/otelcontribcol/go.sum @@ -539,8 +539,6 @@ github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+m github.com/dvsekhvalnov/jose2go v0.0.0-20200901110807-248326c1351b/go.mod h1:7BvyPhdbLxMXIYTFPLsyJRFMsKmOZnQmzh6Gb+uquuM= github.com/dvsekhvalnov/jose2go v1.6.0 h1:Y9gnSnP4qEI0+/uQkHvFXeD2PLPJeXEL+ySMEA2EjTY= github.com/dvsekhvalnov/jose2go v1.6.0/go.mod h1:QsHjhyTlD/lAVqn/NSbVZmSCGeDehTB/mPZadG+mhXU= -github.com/dynatrace-oss/dynatrace-metric-utils-go v0.5.0 h1:wHGPJSXvwKQVf/XfhjUPyrhpcPKWNy8F3ikH+eiwoBg= -github.com/dynatrace-oss/dynatrace-metric-utils-go v0.5.0/go.mod h1:PseHFo8Leko7J4A/TfZ6kkHdkzKBLUta6hRZR/OEbbc= github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= github.com/eapache/go-resiliency v1.6.0 h1:CqGDTLtpwuWKn6Nj3uNUdflaq+/kIPsg0gfNzHton30= github.com/eapache/go-resiliency v1.6.0/go.mod h1:5yPzW0MIvSe0JDsv0v+DvcjEv2FyD6iZYSs1ZI+iQho= diff --git a/exporter/dynatraceexporter/Makefile b/exporter/dynatraceexporter/Makefile deleted file mode 100644 index ded7a36092dc..000000000000 --- a/exporter/dynatraceexporter/Makefile +++ /dev/null @@ -1 +0,0 @@ -include ../../Makefile.Common diff --git a/exporter/dynatraceexporter/README.md b/exporter/dynatraceexporter/README.md deleted file mode 100644 index fa02ea5f4e71..000000000000 --- a/exporter/dynatraceexporter/README.md +++ /dev/null @@ -1,361 +0,0 @@ -# Dynatrace - -[Dynatrace](https://www.dynatrace.com/integrations/opentelemetry) supports native -OpenTelemetry protocol (OTLP) ingest for traces, metrics and logs. -All signals can be sent directly to Dynatrace via **OTLP protobuf over HTTP** -using the built-in OTLP/HTTP Exporter available in the collector. -More information on using the collector with Dynatrace can be found in the -[Dynatrace documentation](https://www.dynatrace.com/support/help/shortlink/opentelemetry-collector-explanation#configuration). - -## Dynatrace Metrics Exporter - -> **Warning** -> Dynatrace supports native OpenTelemetry protocol (OTLP) ingest for traces, metrics and logs. -> Therefore, the proprietary Dynatrace OpenTelemetry metrics exporter is deprecated in favor of exporting via OTLP/HTTP. -> -> The exporter is still available but after the end of 2023, no support, updates, or compatibility with newer OTel versions will be provided. -> -> Check out the [migration guide](https://www.dynatrace.com/support/help/shortlink/migrating-dynatrace-metrics-exporter-otlp-exporter#migrate-collector-configuration) to learn how to migrate to the OTLP HTTP exporter. For a collector configuration example, please refer to the [collector configuration](https://www.dynatrace.com/support/help/shortlink/opentelemetry-collector-explanation#collector-configuration) page in the Dynatrace documentation. - - -| Status | | -| ------------- |-----------| -| Stability | [deprecated]: metrics | -| Distributions | [contrib] | -| Issues | [![Open issues](https://img.shields.io/github/issues-search/open-telemetry/opentelemetry-collector-contrib?query=is%3Aissue%20is%3Aopen%20label%3Aexporter%2Fdynatrace%20&label=open&color=orange&logo=opentelemetry)](https://github.com/open-telemetry/opentelemetry-collector-contrib/issues?q=is%3Aopen+is%3Aissue+label%3Aexporter%2Fdynatrace) [![Closed issues](https://img.shields.io/github/issues-search/open-telemetry/opentelemetry-collector-contrib?query=is%3Aissue%20is%3Aclosed%20label%3Aexporter%2Fdynatrace%20&label=closed&color=blue&logo=opentelemetry)](https://github.com/open-telemetry/opentelemetry-collector-contrib/issues?q=is%3Aclosed+is%3Aissue+label%3Aexporter%2Fdynatrace) | -| [Code Owners](https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/main/CONTRIBUTING.md#becoming-a-code-owner) | [@dyladan](https://www.github.com/dyladan), [@arminru](https://www.github.com/arminru), [@evan-bradley](https://www.github.com/evan-bradley) | - -[deprecated]: https://github.com/open-telemetry/opentelemetry-collector#deprecated -[contrib]: https://github.com/open-telemetry/opentelemetry-collector-releases/tree/main/distributions/otelcol-contrib - - -The Dynatrace metrics exporter exports metrics to the [Metrics API v2](https://www.dynatrace.com/support/help/dynatrace-api/environment-api/metric-v2/post-ingest-metrics/) -using the [metrics ingestion protocol](https://www.dynatrace.com/support/help/how-to-use-dynatrace/metrics/metric-ingestion/metric-ingestion-protocol/). - -> The requests sent to Dynatrace are authenticated using an API token mechanism documented [here](https://www.dynatrace.com/support/help/dynatrace-api/basics/dynatrace-api-authentication/). -> Please review the Collector's [security -> documentation](https://github.com/open-telemetry/opentelemetry-collector/blob/main/docs/security-best-practices.md), -> which contains recommendations on securing sensitive information such as the -> API key required by this exporter. - -### Requirements - -You will either need a Dynatrace OneAgent (version 1.201 or higher) installed on the same host as the Collector; or a Dynatrace environment with version 1.202 or higher. - -- Collector contrib minimum version: 0.18.0 - - -### Getting Started - -The Dynatrace exporter is enabled by adding a `dynatrace` entry to the `exporters` section of your config file. -All configurations are optional, but if an `endpoint` other than the OneAgent metric ingestion endpoint is specified then an `api_token` is required. -To see all available options, see [Advanced Configuration](#advanced-configuration) below. - -> When using this exporter, it is strongly RECOMMENDED to configure the OpenTelemetry SDKs to export metrics -> with DELTA temporality. If you are exporting Sum or Histogram metrics with CUMULATIVE temporality, read -> about possible limitations of this exporter [below](#considerations-when-exporting-cumulative-data-points). - -#### Running alongside Dynatrace OneAgent (preferred) - -If you run the Collector on a host or VM that is monitored by the Dynatrace OneAgent then you only need to enable the exporter. No further configurations needed. The Dynatrace exporter will send all metrics to the OneAgent which will use its secure and load balanced connection to send the metrics to your Dynatrace SaaS or Managed environment. -Depending on your environment, you might have to enable metrics ingestion on the OneAgent first as described in the [Dynatrace documentation](https://www.dynatrace.com/support/help/how-to-use-dynatrace/metrics/metric-ingestion/ingestion-methods/opentelemetry/). - -> Note: The name and identifier of the host running the Collector will be added as a dimension to every metric. If this is undesirable, then the output plugin may be used in standalone mode using the directions below. - -```yaml -exporters: - dynatrace: - ## No options are required. By default, metrics will be exported via the OneAgent on the local host. -``` - -#### Running standalone - -If you run the Collector on a host or VM without a OneAgent you will need to configure the Metrics v2 API endpoint of your Dynatrace environment to send the metrics to as well as an API token. - -Find out how to create a token in the [Dynatrace documentation](https://www.dynatrace.com/support/help/dynatrace-api/basics/dynatrace-api-authentication/) or navigate to **Access tokens** in your Dynatrace environment and create a token with the 'Ingest metrics' (`metrics.ingest`) scope enabled. It is recommended to limit token scope to only this permission. - -The endpoint for the Dynatrace Metrics API v2 is: - -* on Dynatrace Managed: `https://{your-domain}/e/{your-environment-id}/api/v2/metrics/ingest` -* on Dynatrace SaaS: `https://{your-environment-id}.live.dynatrace.com/api/v2/metrics/ingest` - -More details can be found in the [Dynatrace documentation for the Metrics v2 API](https://www.dynatrace.com/support/help/dynatrace-api/environment-api/metric-v2/post-ingest-metrics/). - -```yaml -exporters: - dynatrace: - ## If no OneAgent is running on the host, endpoint and api_token need to be set - - ## Dynatrace Metrics Ingest v2 endpoint to receive metrics - endpoint: "https://{your-environment-id}.live.dynatrace.com/api/v2/metrics/ingest" - - ## API token is required if an endpoint is specified and should be restricted to the 'Ingest metrics' scope - ## hard-coded for illustration only, should be read from a secure source - api_token: "your API token here" -``` - -You can learn more about how to use the Dynatrace API [here](https://www.dynatrace.com/support/help/dynatrace-api/). - -#### Metric Batching - -Dynatrace recommends the use of the batch processor with a maximum batch size of 1000 metrics and a timeout between 10 and 60 seconds. Batches with more than 1000 metrics may be throttled by Dynatrace. - -Full example: - - ```yaml -receivers: - # Collect own metrics and export them to Dynatrace - prometheus: - config: - scrape_configs: - - job_name: 'otel-collector' - scrape_interval: 10s - static_configs: - - targets: [ '0.0.0.0:8888' ] - -processors: - batch: - # Batch size must be less than or equal to 1000 - send_batch_max_size: 1000 - timeout: 30s - -exporters: - dynatrace: - # optional - Dimensions specified here will be included as a dimension on every exported metric - # unless that metric already has a dimension with the same key. - default_dimensions: - example_dimension: example value - - # optional - prefix will be prepended to each metric name in prefix.name format - prefix: my_prefix - - endpoint: https://abc12345.live.dynatrace.com/api/v2/metrics/ingest - # Token must at least have the Ingest metrics (metrics.ingest) permission - api_token: my_api_token - -service: - pipelines: - metrics: - receivers: [prometheus] - processors: [batch] - exporters: [dynatrace] - - ``` - -### Advanced Configuration - -Several helper files are leveraged to provide additional capabilities automatically: - -- [HTTP settings](https://github.com/open-telemetry/opentelemetry-collector/blob/main/config/confighttp/README.md) -- [TLS and mTLS settings](https://github.com/open-telemetry/opentelemetry-collector/blob/main/config/configtls/README.md) -- [Queuing, retry and timeout settings](https://github.com/open-telemetry/opentelemetry-collector/blob/main/exporter/exporterhelper/README.md) except timeout which is handled by the HTTP settings - -```yaml -receivers: - otlp: - protocols: - grpc: - endpoint: 0.0.0.0:4317 -exporters: - dynatrace: - endpoint: https://ab12345.live.dynatrace.com - api_token: - default_dimensions: - example_dimension: example value - prefix: my_prefix - headers: - - header1: value1 - read_buffer_size: 4000 - write_buffer_size: 4000 - timeout: 30s - tls: - insecure_skip_verify: false # (default=false) - retry_on_failure: - enabled: true - initial_interval: 5s - max_interval: 30s - max_elapsed_time: 120s - sending_queue: - enabled: true - num_consumers: 10 - queue_size: 1000 - resource_to_telemetry_conversion: - enabled: false -service: - extensions: - pipelines: - metrics: - receivers: [otlp] - exporters: [dynatrace] -``` - -#### default_dimensions (Optional) - -`default_dimensions` are included as dimensions on all exported metrics unless that metric already has a dimension with the same key. -`default_dimensions` is specified as a map of string key-value pairs. - -#### prefix (Optional) - -Prefix is a string which will be used as the first part of a dot-separated metric key. -For example, if a metric with name `request_count` is prefixed with `my_service`, the resulting -metric key is `my_service.request_count`. - -#### headers (Optional) - -Additional headers to be included with every outgoing http request. - -#### read_buffer_size (Optional) - -Defines the buffer size to allocate to the HTTP client for reading the response. - -Default: `4096` - -#### write_buffer_size (Optional) - -Defines the buffer size to allocate to the HTTP client for writing the payload - -Default: `4096` - -#### timeout (Optional) - -Timeout specifies a time limit for requests made by this -Client. The timeout includes connection time, any -redirects, and reading the response body. The timer remains -running after Get, Head, Post, or Do return and will -interrupt reading of the Response.Body. - -https://golang.org/pkg/net/http/#Client - -Default: `0` - -#### tls.insecure_skip_verify (Optional) - -Additionally you can configure TLS to be enabled but skip verifying the server's certificate chain. -This cannot be combined with `insecure` since `insecure` won't use TLS at all. -More details can be found in the collector readme for -[TLS and mTLS settings](https://github.com/open-telemetry/opentelemetry-collector/blob/main/config/configtls/README.md). - -Default: `false` - -#### retry_on_failure.enabled (Optional) - -Default: `true` - -#### retry_on_failure.initial_interval (Optional) - -Time to wait after the first failure before retrying; ignored if enabled is false. - -Default: `5s` - -#### retry_on_failure.max_interval (Optional) - -The upper bound on backoff; ignored if enabled is false - -Default: `30s` - -#### retry_on_failure.max_elapsed_time (Optional) - -The maximum amount of time spent trying to send a batch; ignored if enabled is false. - -Default: `120s` - -#### sending_queue.enabled (Optional) - -Default: `true` - -#### sending_queue.num_consumers (Optional) - -Number of consumers that dequeue batches; ignored if enabled is false. - -Default: `10` - -#### sending_queue.queue_size (Optional) - -Maximum number of batches kept in memory before data; ignored if enabled is false; -User should calculate this as `num_seconds * requests_per_second` where: - -- `num_seconds` is the number of seconds to buffer in case of a backend outage -- `requests_per_second` is the average number of requests per seconds. - -Default: `1000` - -#### resource_to_telemetry_conversion (Optional) - -When `resource_to_telemetry_conversion.enabled` is set to `true`, all resource -attributes will be included as metric dimensions in Dynatrace in addition to the -attributes present on the metric data point. - -Default: `false` - -> :warning: **Please note** that the Dynatrace API has a limit of `50` attributes -> per metric data point and any data point which exceeds this limit will be dropped. - -If you think you might exceed this limit, you should use the -[transform processor](https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/processor/transformprocessor) -to apply a filter, so only a select subset of your resource attributes are converted. - -```yaml -receivers: - otlp: - protocols: - grpc: - endpoint: 0.0.0.0:4317 -processors: - transform: - metrics: - queries: - - keep_keys(resource.attributes, "key1", "key2", "key3") -exporters: - dynatrace: - endpoint: https://ab12345.live.dynatrace.com - api_token: - resource_to_telemetry_conversion: - enabled: true -service: - pipelines: - metrics: - receivers: [otlp] - processors: [transform] - exporters: [dynatrace] -``` - -#### tags (Deprecated, Optional) - -**Deprecated: Please use [default_dimensions](#default_dimensions-optional) instead** - -### Temporality - -If possible when configuring your SDK, use DELTA temporality for Counter, Asynchronous Counter, and Histogram metrics. -Use CUMULATIVE temporality for UpDownCounter and Asynchronous UpDownCounter metrics. -When using OpenTelemetry SDKs to gather metrics data, setting the -`OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE` environment variable to `delta` -should correctly set temporality for all metrics. -You can check the [spec compliance matrix](https://github.com/open-telemetry/opentelemetry-specification/blob/main/spec-compliance-matrix.md#environment-variables) -if you are unsure if the SDK you are using supports this configuration. -You can read more about this and other configurations at -[OpenTelemetry Metrics Exporter - OTLP](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/metrics/sdk_exporters/otlp.md#additional-configuration). - -#### Considerations when exporting Cumulative Data Points - -Histogram metrics with CUMULATIVE temporality are NOT SUPPORTED and will NOT be exported. - -When possible, Sum metrics should use DELTA temporality. -When receiving Sum metrics with CUMULATIVE temporality, this exporter performs CUMULATIVE to DELTA conversion. -This conversion can lead to missing or inconsistent data, as described below: - -##### First Data Points are dropped - -Due to the conversion, the exporter will drop the first received data point -after a counter is created or reset as there is no previous data point to compare it to. -This can be circumvented by configuring the OpenTelemetry SDK to export DELTA values. - -#### Multi-instance collector deployment - -In a multiple-instance deployment of the OpenTelemetry Collector, the conversion -can produce inconsistent data unless it can be guaranteed that metrics from the -same source are processed by the same collector instance. This can be circumvented -by configuring the OpenTelemetry SDK to export DELTA values. - -[beta]:https://github.com/open-telemetry/opentelemetry-collector#beta -[contrib]:https://github.com/open-telemetry/opentelemetry-collector-releases/tree/main/distributions/otelcol-contrib -[AWS]:https://aws-otel.github.io/docs/partners/dynatrace diff --git a/exporter/dynatraceexporter/config/config.go b/exporter/dynatraceexporter/config/config.go deleted file mode 100644 index 5d9df8ac11b3..000000000000 --- a/exporter/dynatraceexporter/config/config.go +++ /dev/null @@ -1,70 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -package config // import "github.com/open-telemetry/opentelemetry-collector-contrib/exporter/dynatraceexporter/config" - -import ( - "errors" - "fmt" - "strings" - - "github.com/dynatrace-oss/dynatrace-metric-utils-go/metric/apiconstants" - "go.opentelemetry.io/collector/config/confighttp" - "go.opentelemetry.io/collector/config/configopaque" - "go.opentelemetry.io/collector/config/configretry" - "go.opentelemetry.io/collector/exporter/exporterhelper" - - "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/resourcetotelemetry" -) - -// Config defines configuration for the Dynatrace exporter. -type Config struct { - confighttp.ClientConfig `mapstructure:",squash"` - - exporterhelper.QueueSettings `mapstructure:"sending_queue"` - configretry.BackOffConfig `mapstructure:"retry_on_failure"` - ResourceToTelemetrySettings resourcetotelemetry.Settings `mapstructure:"resource_to_telemetry_conversion"` - - // Dynatrace API token with metrics ingest permission - APIToken string `mapstructure:"api_token"` - - // DefaultDimensions will be added to all exported metrics - DefaultDimensions map[string]string `mapstructure:"default_dimensions"` - - // String to prefix all metric names - Prefix string `mapstructure:"prefix"` - - // Tags will be added to all exported metrics - // Deprecated: Please use DefaultDimensions instead - Tags []string `mapstructure:"tags"` -} - -func (c *Config) Validate() error { - if err := c.QueueSettings.Validate(); err != nil { - return fmt.Errorf("queue settings has invalid configuration: %w", err) - } - - if c.ClientConfig.Headers == nil { - c.ClientConfig.Headers = make(map[string]configopaque.String) - } - c.APIToken = strings.TrimSpace(c.APIToken) - - if c.Endpoint == "" { - c.Endpoint = apiconstants.GetDefaultOneAgentEndpoint() - } else { - if c.APIToken == "" { - return errors.New("api_token is required if Endpoint is provided") - } - - c.ClientConfig.Headers["Authorization"] = configopaque.String(fmt.Sprintf("Api-Token %s", c.APIToken)) - } - - if !(strings.HasPrefix(c.Endpoint, "http://") || strings.HasPrefix(c.Endpoint, "https://")) { - return errors.New("endpoint must start with https:// or http://") - } - - c.ClientConfig.Headers["Content-Type"] = "text/plain; charset=UTF-8" - c.ClientConfig.Headers["User-Agent"] = "opentelemetry-collector" - - return nil -} diff --git a/exporter/dynatraceexporter/config/config_test.go b/exporter/dynatraceexporter/config/config_test.go deleted file mode 100644 index 6ac082b9b642..000000000000 --- a/exporter/dynatraceexporter/config/config_test.go +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -package config - -import ( - "testing" - - "github.com/dynatrace-oss/dynatrace-metric-utils-go/metric/apiconstants" - "github.com/stretchr/testify/assert" - "go.opentelemetry.io/collector/config/confighttp" - "go.opentelemetry.io/collector/exporter/exporterhelper" -) - -func TestConfig_Validate(t *testing.T) { - t.Run("Empty configuration", func(t *testing.T) { - c := &Config{} - err := c.Validate() - assert.NoError(t, err) - - assert.Equal(t, apiconstants.GetDefaultOneAgentEndpoint(), c.Endpoint, "Should use default OneAgent endpoint") - }) - - t.Run("Valid configuration", func(t *testing.T) { - c := &Config{ClientConfig: confighttp.ClientConfig{Endpoint: "http://example.com/"}, APIToken: "token"} - err := c.Validate() - assert.NoError(t, err) - - assert.Equal(t, "http://example.com/", c.Endpoint, "Should use provided endpoint") - }) - - t.Run("Invalid Endpoint", func(t *testing.T) { - c := &Config{ClientConfig: confighttp.ClientConfig{Endpoint: "example.com"}} - err := c.Validate() - assert.Error(t, err) - }) - - t.Run("Invalid QueueSettings", func(t *testing.T) { - c := &Config{QueueSettings: exporterhelper.QueueSettings{QueueSize: -1, Enabled: true}} - err := c.Validate() - assert.Error(t, err) - }) -} diff --git a/exporter/dynatraceexporter/config/package_test.go b/exporter/dynatraceexporter/config/package_test.go deleted file mode 100644 index cac5be666ad9..000000000000 --- a/exporter/dynatraceexporter/config/package_test.go +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -package config - -import ( - "testing" - - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - goleak.VerifyTestMain(m) -} diff --git a/exporter/dynatraceexporter/doc.go b/exporter/dynatraceexporter/doc.go deleted file mode 100644 index cd2b2aa4756c..000000000000 --- a/exporter/dynatraceexporter/doc.go +++ /dev/null @@ -1,5 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -//go:generate mdatagen metadata.yaml -package dynatraceexporter // import "github.com/open-telemetry/opentelemetry-collector-contrib/exporter/dynatraceexporter" diff --git a/exporter/dynatraceexporter/factory.go b/exporter/dynatraceexporter/factory.go deleted file mode 100644 index c01baa8ef51e..000000000000 --- a/exporter/dynatraceexporter/factory.go +++ /dev/null @@ -1,70 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -package dynatraceexporter // import "github.com/open-telemetry/opentelemetry-collector-contrib/exporter/dynatraceexporter" - -import ( - "context" - - "go.opentelemetry.io/collector/component" - "go.opentelemetry.io/collector/config/confighttp" - "go.opentelemetry.io/collector/config/configretry" - "go.opentelemetry.io/collector/exporter" - "go.opentelemetry.io/collector/exporter/exporterhelper" - - dtconfig "github.com/open-telemetry/opentelemetry-collector-contrib/exporter/dynatraceexporter/config" - "github.com/open-telemetry/opentelemetry-collector-contrib/exporter/dynatraceexporter/internal/metadata" - "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/resourcetotelemetry" -) - -// NewFactory creates a Dynatrace exporter factory -func NewFactory() exporter.Factory { - return exporter.NewFactory( - metadata.Type, - createDefaultConfig, - exporter.WithMetrics(createMetricsExporter, metadata.MetricsStability), - ) -} - -// createDefaultConfig creates the default exporter configuration -func createDefaultConfig() component.Config { - return &dtconfig.Config{ - BackOffConfig: configretry.NewDefaultBackOffConfig(), - QueueSettings: exporterhelper.NewDefaultQueueSettings(), - ResourceToTelemetrySettings: resourcetotelemetry.Settings{ - Enabled: false, - }, - - APIToken: "", - ClientConfig: confighttp.ClientConfig{Endpoint: ""}, - - Tags: []string{}, - DefaultDimensions: make(map[string]string), - } -} - -// createMetricsExporter creates a metrics exporter based on this -func createMetricsExporter( - ctx context.Context, - set exporter.CreateSettings, - c component.Config, -) (exporter.Metrics, error) { - - cfg := c.(*dtconfig.Config) - - exp := newMetricsExporter(set, cfg) - - exporter, err := exporterhelper.NewMetricsExporter( - ctx, - set, - cfg, - exp.PushMetricsData, - exporterhelper.WithQueue(cfg.QueueSettings), - exporterhelper.WithRetry(cfg.BackOffConfig), - exporterhelper.WithStart(exp.start), - ) - if err != nil { - return nil, err - } - return resourcetotelemetry.WrapMetricsExporter(cfg.ResourceToTelemetrySettings, exporter), nil -} diff --git a/exporter/dynatraceexporter/factory_test.go b/exporter/dynatraceexporter/factory_test.go deleted file mode 100644 index a4bccad37aed..000000000000 --- a/exporter/dynatraceexporter/factory_test.go +++ /dev/null @@ -1,144 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -package dynatraceexporter - -import ( - "path/filepath" - "testing" - - "github.com/dynatrace-oss/dynatrace-metric-utils-go/metric/apiconstants" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "go.opentelemetry.io/collector/component" - "go.opentelemetry.io/collector/component/componenttest" - "go.opentelemetry.io/collector/config/confighttp" - "go.opentelemetry.io/collector/config/configopaque" - "go.opentelemetry.io/collector/config/configretry" - "go.opentelemetry.io/collector/confmap/confmaptest" - "go.opentelemetry.io/collector/exporter/exporterhelper" - - dtconfig "github.com/open-telemetry/opentelemetry-collector-contrib/exporter/dynatraceexporter/config" - "github.com/open-telemetry/opentelemetry-collector-contrib/exporter/dynatraceexporter/internal/metadata" - "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/resourcetotelemetry" -) - -// Test that the factory creates the default configuration -func TestCreateDefaultConfig(t *testing.T) { - factory := NewFactory() - cfg := factory.CreateDefaultConfig() - - assert.Equal(t, &dtconfig.Config{ - BackOffConfig: configretry.NewDefaultBackOffConfig(), - QueueSettings: exporterhelper.NewDefaultQueueSettings(), - ResourceToTelemetrySettings: resourcetotelemetry.Settings{ - Enabled: false, - }, - - Tags: []string{}, - DefaultDimensions: make(map[string]string), - }, cfg, "failed to create default config") - - assert.NoError(t, componenttest.CheckConfigStruct(cfg)) -} - -func TestLoadConfig(t *testing.T) { - t.Parallel() - - cm, err := confmaptest.LoadConf(filepath.Join("testdata", "config.yml")) - require.NoError(t, err) - - tests := []struct { - id component.ID - expected component.Config - errorMessage string - }{ - { - id: component.NewIDWithName(metadata.Type, "defaults"), - expected: &dtconfig.Config{ - BackOffConfig: configretry.NewDefaultBackOffConfig(), - QueueSettings: exporterhelper.NewDefaultQueueSettings(), - - ClientConfig: confighttp.ClientConfig{ - Endpoint: apiconstants.GetDefaultOneAgentEndpoint(), - Headers: map[string]configopaque.String{ - "Content-Type": "text/plain; charset=UTF-8", - "User-Agent": "opentelemetry-collector"}, - }, - Tags: []string{}, - DefaultDimensions: make(map[string]string), - }, - }, - { - id: component.NewIDWithName(metadata.Type, "valid"), - expected: &dtconfig.Config{ - BackOffConfig: configretry.NewDefaultBackOffConfig(), - QueueSettings: exporterhelper.NewDefaultQueueSettings(), - - ClientConfig: confighttp.ClientConfig{ - Endpoint: "http://example.com/api/v2/metrics/ingest", - Headers: map[string]configopaque.String{ - "Authorization": "Api-Token token", - "Content-Type": "text/plain; charset=UTF-8", - "User-Agent": "opentelemetry-collector"}, - }, - APIToken: "token", - - Prefix: "myprefix", - - Tags: []string{}, - DefaultDimensions: map[string]string{ - "dimension_example": "dimension_value", - }, - }, - }, - { - id: component.NewIDWithName(metadata.Type, "valid_tags"), - expected: &dtconfig.Config{ - BackOffConfig: configretry.NewDefaultBackOffConfig(), - QueueSettings: exporterhelper.NewDefaultQueueSettings(), - - ClientConfig: confighttp.ClientConfig{ - Endpoint: "http://example.com/api/v2/metrics/ingest", - Headers: map[string]configopaque.String{ - "Authorization": "Api-Token token", - "Content-Type": "text/plain; charset=UTF-8", - "User-Agent": "opentelemetry-collector"}, - }, - APIToken: "token", - - Prefix: "myprefix", - - Tags: []string{"tag_example=tag_value"}, - DefaultDimensions: make(map[string]string), - }, - }, - { - id: component.NewIDWithName(metadata.Type, "bad_endpoint"), - errorMessage: "endpoint must start with https:// or http://", - }, - { - id: component.NewIDWithName(metadata.Type, "missing_token"), - errorMessage: "api_token is required if Endpoint is provided", - }, - } - - for _, tt := range tests { - t.Run(tt.id.String(), func(t *testing.T) { - factory := NewFactory() - cfg := factory.CreateDefaultConfig() - - sub, err := cm.Sub(tt.id.String()) - require.NoError(t, err) - require.NoError(t, component.UnmarshalConfig(sub, cfg)) - - if tt.expected == nil { - assert.EqualError(t, component.ValidateConfig(cfg), tt.errorMessage) - return - } - - assert.NoError(t, component.ValidateConfig(cfg)) - assert.Equal(t, tt.expected, cfg) - }) - } -} diff --git a/exporter/dynatraceexporter/generated_component_test.go b/exporter/dynatraceexporter/generated_component_test.go deleted file mode 100644 index cf9af9d2bc5f..000000000000 --- a/exporter/dynatraceexporter/generated_component_test.go +++ /dev/null @@ -1,126 +0,0 @@ -// Code generated by mdatagen. DO NOT EDIT. - -package dynatraceexporter - -import ( - "context" - "testing" - "time" - - "github.com/stretchr/testify/require" - "go.opentelemetry.io/collector/component" - "go.opentelemetry.io/collector/component/componenttest" - "go.opentelemetry.io/collector/confmap/confmaptest" - "go.opentelemetry.io/collector/exporter" - "go.opentelemetry.io/collector/exporter/exportertest" - "go.opentelemetry.io/collector/pdata/pcommon" - "go.opentelemetry.io/collector/pdata/plog" - "go.opentelemetry.io/collector/pdata/pmetric" - "go.opentelemetry.io/collector/pdata/ptrace" -) - -func TestComponentLifecycle(t *testing.T) { - factory := NewFactory() - - tests := []struct { - name string - createFn func(ctx context.Context, set exporter.CreateSettings, cfg component.Config) (component.Component, error) - }{ - - { - name: "metrics", - createFn: func(ctx context.Context, set exporter.CreateSettings, cfg component.Config) (component.Component, error) { - return factory.CreateMetricsExporter(ctx, set, cfg) - }, - }, - } - - cm, err := confmaptest.LoadConf("metadata.yaml") - require.NoError(t, err) - cfg := factory.CreateDefaultConfig() - sub, err := cm.Sub("tests::config") - require.NoError(t, err) - require.NoError(t, component.UnmarshalConfig(sub, cfg)) - - for _, test := range tests { - t.Run(test.name+"-shutdown", func(t *testing.T) { - c, err := test.createFn(context.Background(), exportertest.NewNopCreateSettings(), cfg) - require.NoError(t, err) - err = c.Shutdown(context.Background()) - require.NoError(t, err) - }) - t.Run(test.name+"-lifecycle", func(t *testing.T) { - c, err := test.createFn(context.Background(), exportertest.NewNopCreateSettings(), cfg) - require.NoError(t, err) - host := componenttest.NewNopHost() - err = c.Start(context.Background(), host) - require.NoError(t, err) - require.NotPanics(t, func() { - switch test.name { - case "logs": - e, ok := c.(exporter.Logs) - require.True(t, ok) - logs := generateLifecycleTestLogs() - if !e.Capabilities().MutatesData { - logs.MarkReadOnly() - } - err = e.ConsumeLogs(context.Background(), logs) - case "metrics": - e, ok := c.(exporter.Metrics) - require.True(t, ok) - metrics := generateLifecycleTestMetrics() - if !e.Capabilities().MutatesData { - metrics.MarkReadOnly() - } - err = e.ConsumeMetrics(context.Background(), metrics) - case "traces": - e, ok := c.(exporter.Traces) - require.True(t, ok) - traces := generateLifecycleTestTraces() - if !e.Capabilities().MutatesData { - traces.MarkReadOnly() - } - err = e.ConsumeTraces(context.Background(), traces) - } - }) - - err = c.Shutdown(context.Background()) - require.NoError(t, err) - }) - } -} - -func generateLifecycleTestLogs() plog.Logs { - logs := plog.NewLogs() - rl := logs.ResourceLogs().AppendEmpty() - rl.Resource().Attributes().PutStr("resource", "R1") - l := rl.ScopeLogs().AppendEmpty().LogRecords().AppendEmpty() - l.Body().SetStr("test log message") - l.SetTimestamp(pcommon.NewTimestampFromTime(time.Now())) - return logs -} - -func generateLifecycleTestMetrics() pmetric.Metrics { - metrics := pmetric.NewMetrics() - rm := metrics.ResourceMetrics().AppendEmpty() - rm.Resource().Attributes().PutStr("resource", "R1") - m := rm.ScopeMetrics().AppendEmpty().Metrics().AppendEmpty() - m.SetName("test_metric") - dp := m.SetEmptyGauge().DataPoints().AppendEmpty() - dp.Attributes().PutStr("test_attr", "value_1") - dp.SetIntValue(123) - dp.SetTimestamp(pcommon.NewTimestampFromTime(time.Now())) - return metrics -} - -func generateLifecycleTestTraces() ptrace.Traces { - traces := ptrace.NewTraces() - rs := traces.ResourceSpans().AppendEmpty() - rs.Resource().Attributes().PutStr("resource", "R1") - span := rs.ScopeSpans().AppendEmpty().Spans().AppendEmpty() - span.Attributes().PutStr("test_attr", "value_1") - span.SetName("test_span") - span.SetStartTimestamp(pcommon.NewTimestampFromTime(time.Now().Add(-1 * time.Second))) - span.SetEndTimestamp(pcommon.NewTimestampFromTime(time.Now())) - return traces -} diff --git a/exporter/dynatraceexporter/go.mod b/exporter/dynatraceexporter/go.mod deleted file mode 100644 index ecaf3758b02e..000000000000 --- a/exporter/dynatraceexporter/go.mod +++ /dev/null @@ -1,96 +0,0 @@ -// Deprecated: The Dynatrace metrics exporter is deprecated. After the end of 2023, no support, updates, or compatibility with newer OTel versions will be provided/guaranteed. See https://github.com/open-telemetry/opentelemetry-collector-contrib/pull/23992 for more details. -module github.com/open-telemetry/opentelemetry-collector-contrib/exporter/dynatraceexporter - -go 1.21 - -require ( - github.com/dynatrace-oss/dynatrace-metric-utils-go v0.5.0 - github.com/open-telemetry/opentelemetry-collector-contrib/internal/common v0.98.0 - github.com/open-telemetry/opentelemetry-collector-contrib/pkg/resourcetotelemetry v0.98.0 - github.com/stretchr/testify v1.9.0 - go.opentelemetry.io/collector/component v0.98.0 - go.opentelemetry.io/collector/config/confighttp v0.98.0 - go.opentelemetry.io/collector/config/configopaque v1.5.0 - go.opentelemetry.io/collector/config/configretry v0.98.0 - go.opentelemetry.io/collector/config/configtls v0.98.0 - go.opentelemetry.io/collector/confmap v0.98.0 - go.opentelemetry.io/collector/consumer v0.98.0 - go.opentelemetry.io/collector/exporter v0.98.0 - go.opentelemetry.io/collector/pdata v1.5.0 - go.opentelemetry.io/otel/metric v1.25.0 - go.opentelemetry.io/otel/trace v1.25.0 - go.uber.org/goleak v1.3.0 - go.uber.org/zap v1.27.0 -) - -require ( - github.com/beorn7/perks v1.0.1 // indirect - github.com/cenkalti/backoff/v4 v4.3.0 // indirect - github.com/cespare/xxhash/v2 v2.2.0 // indirect - github.com/davecgh/go-spew v1.1.1 // indirect - github.com/felixge/httpsnoop v1.0.4 // indirect - github.com/fsnotify/fsnotify v1.7.0 // indirect - github.com/go-logr/logr v1.4.1 // indirect - github.com/go-logr/stdr v1.2.2 // indirect - github.com/go-viper/mapstructure/v2 v2.0.0-alpha.1 // indirect - github.com/gogo/protobuf v1.3.2 // indirect - github.com/golang/protobuf v1.5.3 // indirect - github.com/golang/snappy v0.0.4 // indirect - github.com/google/uuid v1.6.0 // indirect - github.com/hashicorp/go-version v1.6.0 // indirect - github.com/json-iterator/go v1.1.12 // indirect - github.com/klauspost/compress v1.17.8 // indirect - github.com/knadh/koanf/maps v0.1.1 // indirect - github.com/knadh/koanf/providers/confmap v0.1.0 // indirect - github.com/knadh/koanf/v2 v2.1.1 // indirect - github.com/mitchellh/copystructure v1.2.0 // indirect - github.com/mitchellh/reflectwalk v1.0.2 // indirect - github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect - github.com/modern-go/reflect2 v1.0.2 // indirect - github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/prometheus/client_golang v1.19.0 // indirect - github.com/prometheus/client_model v0.6.1 // indirect - github.com/prometheus/common v0.48.0 // indirect - github.com/prometheus/procfs v0.12.0 // indirect - github.com/rs/cors v1.10.1 // indirect - go.opentelemetry.io/collector v0.98.0 // indirect - go.opentelemetry.io/collector/config/configauth v0.98.0 // indirect - go.opentelemetry.io/collector/config/configcompression v1.5.0 // indirect - go.opentelemetry.io/collector/config/configtelemetry v0.98.0 // indirect - go.opentelemetry.io/collector/config/internal v0.98.0 // indirect - go.opentelemetry.io/collector/extension v0.98.0 // indirect - go.opentelemetry.io/collector/extension/auth v0.98.0 // indirect - go.opentelemetry.io/collector/featuregate v1.5.0 // indirect - go.opentelemetry.io/collector/receiver v0.98.0 // indirect - go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 // indirect - go.opentelemetry.io/otel v1.25.0 // indirect - go.opentelemetry.io/otel/exporters/prometheus v0.47.0 // indirect - go.opentelemetry.io/otel/sdk v1.25.0 // indirect - go.opentelemetry.io/otel/sdk/metric v1.25.0 // indirect - go.uber.org/multierr v1.11.0 // indirect - golang.org/x/net v0.24.0 // indirect - golang.org/x/sys v0.19.0 // indirect - golang.org/x/text v0.14.0 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20240123012728-ef4313101c80 // indirect - google.golang.org/grpc v1.62.1 // indirect - google.golang.org/protobuf v1.33.0 // indirect - gopkg.in/yaml.v3 v3.0.1 // indirect -) - -replace github.com/open-telemetry/opentelemetry-collector-contrib/internal/common => ../../internal/common - -replace github.com/open-telemetry/opentelemetry-collector-contrib/pkg/resourcetotelemetry => ../../pkg/resourcetotelemetry - -replace github.com/open-telemetry/opentelemetry-collector-contrib/internal/coreinternal => ../../internal/coreinternal - -retract ( - v0.76.2 - v0.76.1 - v0.65.0 -) - -replace github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatautil => ../../pkg/pdatautil - -replace github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatatest => ../../pkg/pdatatest - -replace github.com/open-telemetry/opentelemetry-collector-contrib/pkg/golden => ../../pkg/golden diff --git a/exporter/dynatraceexporter/go.sum b/exporter/dynatraceexporter/go.sum deleted file mode 100644 index 485dc436fc15..000000000000 --- a/exporter/dynatraceexporter/go.sum +++ /dev/null @@ -1,184 +0,0 @@ -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.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= -github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= -github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= -github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -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/dynatrace-oss/dynatrace-metric-utils-go v0.5.0 h1:wHGPJSXvwKQVf/XfhjUPyrhpcPKWNy8F3ikH+eiwoBg= -github.com/dynatrace-oss/dynatrace-metric-utils-go v0.5.0/go.mod h1:PseHFo8Leko7J4A/TfZ6kkHdkzKBLUta6hRZR/OEbbc= -github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= -github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= -github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= -github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= -github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ= -github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= -github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= -github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= -github.com/go-viper/mapstructure/v2 v2.0.0-alpha.1 h1:TQcrn6Wq+sKGkpyPvppOz99zsMBaUOKXq6HSv655U1c= -github.com/go-viper/mapstructure/v2 v2.0.0-alpha.1/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= -github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= -github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= -github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= -github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= -github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= -github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= -github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mOkIeek= -github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= -github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= -github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= -github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= -github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/klauspost/compress v1.17.8 h1:YcnTYrq7MikUT7k0Yb5eceMmALQPYBW/Xltxn0NAMnU= -github.com/klauspost/compress v1.17.8/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= -github.com/knadh/koanf/maps v0.1.1 h1:G5TjmUh2D7G2YWf5SQQqSiHRJEjaicvU0KpypqB3NIs= -github.com/knadh/koanf/maps v0.1.1/go.mod h1:npD/QZY3V6ghQDdcQzl1W4ICNVTkohC8E73eI2xW4yI= -github.com/knadh/koanf/providers/confmap v0.1.0 h1:gOkxhHkemwG4LezxxN8DMOFopOPghxRVp7JbIvdvqzU= -github.com/knadh/koanf/providers/confmap v0.1.0/go.mod h1:2uLhxQzJnyHKfxG927awZC7+fyHFdQkd697K4MdLnIU= -github.com/knadh/koanf/v2 v2.1.1 h1:/R8eXqasSTsmDCsAyYj+81Wteg8AqrV9CP6gvsTsOmM= -github.com/knadh/koanf/v2 v2.1.1/go.mod h1:4mnTRbZCK+ALuBXHZMjDfG9y714L7TykVnZkXbMU3Es= -github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= -github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= -github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s= -github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= -github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= -github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= -github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= -github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= -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 v1.19.0 h1:ygXvpU1AoN1MhdzckN+PyD9QJOSD4x7kmXYlnfbA6JU= -github.com/prometheus/client_golang v1.19.0/go.mod h1:ZRM9uEAypZakd+q/x7+gmsvXdURP+DABIEIjnmDdp+k= -github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= -github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY= -github.com/prometheus/common v0.48.0 h1:QO8U2CdOzSn1BBsmXJXduaaW+dY/5QLjfB8svtSzKKE= -github.com/prometheus/common v0.48.0/go.mod h1:0/KsvlIEfPQCQ5I2iNSAWKPZziNCvRs5EC6ILDTlAPc= -github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo= -github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo= -github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= -github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= -github.com/rs/cors v1.10.1 h1:L0uuZVXIKlI1SShY2nhFfo44TYvDPQ1w4oFkUJNfhyo= -github.com/rs/cors v1.10.1/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= -github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= -github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -go.opentelemetry.io/collector v0.98.0 h1:O7bpARGWzNfFQEYevLl4iigDrpGTJY3vV/kKqNZzMOk= -go.opentelemetry.io/collector v0.98.0/go.mod h1:fvPM+tBML07uvAP1MV2msYPSYJ9U/lgE1jDb3AFBaMM= -go.opentelemetry.io/collector/component v0.98.0 h1:0TMaBOyCdABiVLFdGOgG8zd/1IeGldCinYonbY08xWk= -go.opentelemetry.io/collector/component v0.98.0/go.mod h1:F6zyQLsoExl6r2q6WWZm8rmSSALbwG2zwIHLrMzZVio= -go.opentelemetry.io/collector/config/configauth v0.98.0 h1:FPffZ1dRL6emStrDUEGpL0rCChbUZNAQgpArXD0SESI= -go.opentelemetry.io/collector/config/configauth v0.98.0/go.mod h1:5pMzf2zgFwS7tujNq0AtOOli5vxIvnrNi7JlZwrBOFo= -go.opentelemetry.io/collector/config/configcompression v1.5.0 h1:FTxKbFPN4LznRCH/GQ+b+0tAWmg80Y2eEka79S2sLZ0= -go.opentelemetry.io/collector/config/configcompression v1.5.0/go.mod h1:O0fOPCADyGwGLLIf5lf7N3960NsnIfxsm6dr/mIpL+M= -go.opentelemetry.io/collector/config/confighttp v0.98.0 h1:pW7gR34TTXcrCHJgemL6A4VBVBS2NyDAkruSMvQj1Vo= -go.opentelemetry.io/collector/config/confighttp v0.98.0/go.mod h1:M9PMtiKrTJMG8i3SqJ+AUVKhR6sa3G/8S2F1+Dxkkr0= -go.opentelemetry.io/collector/config/configopaque v1.5.0 h1:WJzgmsFU2v63BypPBNGL31ACwWn6PwumPJNpLZplcdE= -go.opentelemetry.io/collector/config/configopaque v1.5.0/go.mod h1:/otnfj2E8r5EfaAdNV4qHkTclmiBCZXaahV5EcLwT7k= -go.opentelemetry.io/collector/config/configretry v0.98.0 h1:gZRenX9oMLJmQ/CD8YwFNl9YYl68RtcD0RYSCJhrMAk= -go.opentelemetry.io/collector/config/configretry v0.98.0/go.mod h1:uRdmPeCkrW9Zsadh2WEbQ1AGXGYJ02vCfmmT+0g69nY= -go.opentelemetry.io/collector/config/configtelemetry v0.98.0 h1:f8RNZ1l/kYPPoxFmKKvTUli8iON7CMsm85KM38PVNts= -go.opentelemetry.io/collector/config/configtelemetry v0.98.0/go.mod h1:YV5PaOdtnU1xRomPcYqoHmyCr48tnaAREeGO96EZw8o= -go.opentelemetry.io/collector/config/configtls v0.98.0 h1:g+MADy01ge8iGC6v2tbJ5G27CWNG1BaJtmYdmpvm8e4= -go.opentelemetry.io/collector/config/configtls v0.98.0/go.mod h1:9RHArziz0mNEEkti0kz5LIdvbQGT7/Unu/0whKKazHQ= -go.opentelemetry.io/collector/config/internal v0.98.0 h1:wz/6ncawMX5cfIiXJEYSUm1g1U6iE/VxFRm4/WhVBPI= -go.opentelemetry.io/collector/config/internal v0.98.0/go.mod h1:xPnEE6QaTSXr+ctYMSTBxI2qwTntTUM4cYk7OTm6Ugc= -go.opentelemetry.io/collector/confmap v0.98.0 h1:qQreBlrqio1y7uhrAvr+W86YbQ6fw7StgkbYpvJ2vVc= -go.opentelemetry.io/collector/confmap v0.98.0/go.mod h1:BWKPIpYeUzSG6ZgCJMjF7xsLvyrvJCfYURl57E5vhiQ= -go.opentelemetry.io/collector/consumer v0.98.0 h1:47zJ5HFKXVA0RciuwkZnPU5W8j0TYUxToB1/zzzgEhs= -go.opentelemetry.io/collector/consumer v0.98.0/go.mod h1:c2edTq38uVJET/NE6VV7/Qpyznnlz8b6VE7J6TXD57c= -go.opentelemetry.io/collector/exporter v0.98.0 h1:eN2qtkiwpeX9gBu9JZw1k/CZ3N9wZE1aGJ1A0EvwJ7w= -go.opentelemetry.io/collector/exporter v0.98.0/go.mod h1:GCW46a0VAuW7nljlW//GgFXI+8mSrJjrdEKVO9icExE= -go.opentelemetry.io/collector/extension v0.98.0 h1:08B5ipEsoNmPHY96j5EUsUrFre01GOZ4zgttUDtPUkY= -go.opentelemetry.io/collector/extension v0.98.0/go.mod h1:fZ1Hnnahszl5j3xcW2sMRJ0FLWDOFkFMQeVDP0Se7i8= -go.opentelemetry.io/collector/extension/auth v0.98.0 h1:7b1jioijJbTMqaOCrz5Hoqf+zJn2iPlGmtN7pXLNWbA= -go.opentelemetry.io/collector/extension/auth v0.98.0/go.mod h1:gssWC4AxAwAEKI2CqS93lhjWffsVdzD8q7UGL6LaRr0= -go.opentelemetry.io/collector/featuregate v1.5.0 h1:uK8qnYQKz1TMkK+FDTFsywg/EybW/gbnOUaPNUkRznM= -go.opentelemetry.io/collector/featuregate v1.5.0/go.mod h1:w7nUODKxEi3FLf1HslCiE6YWtMtOOrMnSwsDam8Mg9w= -go.opentelemetry.io/collector/pdata v1.5.0 h1:1fKTmUpr0xCOhP/B0VEvtz7bYPQ45luQ8XFyA07j8LE= -go.opentelemetry.io/collector/pdata v1.5.0/go.mod h1:TYj8aKRWZyT/KuKQXKyqSEvK/GV+slFaDMEI+Ke64Yw= -go.opentelemetry.io/collector/pdata/testdata v0.98.0 h1:8gohV+LFXqMzuDwfOOQy9GcZBOX0C9xGoQkoeXFTzmI= -go.opentelemetry.io/collector/pdata/testdata v0.98.0/go.mod h1:B/IaHcf6+RtxI292CZu9TjfYQdi1n4+v6b8rHEonpKs= -go.opentelemetry.io/collector/receiver v0.98.0 h1:qw6JYwm+sHcZvM1DByo3QlGe6yGHuwd0yW4hEPVqYKU= -go.opentelemetry.io/collector/receiver v0.98.0/go.mod h1:AwIWn+KnquTR+kbhXQrMH+i2PvTCFldSIJznBWFYs0s= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 h1:jq9TW8u3so/bN+JPT166wjOI6/vQPF6Xe7nMNIltagk= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0/go.mod h1:p8pYQP+m5XfbZm9fxtSKAbM6oIllS7s2AfxrChvc7iw= -go.opentelemetry.io/otel v1.25.0 h1:gldB5FfhRl7OJQbUHt/8s0a7cE8fbsPAtdpRaApKy4k= -go.opentelemetry.io/otel v1.25.0/go.mod h1:Wa2ds5NOXEMkCmUou1WA7ZBfLTHWIsp034OVD7AO+Vg= -go.opentelemetry.io/otel/exporters/prometheus v0.47.0 h1:OL6yk1Z/pEGdDnrBbxSsH+t4FY1zXfBRGd7bjwhlMLU= -go.opentelemetry.io/otel/exporters/prometheus v0.47.0/go.mod h1:xF3N4OSICZDVbbYZydz9MHFro1RjmkPUKEvar2utG+Q= -go.opentelemetry.io/otel/metric v1.25.0 h1:LUKbS7ArpFL/I2jJHdJcqMGxkRdxpPHE0VU/D4NuEwA= -go.opentelemetry.io/otel/metric v1.25.0/go.mod h1:rkDLUSd2lC5lq2dFNrX9LGAbINP5B7WBkC78RXCpH5s= -go.opentelemetry.io/otel/sdk v1.25.0 h1:PDryEJPC8YJZQSyLY5eqLeafHtG+X7FWnf3aXMtxbqo= -go.opentelemetry.io/otel/sdk v1.25.0/go.mod h1:oFgzCM2zdsxKzz6zwpTZYLLQsFwc+K0daArPdIhuxkw= -go.opentelemetry.io/otel/sdk/metric v1.25.0 h1:7CiHOy08LbrxMAp4vWpbiPcklunUshVpAvGBrdDRlGw= -go.opentelemetry.io/otel/sdk/metric v1.25.0/go.mod h1:LzwoKptdbBBdYfvtGCzGwk6GWMA3aUzBOwtQpR6Nz7o= -go.opentelemetry.io/otel/trace v1.25.0 h1:tqukZGLwQYRIFtSQM2u2+yfMVTgGVeqRLPUYx1Dq6RM= -go.opentelemetry.io/otel/trace v1.25.0/go.mod h1:hCCs70XM/ljO+BeQkyFnbK28SBIJ/Emuha+ccrCRT7I= -go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= -go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= -go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= -go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= -go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= -go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w= -golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8= -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-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -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-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o= -golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= -golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240123012728-ef4313101c80 h1:AjyfHzEPEFp/NpvfN5g+KDla3EMojjhRVZc1i7cj+oM= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240123012728-ef4313101c80/go.mod h1:PAREbraiVEVGVdTZsVWjSbbTtSyGbAgIIvni8a8CD5s= -google.golang.org/grpc v1.62.1 h1:B4n+nfKzOICUXMgyrNd19h/I9oH0L1pizfk1d4zSgTk= -google.golang.org/grpc v1.62.1/go.mod h1:IWTG0VlJLCh1SkC58F7np9ka9mx/WNkjl4PGJaiq+QE= -google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= -google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/exporter/dynatraceexporter/internal/metadata/generated_status.go b/exporter/dynatraceexporter/internal/metadata/generated_status.go deleted file mode 100644 index b1e9fa13d455..000000000000 --- a/exporter/dynatraceexporter/internal/metadata/generated_status.go +++ /dev/null @@ -1,25 +0,0 @@ -// Code generated by mdatagen. DO NOT EDIT. - -package metadata - -import ( - "go.opentelemetry.io/collector/component" - "go.opentelemetry.io/otel/metric" - "go.opentelemetry.io/otel/trace" -) - -var ( - Type = component.MustNewType("dynatrace") -) - -const ( - MetricsStability = component.StabilityLevelDeprecated -) - -func Meter(settings component.TelemetrySettings) metric.Meter { - return settings.MeterProvider.Meter("otelcol/dynatrace") -} - -func Tracer(settings component.TelemetrySettings) trace.Tracer { - return settings.TracerProvider.Tracer("otelcol/dynatrace") -} diff --git a/exporter/dynatraceexporter/internal/serialization/gauge.go b/exporter/dynatraceexporter/internal/serialization/gauge.go deleted file mode 100644 index 8a8385a7b6ee..000000000000 --- a/exporter/dynatraceexporter/internal/serialization/gauge.go +++ /dev/null @@ -1,71 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -package serialization // import "github.com/open-telemetry/opentelemetry-collector-contrib/exporter/dynatraceexporter/internal/serialization" - -import ( - "fmt" - - dtMetric "github.com/dynatrace-oss/dynatrace-metric-utils-go/metric" - "github.com/dynatrace-oss/dynatrace-metric-utils-go/metric/dimensions" - "go.opentelemetry.io/collector/pdata/pmetric" - "go.uber.org/zap" -) - -func serializeGaugePoint(name, prefix string, dims dimensions.NormalizedDimensionList, dp pmetric.NumberDataPoint) (string, error) { - var metricOption dtMetric.MetricOption - - switch dp.ValueType() { - case pmetric.NumberDataPointValueTypeEmpty: - return "", fmt.Errorf("unsupported value type none") - case pmetric.NumberDataPointValueTypeInt: - metricOption = dtMetric.WithIntGaugeValue(dp.IntValue()) - case pmetric.NumberDataPointValueTypeDouble: - metricOption = dtMetric.WithFloatGaugeValue(dp.DoubleValue()) - default: - return "", fmt.Errorf("unknown data type") - } - - dm, err := dtMetric.NewMetric( - name, - dtMetric.WithPrefix(prefix), - dtMetric.WithDimensions(dims), - dtMetric.WithTimestamp(dp.Timestamp().AsTime()), - metricOption, - ) - - if err != nil { - return "", err - } - - return dm.Serialize() -} - -func serializeGauge(logger *zap.Logger, prefix string, metric pmetric.Metric, defaultDimensions dimensions.NormalizedDimensionList, staticDimensions dimensions.NormalizedDimensionList, metricLines []string) []string { - points := metric.Gauge().DataPoints() - - for i := 0; i < points.Len(); i++ { - dp := points.At(i) - - line, err := serializeGaugePoint( - metric.Name(), - prefix, - makeCombinedDimensions(defaultDimensions, dp.Attributes(), staticDimensions), - dp, - ) - - if err != nil { - logger.Warn( - "Error serializing gauge data point", - zap.String("name", metric.Name()), - zap.String("value-type", dp.ValueType().String()), - zap.Error(err), - ) - } - - if line != "" { - metricLines = append(metricLines, line) - } - } - return metricLines -} diff --git a/exporter/dynatraceexporter/internal/serialization/gauge_test.go b/exporter/dynatraceexporter/internal/serialization/gauge_test.go deleted file mode 100644 index af389c2e732b..000000000000 --- a/exporter/dynatraceexporter/internal/serialization/gauge_test.go +++ /dev/null @@ -1,180 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -package serialization - -import ( - "math" - "testing" - "time" - - "github.com/dynatrace-oss/dynatrace-metric-utils-go/metric/dimensions" - "github.com/stretchr/testify/assert" - "go.opentelemetry.io/collector/pdata/pcommon" - "go.opentelemetry.io/collector/pdata/pmetric" - "go.uber.org/zap" - "go.uber.org/zap/zaptest/observer" -) - -func Test_serializeGaugePoint(t *testing.T) { - t.Run("float with prefix and dimension", func(t *testing.T) { - dp := pmetric.NewNumberDataPoint() - dp.SetDoubleValue(5.5) - dp.SetTimestamp(pcommon.Timestamp(time.Date(2021, 07, 16, 12, 30, 0, 0, time.UTC).UnixNano())) - - got, err := serializeGaugePoint("dbl_gauge", "prefix", dimensions.NewNormalizedDimensionList(dimensions.NewDimension("key", "value")), dp) - assert.NoError(t, err) - assert.Equal(t, "prefix.dbl_gauge,key=value gauge,5.5 1626438600000", got) - }) - - t.Run("int with prefix and dimension", func(t *testing.T) { - dp := pmetric.NewNumberDataPoint() - dp.SetIntValue(5) - dp.SetTimestamp(pcommon.Timestamp(time.Date(2021, 07, 16, 12, 30, 0, 0, time.UTC).UnixNano())) - - got, err := serializeGaugePoint("int_gauge", "prefix", dimensions.NewNormalizedDimensionList(dimensions.NewDimension("key", "value")), dp) - assert.NoError(t, err) - assert.Equal(t, "prefix.int_gauge,key=value gauge,5 1626438600000", got) - }) - - t.Run("without timestamp", func(t *testing.T) { - dp := pmetric.NewNumberDataPoint() - dp.SetIntValue(5) - - got, err := serializeGaugePoint("int_gauge", "prefix", dimensions.NewNormalizedDimensionList(), dp) - assert.NoError(t, err) - assert.Equal(t, "prefix.int_gauge gauge,5", got) - }) -} - -func Test_serializeGauge(t *testing.T) { - type args struct { - prefix string - metricName string - intValues []int64 - floatValues []float64 - defaultDimensions dimensions.NormalizedDimensionList - staticDimensions dimensions.NormalizedDimensionList - } - - tests := []struct { - name string - args args - want []string - wantLogs []simplifiedLogRecord - }{ - { - name: "no data points", - args: args{ - metricName: "name", - intValues: []int64{}, - }, - want: []string{}, - wantLogs: []simplifiedLogRecord{}, - }, - { - name: "basic int gauge", - args: args{ - prefix: "prefix", - metricName: "name", - intValues: []int64{1, 2, 3}, - }, - want: []string{ - "prefix.name gauge,1", - "prefix.name gauge,2", - "prefix.name gauge,3", - }, - wantLogs: []simplifiedLogRecord{}, - }, - { - name: "invalid name", - args: args{ - metricName: ".", - intValues: []int64{3}, - }, - want: []string{}, - wantLogs: []simplifiedLogRecord{ - { - message: "Error serializing gauge data point", - attributes: map[string]string{ - "name": ".", - "value-type": "Int", - "error": "first key section is empty (.)", - }, - }, - }, - }, - { - name: "invalid double values", - args: args{ - metricName: "metric_name", - floatValues: []float64{ - math.Inf(-1), - math.Inf(1), - math.NaN(), - }, - }, - want: []string{}, - wantLogs: []simplifiedLogRecord{ - { - message: "Error serializing gauge data point", - attributes: map[string]string{ - "name": "metric_name", - "value-type": "Double", - "error": "value is infinite", - }, - }, - { - message: "Error serializing gauge data point", - attributes: map[string]string{ - "name": "metric_name", - "value-type": "Double", - "error": "value is infinite", - }, - }, - { - message: "Error serializing gauge data point", - attributes: map[string]string{ - "name": "metric_name", - "value-type": "Double", - "error": "value is NaN.", - }, - }, - }, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - zapCore, observedLogs := observer.New(zap.WarnLevel) - logger := zap.New(zapCore) - - metric := pmetric.NewMetric() - metric.SetName(tt.args.metricName) - dataPoints := metric.SetEmptyGauge().DataPoints() - - if tt.args.intValues != nil { - if tt.args.floatValues != nil { - t.Fatal("both int and float values set") - } - for _, intVal := range tt.args.intValues { - dataPoints.AppendEmpty().SetIntValue(intVal) - } - } - if tt.args.floatValues != nil { - for _, floatVal := range tt.args.floatValues { - dataPoints.AppendEmpty().SetDoubleValue(floatVal) - } - } - - actual := serializeGauge(logger, tt.args.prefix, metric, tt.args.defaultDimensions, tt.args.staticDimensions, []string{}) - - assert.ElementsMatch(t, actual, tt.want) - - // check that logs contain the expected messages. - if tt.wantLogs != nil { - observedLogRecords := makeSimplifiedLogRecordsFromObservedLogs(observedLogs) - assert.ElementsMatch(t, observedLogRecords, tt.wantLogs) - } - }) - } -} diff --git a/exporter/dynatraceexporter/internal/serialization/histogram.go b/exporter/dynatraceexporter/internal/serialization/histogram.go deleted file mode 100644 index 5b752a39765d..000000000000 --- a/exporter/dynatraceexporter/internal/serialization/histogram.go +++ /dev/null @@ -1,184 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -package serialization // import "github.com/open-telemetry/opentelemetry-collector-contrib/exporter/dynatraceexporter/internal/serialization" - -import ( - dtMetric "github.com/dynatrace-oss/dynatrace-metric-utils-go/metric" - "github.com/dynatrace-oss/dynatrace-metric-utils-go/metric/dimensions" - "go.opentelemetry.io/collector/pdata/pmetric" - "go.uber.org/zap" -) - -func serializeHistogramPoint(name, prefix string, dims dimensions.NormalizedDimensionList, dp pmetric.HistogramDataPoint) (string, error) { - if dp.Count() == 0 { - return "", nil - } - - min, max, sum := histDataPointToSummary(dp) - - dm, err := dtMetric.NewMetric( - name, - dtMetric.WithPrefix(prefix), - dtMetric.WithDimensions(dims), - dtMetric.WithTimestamp(dp.Timestamp().AsTime()), - dtMetric.WithFloatSummaryValue(min, max, sum, int64(dp.Count())), - ) - - if err != nil { - return "", err - } - - return dm.Serialize() -} - -func serializeHistogram(logger *zap.Logger, prefix string, metric pmetric.Metric, defaultDimensions dimensions.NormalizedDimensionList, staticDimensions dimensions.NormalizedDimensionList, metricLines []string) []string { - hist := metric.Histogram() - - if hist.AggregationTemporality() == pmetric.AggregationTemporalityCumulative { - logger.Warn( - "dropping cumulative histogram", - zap.String("name", metric.Name()), - ) - return metricLines - } - - for i := 0; i < hist.DataPoints().Len(); i++ { - dp := hist.DataPoints().At(i) - - line, err := serializeHistogramPoint( - metric.Name(), - prefix, - makeCombinedDimensions(defaultDimensions, dp.Attributes(), staticDimensions), - dp, - ) - - if err != nil { - logger.Warn( - "Error serializing histogram data point", - zap.String("name", metric.Name()), - zap.Error(err), - ) - } - - if line != "" { - metricLines = append(metricLines, line) - } - } - return metricLines -} - -// histDataPointToSummary returns the estimated minimum and maximum value in the histogram by using the min and max non-empty buckets. -// It MAY NOT be called with a data point with dp.Count() == 0. -func histDataPointToSummary(dp pmetric.HistogramDataPoint) (float64, float64, float64) { - bounds := dp.ExplicitBounds() - counts := dp.BucketCounts() - - // shortcut if min, max, and sum are provided - if dp.HasMin() && dp.HasMax() && dp.HasSum() { - return dp.Min(), dp.Max(), dp.Sum() - } - - // a single-bucket histogram is a special case - if counts.Len() == 1 { - return estimateSingleBucketHistogram(dp) - } - - // If any of min, max, sum is not provided in the data point, - // loop through the buckets to estimate them. - // All three values are estimated in order to avoid looping multiple times - // or complicating the loop with branches. After the loop, estimates - // will be overridden with any values provided by the data point. - foundNonEmptyBucket := false - var min, max, sum float64 = 0, 0, 0 - - // Because we do not know the actual min, max, or sum, we estimate them based on non-empty buckets - for i := 0; i < counts.Len(); i++ { - // empty bucket - if counts.At(i) == 0 { - continue - } - - // range for bucket counts[i] is bounds[i-1] to bounds[i] - - // min estimation - if !foundNonEmptyBucket { - foundNonEmptyBucket = true - if i == 0 { - // if we're in the first bucket, the best estimate we can make for min is the upper bound - min = bounds.At(i) - } else { - min = bounds.At(i - 1) - } - } - - // max estimation - if i == counts.Len()-1 { - // if we're in the last bucket, the best estimate we can make for max is the lower bound - max = bounds.At(i - 1) - } else { - max = bounds.At(i) - } - - // sum estimation - switch i { - case 0: - // in the first bucket, estimate sum using the upper bound - sum += float64(counts.At(i)) * bounds.At(i) - case counts.Len() - 1: - // in the last bucket, estimate sum using the lower bound - sum += float64(counts.At(i)) * bounds.At(i-1) - default: - // in any other bucket, estimate sum using the bucket midpoint - sum += float64(counts.At(i)) * (bounds.At(i) + bounds.At(i-1)) / 2 - } - } - - // Override estimates with any values provided by the data point - if dp.HasMin() { - min = dp.Min() - } - if dp.HasMax() { - max = dp.Max() - } - if dp.HasSum() { - sum = dp.Sum() - } - - // Set min to average when higher than average. This can happen when most values are lower than first boundary (falling in first bucket). - // Set max to average when lower than average. This can happen when most values are higher than last boundary (falling in last bucket). - // dp.Count() will never be zero - avg := sum / float64(dp.Count()) - if min > avg { - min = avg - } - if max < avg { - max = avg - } - - return min, max, sum -} - -func estimateSingleBucketHistogram(dp pmetric.HistogramDataPoint) (float64, float64, float64) { - var min, max, sum float64 - - if dp.HasSum() { - sum = dp.Sum() - } - - mean := sum / float64(dp.Count()) - - if dp.HasMin() { - min = dp.Min() - } else { - min = mean - } - - if dp.HasMax() { - max = dp.Max() - } else { - max = mean - } - - return min, max, sum -} diff --git a/exporter/dynatraceexporter/internal/serialization/histogram_test.go b/exporter/dynatraceexporter/internal/serialization/histogram_test.go deleted file mode 100644 index 9daffe5aacb2..000000000000 --- a/exporter/dynatraceexporter/internal/serialization/histogram_test.go +++ /dev/null @@ -1,398 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -package serialization - -import ( - "testing" - "time" - - "github.com/dynatrace-oss/dynatrace-metric-utils-go/metric/dimensions" - "github.com/stretchr/testify/assert" - "go.opentelemetry.io/collector/pdata/pcommon" - "go.opentelemetry.io/collector/pdata/pmetric" - "go.uber.org/zap" - "go.uber.org/zap/zaptest/observer" -) - -func Test_serializeHistogramPoint(t *testing.T) { - hist := pmetric.NewHistogramDataPoint() - hist.ExplicitBounds().FromRaw([]float64{0, 2, 4, 8}) - hist.BucketCounts().FromRaw([]uint64{0, 1, 0, 1, 0}) - hist.SetCount(2) - hist.SetSum(9.5) - hist.SetTimestamp(pcommon.Timestamp(time.Date(2021, 07, 16, 12, 30, 0, 0, time.UTC).UnixNano())) - - t.Run("delta with prefix and dimension", func(t *testing.T) { - got, err := serializeHistogramPoint("delta_hist", "prefix", dimensions.NewNormalizedDimensionList(dimensions.NewDimension("key", "value")), hist) - assert.NoError(t, err) - assert.Equal(t, "prefix.delta_hist,key=value gauge,min=0,max=8,sum=9.5,count=2 1626438600000", got) - }) - - t.Run("delta with non-empty first and last bucket", func(t *testing.T) { - histWithNonEmptyFirstLast := pmetric.NewHistogramDataPoint() - histWithNonEmptyFirstLast.ExplicitBounds().FromRaw([]float64{0, 2, 4, 8}) - histWithNonEmptyFirstLast.BucketCounts().FromRaw([]uint64{0, 1, 0, 1, 1}) - histWithNonEmptyFirstLast.SetCount(3) - histWithNonEmptyFirstLast.SetSum(9.5) - histWithNonEmptyFirstLast.SetTimestamp(pcommon.Timestamp(time.Date(2021, 07, 16, 12, 30, 0, 0, time.UTC).UnixNano())) - - got, err := serializeHistogramPoint("delta_nonempty_first_last_hist", "prefix", dimensions.NewNormalizedDimensionList(dimensions.NewDimension("key", "value")), histWithNonEmptyFirstLast) - assert.NoError(t, err) - assert.Equal(t, "prefix.delta_nonempty_first_last_hist,key=value gauge,min=0,max=8,sum=9.5,count=3 1626438600000", got) - }) - - t.Run("when average > highest boundary, max = average", func(t *testing.T) { - // average = 15, highest boundary = 10 - histWitMaxGreaterAvg := pmetric.NewHistogramDataPoint() - histWitMaxGreaterAvg.ExplicitBounds().FromRaw([]float64{0, 10}) - histWitMaxGreaterAvg.BucketCounts().FromRaw([]uint64{0, 0, 2}) - histWitMaxGreaterAvg.SetCount(2) - histWitMaxGreaterAvg.SetSum(30) - histWitMaxGreaterAvg.SetTimestamp(pcommon.Timestamp(time.Date(2021, 07, 16, 12, 30, 0, 0, time.UTC).UnixNano())) - - got, err := serializeHistogramPoint("delta_nonempty_first_last_hist", "prefix", dimensions.NewNormalizedDimensionList(dimensions.NewDimension("key", "value")), histWitMaxGreaterAvg) - assert.NoError(t, err) - assert.Equal(t, "prefix.delta_nonempty_first_last_hist,key=value gauge,min=10,max=15,sum=30,count=2 1626438600000", got) - }) - - t.Run("when average < lowest boundary, min = average", func(t *testing.T) { - // average = 5, lowest boundary = 10 - histWitMinLessAvg := pmetric.NewHistogramDataPoint() - histWitMinLessAvg.ExplicitBounds().FromRaw([]float64{10, 20}) - histWitMinLessAvg.BucketCounts().FromRaw([]uint64{2, 0, 0}) - histWitMinLessAvg.SetCount(2) - histWitMinLessAvg.SetSum(10) - histWitMinLessAvg.SetTimestamp(pcommon.Timestamp(time.Date(2021, 07, 16, 12, 30, 0, 0, time.UTC).UnixNano())) - - got, err := serializeHistogramPoint("delta_nonempty_first_last_hist", "prefix", dimensions.NewNormalizedDimensionList(dimensions.NewDimension("key", "value")), histWitMinLessAvg) - assert.NoError(t, err) - assert.Equal(t, "prefix.delta_nonempty_first_last_hist,key=value gauge,min=5,max=10,sum=10,count=2 1626438600000", got) - }) - - t.Run("when min is provided it should be used", func(t *testing.T) { - minMaxHist := pmetric.NewHistogramDataPoint() - minMaxHist.ExplicitBounds().FromRaw([]float64{10, 20}) - minMaxHist.BucketCounts().FromRaw([]uint64{2, 0, 0}) - minMaxHist.SetCount(2) - minMaxHist.SetSum(10) - minMaxHist.SetMin(3) - minMaxHist.SetTimestamp(pcommon.Timestamp(time.Date(2021, 07, 16, 12, 30, 0, 0, time.UTC).UnixNano())) - - got, err := serializeHistogramPoint("min_max_hist", "prefix", dimensions.NewNormalizedDimensionList(), minMaxHist) - assert.NoError(t, err) - // min 3, max 10, sum 10 is impossible but passes consistency check because the estimated max 10 is greater than the mean 5 - // it is the best we can do without a better max estimate - assert.Equal(t, "prefix.min_max_hist gauge,min=3,max=10,sum=10,count=2 1626438600000", got) - }) - - t.Run("when max is provided it should be used", func(t *testing.T) { - minMaxHist := pmetric.NewHistogramDataPoint() - minMaxHist.ExplicitBounds().FromRaw([]float64{10, 20}) - minMaxHist.BucketCounts().FromRaw([]uint64{2, 0, 0}) - minMaxHist.SetCount(2) - minMaxHist.SetSum(10) - minMaxHist.SetMax(7) - minMaxHist.SetTimestamp(pcommon.Timestamp(time.Date(2021, 07, 16, 12, 30, 0, 0, time.UTC).UnixNano())) - - got, err := serializeHistogramPoint("min_max_hist", "prefix", dimensions.NewNormalizedDimensionList(), minMaxHist) - assert.NoError(t, err) - // min 5, max 7, sum 10 is impossible with count 2 but passes consistency check because the estimated min 10 is reduced to the mean 5 - // it is the best we can do without a better min estimate - assert.Equal(t, "prefix.min_max_hist gauge,min=5,max=7,sum=10,count=2 1626438600000", got) - }) - - t.Run("when min and max is provided it should be used", func(t *testing.T) { - minMaxHist := pmetric.NewHistogramDataPoint() - minMaxHist.ExplicitBounds().FromRaw([]float64{10, 20}) - minMaxHist.BucketCounts().FromRaw([]uint64{2, 0, 0}) - minMaxHist.SetCount(2) - minMaxHist.SetSum(10) - minMaxHist.SetMin(3) - minMaxHist.SetMax(7) - minMaxHist.SetTimestamp(pcommon.Timestamp(time.Date(2021, 07, 16, 12, 30, 0, 0, time.UTC).UnixNano())) - - got, err := serializeHistogramPoint("min_max_hist", "prefix", dimensions.NewNormalizedDimensionList(), minMaxHist) - assert.NoError(t, err) - assert.Equal(t, "prefix.min_max_hist gauge,min=3,max=7,sum=10,count=2 1626438600000", got) - }) - - t.Run("when min is not provided it should be estimated", func(t *testing.T) { - t.Run("values between first two boundaries", func(t *testing.T) { - hist := pmetric.NewHistogramDataPoint() - hist.ExplicitBounds().FromRaw([]float64{1, 2, 3, 4, 5}) - hist.BucketCounts().FromRaw([]uint64{0, 1, 0, 3, 2, 0}) - hist.SetCount(6) - hist.SetSum(21.2) - - min, _, _ := histDataPointToSummary(hist) - - assert.Equal(t, 1.0, min, "use bucket min") - }) - - t.Run("first bucket has value", func(t *testing.T) { - hist := pmetric.NewHistogramDataPoint() - hist.ExplicitBounds().FromRaw([]float64{1, 2, 3, 4, 5}) - hist.BucketCounts().FromRaw([]uint64{1, 0, 0, 3, 0, 4}) - hist.SetCount(8) - hist.SetSum(34.5) - - min, _, _ := histDataPointToSummary(hist) - - assert.Equal(t, 1.0, min, "use the first boundary as estimation instead of Inf") - }) - - t.Run("only the first bucket has values, use the mean", func(t *testing.T) { - hist := pmetric.NewHistogramDataPoint() - hist.ExplicitBounds().FromRaw([]float64{1, 2, 3, 4, 5}) - hist.BucketCounts().FromRaw([]uint64{3, 0, 0, 0, 0, 0}) - hist.SetCount(3) - hist.SetSum(0.75) - - min, _, _ := histDataPointToSummary(hist) - - assert.Equal(t, 0.25, min) - }) - t.Run("just one bucket from -Inf to Inf", func(t *testing.T) { - hist := pmetric.NewHistogramDataPoint() - hist.ExplicitBounds().FromRaw([]float64{}) - hist.BucketCounts().FromRaw([]uint64{4}) - hist.SetCount(4) - hist.SetSum(8.8) - - min, _, _ := histDataPointToSummary(hist) - - assert.Equal(t, 2.2, min, "calculate the mean as min value") - }) - t.Run("just one bucket from -Inf to Inf", func(t *testing.T) { - hist := pmetric.NewHistogramDataPoint() - hist.ExplicitBounds().FromRaw([]float64{}) - hist.BucketCounts().FromRaw([]uint64{1}) - hist.SetCount(1) - hist.SetSum(1.2) - - min, _, _ := histDataPointToSummary(hist) - - assert.Equal(t, 1.2, min, "calculate the mean as min value") - }) - t.Run("only the last bucket has a value", func(t *testing.T) { - hist := pmetric.NewHistogramDataPoint() - hist.ExplicitBounds().FromRaw([]float64{1, 2, 3, 4, 5}) - hist.BucketCounts().FromRaw([]uint64{0, 0, 0, 0, 0, 3}) - hist.SetCount(3) - hist.SetSum(15.6) - - min, _, _ := histDataPointToSummary(hist) - - assert.Equal(t, 5.0, min, "use the lower bound") - }) - }) - - t.Run("when max is not provided it should be estimated", func(t *testing.T) { - t.Run("values between the last two boundaries", func(t *testing.T) { - hist := pmetric.NewHistogramDataPoint() - hist.ExplicitBounds().FromRaw([]float64{1, 2, 3, 4, 5}) - hist.BucketCounts().FromRaw([]uint64{0, 1, 0, 3, 2, 0}) - hist.SetSum(21.2) - hist.SetCount(6) - - _, max, _ := histDataPointToSummary(hist) - - assert.Equal(t, 5.0, max, "use bucket max") - }) - - t.Run("last bucket has value", func(t *testing.T) { - hist := pmetric.NewHistogramDataPoint() - hist.ExplicitBounds().FromRaw([]float64{1, 2, 3, 4, 5}) - hist.BucketCounts().FromRaw([]uint64{1, 0, 0, 3, 0, 4}) - hist.SetSum(34.5) - hist.SetCount(8) - - _, max, _ := histDataPointToSummary(hist) - - assert.Equal(t, 5.0, max, "use the last boundary as estimation instead of Inf") - }) - - t.Run("only the last bucket has values", func(t *testing.T) { - hist := pmetric.NewHistogramDataPoint() - hist.ExplicitBounds().FromRaw([]float64{1, 2, 3, 4, 5}) - hist.BucketCounts().FromRaw([]uint64{0, 0, 0, 0, 0, 2}) - hist.SetSum(20.2) - hist.SetCount(2) - - _, max, _ := histDataPointToSummary(hist) - - assert.Equal(t, 10.1, max, "use the mean (10.1) Otherwise, the max would be estimated as 5, and max >= avg would be violated") - }) - - t.Run("just one bucket from -Inf to Inf", func(t *testing.T) { - hist := pmetric.NewHistogramDataPoint() - hist.ExplicitBounds().FromRaw([]float64{}) - hist.BucketCounts().FromRaw([]uint64{4}) - hist.SetSum(8.8) - hist.SetCount(4) - - _, max, _ := histDataPointToSummary(hist) - - assert.Equal(t, 2.2, max, "calculate the mean as max value") - }) - - t.Run("just one bucket from -Inf to Inf", func(t *testing.T) { - hist := pmetric.NewHistogramDataPoint() - hist.ExplicitBounds().FromRaw([]float64{}) - hist.BucketCounts().FromRaw([]uint64{1}) - hist.SetSum(1.2) - hist.SetCount(1) - - _, max, _ := histDataPointToSummary(hist) - - assert.Equal(t, 1.2, max, "calculate the mean as max value") - }) - - t.Run("max is larger than sum", func(t *testing.T) { - hist := pmetric.NewHistogramDataPoint() - hist.ExplicitBounds().FromRaw([]float64{0, 5}) - hist.BucketCounts().FromRaw([]uint64{0, 2, 0}) - hist.SetSum(2.3) - hist.SetCount(2) - - _, max, _ := histDataPointToSummary(hist) - - assert.Equal(t, 5.0, max, "use the estimated boundary") - }) - }) - - t.Run("when sum is not provided it should be estimated", func(t *testing.T) { - t.Run("single bucket histogram", func(t *testing.T) { - hist := pmetric.NewHistogramDataPoint() - hist.ExplicitBounds().FromRaw([]float64{}) - hist.BucketCounts().FromRaw([]uint64{13}) - hist.SetCount(6) - - _, _, sum := histDataPointToSummary(hist) - - assert.Equal(t, 0.0, sum, "estimate zero (midpoint of [-Inf, Inf])") - }) - - t.Run("data in bounded buckets", func(t *testing.T) { - hist := pmetric.NewHistogramDataPoint() - hist.ExplicitBounds().FromRaw([]float64{1, 2, 3, 4, 5}) - hist.BucketCounts().FromRaw([]uint64{0, 3, 5, 0, 0, 0}) - hist.SetCount(6) - - _, _, sum := histDataPointToSummary(hist) - - assert.Equal(t, 3*1.5+5*2.5, sum, "estimate sum using bucket midpoints") - }) - - t.Run("data in unbounded buckets", func(t *testing.T) { - t.Run("first bucket", func(t *testing.T) { - hist := pmetric.NewHistogramDataPoint() - hist.ExplicitBounds().FromRaw([]float64{1, 2, 3, 4, 5}) - hist.BucketCounts().FromRaw([]uint64{2, 3, 5, 0, 0, 0}) - hist.SetCount(6) - - _, _, sum := histDataPointToSummary(hist) - - assert.Equal(t, 1*2+3*1.5+5*2.5, sum, "use bucket upper bound") - }) - - t.Run("last bucket", func(t *testing.T) { - hist := pmetric.NewHistogramDataPoint() - hist.ExplicitBounds().FromRaw([]float64{1, 2, 3, 4, 5}) - hist.BucketCounts().FromRaw([]uint64{0, 3, 5, 0, 0, 2}) - hist.SetCount(6) - - _, _, sum := histDataPointToSummary(hist) - - assert.Equal(t, 3*1.5+5*2.5+2*5, sum, "use bucket upper bound") - }) - }) - }) -} - -func Test_serializeHistogram(t *testing.T) { - emptyDims := dimensions.NewNormalizedDimensionList() - t.Run("wrong aggregation temporality", func(t *testing.T) { - metric := pmetric.NewMetric() - metric.SetName("metric_name") - hist := metric.SetEmptyHistogram() - hist.SetAggregationTemporality(pmetric.AggregationTemporalityCumulative) - - zapCore, observedLogs := observer.New(zap.WarnLevel) - logger := zap.New(zapCore) - - lines := serializeHistogram(logger, "", metric, emptyDims, emptyDims, []string{}) - assert.Empty(t, lines) - - actualLogRecords := makeSimplifiedLogRecordsFromObservedLogs(observedLogs) - - expectedLogRecords := []simplifiedLogRecord{ - { - message: "dropping cumulative histogram", - attributes: map[string]string{ - "name": "metric_name", - }, - }, - } - - assert.ElementsMatch(t, actualLogRecords, expectedLogRecords) - }) - - t.Run("serialize returns error", func(t *testing.T) { - // just testing one case to make sure the error reporting works, - // the actual testing is done in Test_serializeHistogramPoint - metric := pmetric.NewMetric() - metric.SetName("metric_name") - hist := metric.SetEmptyHistogram() - hist.SetAggregationTemporality(pmetric.AggregationTemporalityDelta) - dp := hist.DataPoints().AppendEmpty() - dp.SetMin(10) - dp.SetMax(3) - dp.SetCount(1) - dp.SetSum(30) - - zapCore, observedLogs := observer.New(zap.WarnLevel) - logger := zap.New(zapCore) - - lines := serializeHistogram(logger, "", metric, emptyDims, emptyDims, []string{}) - assert.Empty(t, lines) - - expectedLogRecords := []simplifiedLogRecord{ - { - message: "Error serializing histogram data point", - attributes: map[string]string{ - "name": "metric_name", - "error": "min (10.000) cannot be greater than max (3.000)", - }, - }, - } - - assert.ElementsMatch(t, makeSimplifiedLogRecordsFromObservedLogs(observedLogs), expectedLogRecords) - }) - - t.Run("histogram serialized as summary", func(t *testing.T) { - metric := pmetric.NewMetric() - metric.SetName("metric_name") - hist := metric.SetEmptyHistogram() - hist.SetAggregationTemporality(pmetric.AggregationTemporalityDelta) - dp := hist.DataPoints().AppendEmpty() - dp.SetMin(1) - dp.SetMax(5) - dp.SetCount(3) - dp.SetSum(8) - - zapCore, observedLogs := observer.New(zap.WarnLevel) - logger := zap.New(zapCore) - - lines := serializeHistogram(logger, "", metric, emptyDims, emptyDims, []string{}) - - expectedLines := []string{ - "metric_name gauge,min=1,max=5,sum=8,count=3", - } - - assert.ElementsMatch(t, lines, expectedLines) - - assert.Empty(t, observedLogs.All()) - }) -} diff --git a/exporter/dynatraceexporter/internal/serialization/package_test.go b/exporter/dynatraceexporter/internal/serialization/package_test.go deleted file mode 100644 index 5be5ed5b2865..000000000000 --- a/exporter/dynatraceexporter/internal/serialization/package_test.go +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -package serialization - -import ( - "testing" - - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - goleak.VerifyTestMain(m) -} diff --git a/exporter/dynatraceexporter/internal/serialization/serialization.go b/exporter/dynatraceexporter/internal/serialization/serialization.go deleted file mode 100644 index a3f3b1b90d27..000000000000 --- a/exporter/dynatraceexporter/internal/serialization/serialization.go +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -package serialization // import "github.com/open-telemetry/opentelemetry-collector-contrib/exporter/dynatraceexporter/internal/serialization" - -import ( - "fmt" - - "github.com/dynatrace-oss/dynatrace-metric-utils-go/metric/dimensions" - "go.opentelemetry.io/collector/pdata/pcommon" - "go.opentelemetry.io/collector/pdata/pmetric" - "go.uber.org/zap" - - "github.com/open-telemetry/opentelemetry-collector-contrib/internal/common/ttlmap" -) - -func SerializeMetric(logger *zap.Logger, prefix string, metric pmetric.Metric, defaultDimensions, staticDimensions dimensions.NormalizedDimensionList, prev *ttlmap.TTLMap) ([]string, error) { - var metricLines []string - - ce := logger.Check(zap.DebugLevel, "SerializeMetric") - var points int - //exhaustive:enforce - switch metric.Type() { - case pmetric.MetricTypeGauge: - metricLines = serializeGauge(logger, prefix, metric, defaultDimensions, staticDimensions, metricLines) - case pmetric.MetricTypeSum: - metricLines = serializeSum(logger, prefix, metric, defaultDimensions, staticDimensions, prev, metricLines) - case pmetric.MetricTypeHistogram: - metricLines = serializeHistogram(logger, prefix, metric, defaultDimensions, staticDimensions, metricLines) - case pmetric.MetricTypeEmpty: - fallthrough - case pmetric.MetricTypeExponentialHistogram: - fallthrough - case pmetric.MetricTypeSummary: - fallthrough - default: - return nil, fmt.Errorf("metric type %s unsupported", metric.Type().String()) - } - - if ce != nil { - ce.Write(zap.String("DataType", metric.Type().String()), zap.Int("points", points)) - } - - return metricLines, nil -} - -func makeCombinedDimensions(defaultDimensions dimensions.NormalizedDimensionList, dataPointAttributes pcommon.Map, staticDimensions dimensions.NormalizedDimensionList) dimensions.NormalizedDimensionList { - dimsFromAttributes := make([]dimensions.Dimension, 0, dataPointAttributes.Len()) - - dataPointAttributes.Range(func(k string, v pcommon.Value) bool { - dimsFromAttributes = append(dimsFromAttributes, dimensions.NewDimension(k, v.AsString())) - return true - }) - return dimensions.MergeLists( - defaultDimensions, - dimensions.NewNormalizedDimensionList(dimsFromAttributes...), - staticDimensions, - ) -} diff --git a/exporter/dynatraceexporter/internal/serialization/serialization_test.go b/exporter/dynatraceexporter/internal/serialization/serialization_test.go deleted file mode 100644 index 70f60a000f0b..000000000000 --- a/exporter/dynatraceexporter/internal/serialization/serialization_test.go +++ /dev/null @@ -1,167 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -package serialization - -import ( - "fmt" - "sort" - "strings" - "testing" - - "github.com/dynatrace-oss/dynatrace-metric-utils-go/metric/dimensions" - "github.com/stretchr/testify/assert" - "go.opentelemetry.io/collector/pdata/pcommon" - "go.opentelemetry.io/collector/pdata/pmetric" - "go.uber.org/zap" - "go.uber.org/zap/zaptest/observer" - - "github.com/open-telemetry/opentelemetry-collector-contrib/internal/common/ttlmap" -) - -func TestSerializeMetric(t *testing.T) { - logger := zap.NewNop() - defaultDims := dimensions.NewNormalizedDimensionList(dimensions.NewDimension("default", "value")) - staticDims := dimensions.NewNormalizedDimensionList(dimensions.NewDimension("static", "value")) - - t.Run("correctly creates a gauge", func(t *testing.T) { - metric := pmetric.NewMetric() - metric.SetName("metric_name") - gaugeDp := metric.SetEmptyGauge().DataPoints().AppendEmpty() - gaugeDp.SetIntValue(3) - - prev := ttlmap.New(1, 1) - - serialized, err := SerializeMetric(logger, "prefix", metric, defaultDims, staticDims, prev) - assert.NoError(t, err) - - assert.Len(t, serialized, 1) - - assertMetricLineTokensEqual(t, serialized[0], "prefix.metric_name,default=value,static=value gauge,3") - }) - - t.Run("correctly creates a counter from a sum", func(t *testing.T) { - // more in-depth tests for the different sum serializations are in sum_test.go - metric := pmetric.NewMetric() - metric.SetName("metric_name") - sum := metric.SetEmptySum() - sum.SetIsMonotonic(true) - sum.SetAggregationTemporality(pmetric.AggregationTemporalityDelta) - sumDp := sum.DataPoints().AppendEmpty() - sumDp.SetIntValue(4) - - prev := ttlmap.New(1, 1) - - serialized, err := SerializeMetric(logger, "prefix", metric, defaultDims, staticDims, prev) - assert.NoError(t, err) - - assert.Len(t, serialized, 1) - - assertMetricLineTokensEqual(t, serialized[0], "prefix.metric_name,default=value,static=value count,delta=4") - }) - - t.Run("correctly creates a summary from histogram", func(t *testing.T) { - metric := pmetric.NewMetric() - metric.SetName("metric_name") - hist := metric.SetEmptyHistogram() - hist.SetAggregationTemporality(pmetric.AggregationTemporalityDelta) - dp := hist.DataPoints().AppendEmpty() - dp.SetMin(1) - dp.SetMax(3) - dp.SetSum(6) - dp.SetCount(3) - - prev := ttlmap.New(1, 1) - - serialized, err := SerializeMetric(logger, "prefix", metric, defaultDims, staticDims, prev) - assert.NoError(t, err) - - assert.Len(t, serialized, 1) - - assertMetricLineTokensEqual(t, serialized[0], "prefix.metric_name,default=value,static=value gauge,min=1,max=3,sum=6,count=3") - }) - -} - -func Test_makeCombinedDimensions(t *testing.T) { - defaultDims := dimensions.NewNormalizedDimensionList( - dimensions.NewDimension("a", "default"), - dimensions.NewDimension("b", "default"), - dimensions.NewDimension("c", "default"), - ) - attributes := pcommon.NewMap() - attributes.PutStr("a", "attribute") - attributes.PutStr("b", "attribute") - staticDims := dimensions.NewNormalizedDimensionList( - dimensions.NewDimension("a", "static"), - ) - expected := dimensions.NewNormalizedDimensionList( - dimensions.NewDimension("a", "static"), - dimensions.NewDimension("b", "attribute"), - dimensions.NewDimension("c", "default"), - ) - - actual := makeCombinedDimensions(defaultDims, attributes, staticDims) - - sortAndStringify := - func(dims []dimensions.Dimension) string { - sort.Slice(dims, func(i, j int) bool { - return dims[i].Key < dims[j].Key - }) - tokens := make([]string, len(dims)) - for i, dim := range dims { - tokens[i] = fmt.Sprintf("%s=%s", dim.Key, dim.Value) - } - return strings.Join(tokens, ",") - } - - assert.Equal(t, actual.Format(sortAndStringify), expected.Format(sortAndStringify)) -} - -type simplifiedLogRecord struct { - message string - attributes map[string]string -} - -func makeSimplifiedLogRecordsFromObservedLogs(observedLogs *observer.ObservedLogs) []simplifiedLogRecord { - observedLogRecords := make([]simplifiedLogRecord, observedLogs.Len()) - - for i, observedLog := range observedLogs.All() { - contextStringMap := make(map[string]string, len(observedLog.ContextMap())) - for k, v := range observedLog.ContextMap() { - contextStringMap[k] = fmt.Sprint(v) - } - observedLogRecords[i] = simplifiedLogRecord{ - message: observedLog.Message, - attributes: contextStringMap, - } - } - return observedLogRecords -} - -// tokenizes Dynatrace metric lines -// this ensures that comparing metric lines can be done without -// taking into account the order of dimensions, which does not matter -// the first string contains the name, and the array contains all other tokens -func tokenizeMetricLine(s string) (string, []string) { - tokens := strings.Split(s, " ") - if len(tokens) > 0 { - nameAndDims := strings.Split(tokens[0], ",") - if len(nameAndDims) > 0 { - name := nameAndDims[0] - result := nameAndDims[1:] - result = append(result, tokens[1:]...) - return name, result - } - } - return "", nil -} - -// asserts that the metric name as well as the dimensions, value, and timestamps match, ignoring dimension order -func assertMetricLineTokensEqual(t *testing.T, a string, b string) { - nameA, tokensA := tokenizeMetricLine(a) - nameB, tokensB := tokenizeMetricLine(b) - - assert.Equal(t, nameA, nameB) - assert.ElementsMatch(t, tokensA, tokensB) -} diff --git a/exporter/dynatraceexporter/internal/serialization/sum.go b/exporter/dynatraceexporter/internal/serialization/sum.go deleted file mode 100644 index de92a6b09c80..000000000000 --- a/exporter/dynatraceexporter/internal/serialization/sum.go +++ /dev/null @@ -1,209 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -package serialization // import "github.com/open-telemetry/opentelemetry-collector-contrib/exporter/dynatraceexporter/internal/serialization" - -import ( - "fmt" - "sort" - "strings" - - dtMetric "github.com/dynatrace-oss/dynatrace-metric-utils-go/metric" - "github.com/dynatrace-oss/dynatrace-metric-utils-go/metric/dimensions" - "go.opentelemetry.io/collector/pdata/pcommon" - "go.opentelemetry.io/collector/pdata/pmetric" - "go.uber.org/zap" - - "github.com/open-telemetry/opentelemetry-collector-contrib/internal/common/ttlmap" -) - -func serializeSumPoint(name, prefix string, dims dimensions.NormalizedDimensionList, t pmetric.AggregationTemporality, dp pmetric.NumberDataPoint, prev *ttlmap.TTLMap) (string, error) { - switch t { - case pmetric.AggregationTemporalityCumulative: - return serializeCumulativeCounter(name, prefix, dims, dp, prev) - // for now unspecified is treated as delta - case pmetric.AggregationTemporalityUnspecified: - fallthrough - case pmetric.AggregationTemporalityDelta: - return serializeDeltaCounter(name, prefix, dims, dp) - } - - return "", nil -} - -func serializeSum(logger *zap.Logger, prefix string, metric pmetric.Metric, defaultDimensions dimensions.NormalizedDimensionList, staticDimensions dimensions.NormalizedDimensionList, prev *ttlmap.TTLMap, metricLines []string) []string { - sum := metric.Sum() - - if !sum.IsMonotonic() && sum.AggregationTemporality() == pmetric.AggregationTemporalityDelta { - logger.Warn( - "dropping delta non-monotonic sum", - zap.String("name", metric.Name()), - ) - return metricLines - } - - points := metric.Sum().DataPoints() - - for i := 0; i < points.Len(); i++ { - dp := points.At(i) - if sum.IsMonotonic() { - // serialize monotonic sum points as count (cumulatives are converted to delta in serializeSumPoint) - line, err := serializeSumPoint( - metric.Name(), - prefix, - makeCombinedDimensions(defaultDimensions, dp.Attributes(), staticDimensions), - metric.Sum().AggregationTemporality(), - dp, - prev, - ) - - if err != nil { - logger.Warn( - "Error serializing sum data point", - zap.String("name", metric.Name()), - zap.String("value-type", dp.ValueType().String()), - zap.Error(err), - ) - } - - if line != "" { - metricLines = append(metricLines, line) - } - } else { - // Cumulative non-monotonic sum points are serialized as gauges. Delta non-monotonic sums are dropped above. - line, err := serializeGaugePoint( - metric.Name(), - prefix, - makeCombinedDimensions(defaultDimensions, dp.Attributes(), staticDimensions), - dp, - ) - - if err != nil { - logger.Warn( - "Error serializing non-monotonic Sum as gauge", - zap.String("name", metric.Name()), - zap.String("value-type", dp.ValueType().String()), - zap.Error(err), - ) - } - - if line != "" { - metricLines = append(metricLines, line) - } - } - } - - return metricLines -} - -func serializeDeltaCounter(name, prefix string, dims dimensions.NormalizedDimensionList, dp pmetric.NumberDataPoint) (string, error) { - var valueOpt dtMetric.MetricOption - - switch dp.ValueType() { - case pmetric.NumberDataPointValueTypeEmpty: - return "", fmt.Errorf("unsupported value type none") - case pmetric.NumberDataPointValueTypeInt: - valueOpt = dtMetric.WithIntCounterValueDelta(dp.IntValue()) - case pmetric.NumberDataPointValueTypeDouble: - valueOpt = dtMetric.WithFloatCounterValueDelta(dp.DoubleValue()) - default: - return "", fmt.Errorf("unknown data type") - } - - dm, err := dtMetric.NewMetric( - name, - dtMetric.WithPrefix(prefix), - dtMetric.WithDimensions(dims), - dtMetric.WithTimestamp(dp.Timestamp().AsTime()), - valueOpt, - ) - - if err != nil { - return "", err - } - - return dm.Serialize() -} - -func serializeCumulativeCounter(name, prefix string, dims dimensions.NormalizedDimensionList, dp pmetric.NumberDataPoint, prev *ttlmap.TTLMap) (string, error) { - dm, err := convertTotalCounterToDelta(name, prefix, dims, dp, prev) - - if err != nil { - return "", err - } - - if dm == nil { - return "", nil - } - - return dm.Serialize() -} - -func convertTotalCounterToDelta(name, prefix string, dims dimensions.NormalizedDimensionList, dp pmetric.NumberDataPoint, prevCounters *ttlmap.TTLMap) (*dtMetric.Metric, error) { - attrPairs := make([]string, 0, dp.Attributes().Len()) - dp.Attributes().Range(func(k string, v pcommon.Value) bool { - attrPairs = append(attrPairs, k+"="+v.AsString()) - return true - }) - sort.Strings(attrPairs) - id := name + strings.Join(attrPairs, ",") - - prevCounter := prevCounters.Get(id) - - if prevCounter == nil { - prevCounters.Put(id, dp) - return nil, nil - } - - oldCount := prevCounter.(pmetric.NumberDataPoint) - - if oldCount.Timestamp().AsTime().After(dp.Timestamp().AsTime()) { - // current point is older than the previous point - return nil, nil - } - - var valueOpt dtMetric.MetricOption - - if dp.ValueType() != oldCount.ValueType() { - prevCounters.Put(id, dp) - return nil, fmt.Errorf("expected %s to be type %s but got %s - count reset", name, metricValueTypeToString(oldCount.ValueType()), metricValueTypeToString(dp.ValueType())) - } - - switch { - case dp.ValueType() == pmetric.NumberDataPointValueTypeInt: - valueOpt = dtMetric.WithIntCounterValueDelta(dp.IntValue() - oldCount.IntValue()) - case dp.ValueType() == pmetric.NumberDataPointValueTypeDouble: - valueOpt = dtMetric.WithFloatCounterValueDelta(dp.DoubleValue() - oldCount.DoubleValue()) - default: - return nil, fmt.Errorf("%s value type %s not supported", name, metricValueTypeToString(dp.ValueType())) - } - - dm, err := dtMetric.NewMetric( - name, - dtMetric.WithPrefix(prefix), - dtMetric.WithDimensions(dims), - dtMetric.WithTimestamp(dp.Timestamp().AsTime()), - valueOpt, - ) - - if err != nil { - return dm, err - } - - prevCounters.Put(id, dp) - - return dm, err -} - -func metricValueTypeToString(t pmetric.NumberDataPointValueType) string { - switch t { - case pmetric.NumberDataPointValueTypeDouble: - return "MetricValueTypeDouble" - case pmetric.NumberDataPointValueTypeInt: - return "MericValueTypeInt" - case pmetric.NumberDataPointValueTypeEmpty: - return "MericValueTypeNone" - default: - return "MetricValueTypeUnknown" - } -} diff --git a/exporter/dynatraceexporter/internal/serialization/sum_test.go b/exporter/dynatraceexporter/internal/serialization/sum_test.go deleted file mode 100644 index dc283157f8f8..000000000000 --- a/exporter/dynatraceexporter/internal/serialization/sum_test.go +++ /dev/null @@ -1,394 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -package serialization - -import ( - "math" - "testing" - "time" - - "github.com/dynatrace-oss/dynatrace-metric-utils-go/metric/dimensions" - "github.com/stretchr/testify/assert" - "go.opentelemetry.io/collector/pdata/pcommon" - "go.opentelemetry.io/collector/pdata/pmetric" - "go.uber.org/zap" - "go.uber.org/zap/zaptest/observer" - - "github.com/open-telemetry/opentelemetry-collector-contrib/internal/common/ttlmap" -) - -func Test_serializeSumPoint(t *testing.T) { - t.Run("without timestamp", func(t *testing.T) { - dp := pmetric.NewNumberDataPoint() - dp.SetIntValue(5) - - got, err := serializeSumPoint("int_sum", "prefix", dimensions.NewNormalizedDimensionList(), pmetric.AggregationTemporalityDelta, dp, ttlmap.New(1, 1)) - assert.NoError(t, err) - assert.Equal(t, "prefix.int_sum count,delta=5", got) - }) - - t.Run("float delta with prefix and dimension", func(t *testing.T) { - dp := pmetric.NewNumberDataPoint() - dp.SetDoubleValue(5.5) - dp.SetTimestamp(pcommon.Timestamp(time.Date(2021, 07, 16, 12, 30, 0, 0, time.UTC).UnixNano())) - - prev := ttlmap.New(1, 1) - - got, err := serializeSumPoint("double_sum", "prefix", dimensions.NewNormalizedDimensionList(dimensions.NewDimension("key", "value")), pmetric.AggregationTemporalityDelta, dp, prev) - assert.NoError(t, err) - assert.Equal(t, "prefix.double_sum,key=value count,delta=5.5 1626438600000", got) - }) - - t.Run("int delta with prefix and dimension", func(t *testing.T) { - dp := pmetric.NewNumberDataPoint() - dp.SetIntValue(5) - dp.SetTimestamp(pcommon.Timestamp(time.Date(2021, 07, 16, 12, 30, 0, 0, time.UTC).UnixNano())) - - got, err := serializeSumPoint("int_sum", "prefix", dimensions.NewNormalizedDimensionList(dimensions.NewDimension("key", "value")), pmetric.AggregationTemporalityDelta, dp, ttlmap.New(1, 1)) - assert.NoError(t, err) - assert.Equal(t, "prefix.int_sum,key=value count,delta=5 1626438600000", got) - }) - - t.Run("float cumulative with prefix and dimension", func(t *testing.T) { - dp := pmetric.NewNumberDataPoint() - dp.SetDoubleValue(5.5) - dp.SetTimestamp(pcommon.Timestamp(time.Date(2021, 07, 16, 12, 30, 0, 0, time.UTC).UnixNano())) - - dp2 := pmetric.NewNumberDataPoint() - dp2.SetDoubleValue(7.0) - dp2.SetTimestamp(pcommon.Timestamp(time.Date(2021, 07, 16, 12, 31, 0, 0, time.UTC).UnixNano())) - - prev := ttlmap.New(1, 1) - - got, err := serializeSumPoint("double_sum", "prefix", dimensions.NewNormalizedDimensionList(dimensions.NewDimension("key", "value")), pmetric.AggregationTemporalityCumulative, dp, prev) - assert.NoError(t, err) - assert.Equal(t, "", got) - - got, err = serializeSumPoint("double_sum", "prefix", dimensions.NewNormalizedDimensionList(dimensions.NewDimension("key", "value")), pmetric.AggregationTemporalityCumulative, dp2, prev) - assert.NoError(t, err) - assert.Equal(t, "prefix.double_sum,key=value count,delta=1.5 1626438660000", got) - }) - - t.Run("int cumulative with prefix and dimension", func(t *testing.T) { - dp := pmetric.NewNumberDataPoint() - dp.SetIntValue(5) - dp.SetTimestamp(pcommon.Timestamp(time.Date(2021, 07, 16, 12, 30, 0, 0, time.UTC).UnixNano())) - - dp2 := pmetric.NewNumberDataPoint() - dp2.SetIntValue(10) - dp2.SetTimestamp(pcommon.Timestamp(time.Date(2021, 07, 16, 12, 31, 0, 0, time.UTC).UnixNano())) - - prev := ttlmap.New(1, 1) - - got, err := serializeSumPoint("int_sum", "prefix", dimensions.NewNormalizedDimensionList(dimensions.NewDimension("key", "value")), pmetric.AggregationTemporalityCumulative, dp, prev) - assert.NoError(t, err) - assert.Equal(t, "", got) - - got, err = serializeSumPoint("int_sum", "prefix", dimensions.NewNormalizedDimensionList(dimensions.NewDimension("key", "value")), pmetric.AggregationTemporalityCumulative, dp2, prev) - assert.NoError(t, err) - assert.Equal(t, "prefix.int_sum,key=value count,delta=5 1626438660000", got) - }) - - t.Run("different dimensions should be treated as separate counters", func(t *testing.T) { - dp := pmetric.NewNumberDataPoint() - dp.SetIntValue(5) - dp.Attributes().PutStr("sort", "unstable") - dp.Attributes().PutStr("group", "a") - dp.SetTimestamp(pcommon.Timestamp(time.Date(2021, 07, 16, 12, 30, 0, 0, time.UTC).UnixNano())) - - dp2 := pmetric.NewNumberDataPoint() - dp2.SetIntValue(10) - dp2.Attributes().PutStr("sort", "unstable") - dp2.Attributes().PutStr("group", "b") - dp2.SetTimestamp(pcommon.Timestamp(time.Date(2021, 07, 16, 12, 30, 0, 0, time.UTC).UnixNano())) - - dp3 := pmetric.NewNumberDataPoint() - dp3.SetIntValue(10) - dp3.Attributes().PutStr("group", "a") - dp3.Attributes().PutStr("sort", "unstable") - dp3.SetTimestamp(pcommon.Timestamp(time.Date(2021, 07, 16, 12, 30, 0, 0, time.UTC).UnixNano())) - - dp4 := pmetric.NewNumberDataPoint() - dp4.SetIntValue(20) - dp4.Attributes().PutStr("group", "b") - dp4.Attributes().PutStr("sort", "unstable") - dp4.SetTimestamp(pcommon.Timestamp(time.Date(2021, 07, 16, 12, 30, 0, 0, time.UTC).UnixNano())) - - prev := ttlmap.New(1, 1) - - got, err := serializeSumPoint("int_sum", "prefix", dimensions.NewNormalizedDimensionList(dimensions.NewDimension("key", "a")), pmetric.AggregationTemporalityCumulative, dp, prev) - got2, err2 := serializeSumPoint("int_sum", "prefix", dimensions.NewNormalizedDimensionList(dimensions.NewDimension("key", "b")), pmetric.AggregationTemporalityCumulative, dp2, prev) - got3, err3 := serializeSumPoint("int_sum", "prefix", dimensions.NewNormalizedDimensionList(dimensions.NewDimension("key", "a")), pmetric.AggregationTemporalityCumulative, dp3, prev) - got4, err4 := serializeSumPoint("int_sum", "prefix", dimensions.NewNormalizedDimensionList(dimensions.NewDimension("key", "b")), pmetric.AggregationTemporalityCumulative, dp4, prev) - - assert.NoError(t, err) - assert.NoError(t, err2) - assert.NoError(t, err3) - assert.NoError(t, err4) - assert.Equal(t, "", got) - assert.Equal(t, "", got2) - assert.Equal(t, "prefix.int_sum,key=a count,delta=5 1626438600000", got3) - assert.Equal(t, "prefix.int_sum,key=b count,delta=10 1626438600000", got4) - }) - - t.Run("count values older than the previous count value are dropped", func(t *testing.T) { - dp := pmetric.NewNumberDataPoint() - dp.SetIntValue(5) - dp.SetTimestamp(pcommon.Timestamp(time.Date(2021, 07, 16, 12, 30, 0, 0, time.UTC).UnixNano())) - - dp2 := pmetric.NewNumberDataPoint() - dp2.SetIntValue(5) - dp2.SetTimestamp(pcommon.Timestamp(time.Date(2021, 07, 16, 12, 29, 0, 0, time.UTC).UnixNano())) - - prev := ttlmap.New(1, 1) - - got, err := serializeSumPoint("int_sum", "prefix", dimensions.NewNormalizedDimensionList(dimensions.NewDimension("key", "value")), pmetric.AggregationTemporalityCumulative, dp, prev) - assert.NoError(t, err) - assert.Equal(t, "", got) - - assert.Equal(t, dp, prev.Get("int_sum")) - - got, err = serializeSumPoint("int_sum", "prefix", dimensions.NewNormalizedDimensionList(dimensions.NewDimension("key", "value")), pmetric.AggregationTemporalityCumulative, dp2, prev) - assert.NoError(t, err) - assert.Equal(t, "", got) - - assert.Equal(t, dp, prev.Get("int_sum")) - }) -} - -func Test_serializeSum(t *testing.T) { - empty := dimensions.NewNormalizedDimensionList() - t.Run("non-monotonic delta is dropped", func(t *testing.T) { - metric := pmetric.NewMetric() - metric.SetName("metric_name") - sum := metric.SetEmptySum() - sum.SetAggregationTemporality(pmetric.AggregationTemporalityDelta) - sum.SetIsMonotonic(false) - prev := ttlmap.New(10, 10) - - zapCore, observedLogs := observer.New(zap.WarnLevel) - logger := zap.New(zapCore) - - lines := serializeSum(logger, "", metric, empty, empty, prev, []string{}) - - assert.Empty(t, lines) - - expectedLogs := []simplifiedLogRecord{ - { - message: "dropping delta non-monotonic sum", - attributes: map[string]string{ - "name": "metric_name", - }, - }, - } - assert.ElementsMatch(t, makeSimplifiedLogRecordsFromObservedLogs(observedLogs), expectedLogs) - }) - - t.Run("monotonic delta", func(t *testing.T) { - metric := pmetric.NewMetric() - metric.SetName("metric_name") - sum := metric.SetEmptySum() - sum.SetAggregationTemporality(pmetric.AggregationTemporalityDelta) - sum.SetIsMonotonic(true) - - dp := sum.DataPoints().AppendEmpty() - t.Run("with valid value is exported as delta", func(t *testing.T) { - // not checking Double, this is done in Test_serializeSumPoint - dp.SetIntValue(12) - - prev := ttlmap.New(10, 10) - - zapCore, observedLogs := observer.New(zap.WarnLevel) - logger := zap.New(zapCore) - - actualLines := serializeSum(logger, "", metric, empty, empty, prev, []string{}) - - expectedLines := []string{ - "metric_name count,delta=12", - } - assert.ElementsMatch(t, actualLines, expectedLines) - assert.Empty(t, observedLogs.All()) - }) - - t.Run("with invalid value logs warning and returns no line", func(t *testing.T) { - dp.SetDoubleValue(math.NaN()) - - prev := ttlmap.New(10, 10) - - zapCore, observedLogs := observer.New(zap.WarnLevel) - logger := zap.New(zapCore) - - actualLines := serializeSum(logger, "", metric, empty, empty, prev, []string{}) - - expectedLogRecords := []simplifiedLogRecord{ - { - message: "Error serializing sum data point", - attributes: map[string]string{ - "name": "metric_name", - "value-type": "Double", - "error": "value is NaN.", - }, - }, - } - - assert.Empty(t, actualLines) - assert.ElementsMatch(t, makeSimplifiedLogRecordsFromObservedLogs(observedLogs), expectedLogRecords) - }) - - }) - - t.Run("non-monotonic cumulative", func(t *testing.T) { - metric := pmetric.NewMetric() - metric.SetName("metric_name") - sum := metric.SetEmptySum() - sum.SetAggregationTemporality(pmetric.AggregationTemporalityCumulative) - sum.SetIsMonotonic(false) - dp := sum.DataPoints().AppendEmpty() - - t.Run("with valid value is exported as gauge", func(t *testing.T) { - // not checking Int here, this is done in Test_serializeSumPoint - dp.SetDoubleValue(12.3) - - prev := ttlmap.New(10, 10) - - zapCore, observedLogs := observer.New(zap.WarnLevel) - logger := zap.New(zapCore) - - actualLines := serializeSum(logger, "", metric, empty, empty, prev, []string{}) - - expectedLines := []string{ - "metric_name gauge,12.3", - } - - assert.ElementsMatch(t, actualLines, expectedLines) - // no logs / errors expected. - assert.Empty(t, observedLogs.All()) - }) - - t.Run("with invalid value logs warning and returns no line", func(t *testing.T) { - dp.SetDoubleValue(math.NaN()) - - prev := ttlmap.New(10, 10) - - zapCore, observedLogs := observer.New(zap.WarnLevel) - logger := zap.New(zapCore) - - actualLines := serializeSum(logger, "", metric, empty, empty, prev, []string{}) - - expectedLogRecords := []simplifiedLogRecord{ - { - message: "Error serializing non-monotonic Sum as gauge", - attributes: map[string]string{ - "name": "metric_name", - "value-type": "Double", - "error": "value is NaN.", - }, - }, - } - - assert.Empty(t, actualLines) - assert.ElementsMatch(t, makeSimplifiedLogRecordsFromObservedLogs(observedLogs), expectedLogRecords) - }) - - invalidDp := sum.DataPoints().AppendEmpty() - invalidDp.SetDoubleValue(math.NaN()) - - }) - - t.Run("monotonic cumulative", func(t *testing.T) { - metric := pmetric.NewMetric() - metric.SetName("metric_name") - sum := metric.SetEmptySum() - sum.SetAggregationTemporality(pmetric.AggregationTemporalityCumulative) - sum.SetIsMonotonic(true) - dp1 := sum.DataPoints().AppendEmpty() - dp2 := sum.DataPoints().AppendEmpty() - - t.Run("with two valid data points is converted to delta", func(t *testing.T) { - // not checking Int here, this is done in Test_serializeSumPoint - dp1.SetDoubleValue(5.2) - dp2.SetDoubleValue(5.7) - - prev := ttlmap.New(10, 10) - - zapCore, observedLogs := observer.New(zap.WarnLevel) - logger := zap.New(zapCore) - - actualLines := serializeSum(logger, "", metric, empty, empty, prev, []string{}) - - expectedLines := []string{ - "metric_name count,delta=0.5", - } - - assert.ElementsMatch(t, actualLines, expectedLines) - assert.Empty(t, observedLogs.All()) - - }) - - t.Run("with invalid value logs error and exports no line", func(t *testing.T) { - dp1.SetDoubleValue(5.2) - dp2.SetDoubleValue(math.NaN()) - - prev := ttlmap.New(10, 10) - - zapCore, observedLogs := observer.New(zap.WarnLevel) - logger := zap.New(zapCore) - - actualLines := serializeSum(logger, "", metric, empty, empty, prev, []string{}) - - expectedLogRecords := []simplifiedLogRecord{ - { - message: "Error serializing sum data point", - attributes: map[string]string{ - "name": "metric_name", - "value-type": "Double", - "error": "value is NaN.", - }, - }, - } - - assert.Empty(t, actualLines) - assert.ElementsMatch(t, makeSimplifiedLogRecordsFromObservedLogs(observedLogs), expectedLogRecords) - }) - - t.Run("conversion with incompatible types returns an error", func(t *testing.T) { - // double and int are incompatible - dp1.SetDoubleValue(5.2) - dp2.SetIntValue(5) - - prev := ttlmap.New(10, 10) - - zapCore, observedLogs := observer.New(zap.WarnLevel) - logger := zap.New(zapCore) - - actualLines := serializeSum(logger, "", metric, empty, empty, prev, []string{}) - - expectedLogRecords := []simplifiedLogRecord{ - { - message: "Error serializing sum data point", - attributes: map[string]string{ - "name": "metric_name", - "value-type": "Int", - "error": "expected metric_name to be type MetricValueTypeDouble but got MericValueTypeInt - count reset", - }, - }, - } - - assert.Empty(t, actualLines) - assert.ElementsMatch(t, makeSimplifiedLogRecordsFromObservedLogs(observedLogs), expectedLogRecords) - }) - }) -} - -func Test_convertTotalCounterToDelta_notMutating(t *testing.T) { - dp := pmetric.NewNumberDataPoint() - dp.SetIntValue(5) - dp.Attributes().PutStr("attr2", "val2") - dp.Attributes().PutStr("attr1", "val1") - orig := pmetric.NewNumberDataPoint() - dp.CopyTo(orig) - _, err := convertTotalCounterToDelta("m", "prefix", dimensions.NormalizedDimensionList{}, dp, ttlmap.New(1, 1)) - assert.NoError(t, err) - assert.Equal(t, orig, dp) // make sure the original data point is not mutated -} diff --git a/exporter/dynatraceexporter/metadata.yaml b/exporter/dynatraceexporter/metadata.yaml deleted file mode 100644 index 0c217894827a..000000000000 --- a/exporter/dynatraceexporter/metadata.yaml +++ /dev/null @@ -1,13 +0,0 @@ -type: dynatrace -scope_name: otelcol/dynatrace - -status: - class: exporter - stability: - deprecated: [metrics] - distributions: [contrib] - codeowners: - active: [dyladan, arminru, evan-bradley] - -tests: - expect_consumer_error: true diff --git a/exporter/dynatraceexporter/metrics_exporter.go b/exporter/dynatraceexporter/metrics_exporter.go deleted file mode 100644 index 6b4d1a26befa..000000000000 --- a/exporter/dynatraceexporter/metrics_exporter.go +++ /dev/null @@ -1,340 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -package dynatraceexporter // import "github.com/open-telemetry/opentelemetry-collector-contrib/exporter/dynatraceexporter" - -import ( - "bytes" - "context" - "encoding/json" - "fmt" - "io" - "net/http" - "strings" - "time" - - "github.com/dynatrace-oss/dynatrace-metric-utils-go/metric/apiconstants" - "github.com/dynatrace-oss/dynatrace-metric-utils-go/metric/dimensions" - "go.opentelemetry.io/collector/component" - "go.opentelemetry.io/collector/consumer/consumererror" - "go.opentelemetry.io/collector/exporter" - "go.opentelemetry.io/collector/pdata/pmetric" - "go.uber.org/zap" - - "github.com/open-telemetry/opentelemetry-collector-contrib/exporter/dynatraceexporter/config" - "github.com/open-telemetry/opentelemetry-collector-contrib/exporter/dynatraceexporter/internal/serialization" - "github.com/open-telemetry/opentelemetry-collector-contrib/internal/common/ttlmap" -) - -const ( - cSweepIntervalSeconds = 300 - cMaxAgeSeconds = 900 -) - -// newMetricsExporter exports to a Dynatrace Metrics v2 API -func newMetricsExporter(params exporter.CreateSettings, cfg *config.Config) *metricsExporter { - var confDefaultDims []dimensions.Dimension - for key, value := range cfg.DefaultDimensions { - confDefaultDims = append(confDefaultDims, dimensions.NewDimension(key, value)) - } - - defaultDimensions := dimensions.MergeLists( - dimensionsFromTags(cfg.Tags), - dimensions.NewNormalizedDimensionList(confDefaultDims...), - ) - - staticDimensions := dimensions.NewNormalizedDimensionList(dimensions.NewDimension("dt.metrics.source", "opentelemetry")) - - prevPts := ttlmap.New(cSweepIntervalSeconds, cMaxAgeSeconds) - prevPts.Start() - - return &metricsExporter{ - settings: params.TelemetrySettings, - cfg: cfg, - defaultDimensions: defaultDimensions, - staticDimensions: staticDimensions, - prevPts: prevPts, - } -} - -// metricsExporter forwards metrics to a Dynatrace agent -type metricsExporter struct { - settings component.TelemetrySettings - cfg *config.Config - client *http.Client - isDisabled bool - - defaultDimensions dimensions.NormalizedDimensionList - staticDimensions dimensions.NormalizedDimensionList - - prevPts *ttlmap.TTLMap -} - -// for backwards-compatibility with deprecated `Tags` config option -func dimensionsFromTags(tags []string) dimensions.NormalizedDimensionList { - var dims []dimensions.Dimension - for _, tag := range tags { - parts := strings.SplitN(tag, "=", 2) - if len(parts) == 2 { - dims = append(dims, dimensions.NewDimension(parts[0], parts[1])) - } - } - return dimensions.NewNormalizedDimensionList(dims...) -} - -func (e *metricsExporter) PushMetricsData(ctx context.Context, md pmetric.Metrics) error { - if e.isDisabled { - return nil - } - - lines := e.serializeMetrics(md) - e.settings.Logger.Debug( - "Serialization complete", - zap.Int("data-point-count", md.DataPointCount()), - zap.Int("lines", len(lines)), - ) - - // If request is empty string, there are no serializable metrics in the batch. - // This can happen if all metric names are invalid - if len(lines) == 0 { - return nil - } - - err := e.send(ctx, lines) - - if err != nil { - return err - } - - return nil -} - -func (e *metricsExporter) serializeMetrics(md pmetric.Metrics) []string { - var lines []string - - resourceMetrics := md.ResourceMetrics() - - for i := 0; i < resourceMetrics.Len(); i++ { - resourceMetric := resourceMetrics.At(i) - libraryMetrics := resourceMetric.ScopeMetrics() - for j := 0; j < libraryMetrics.Len(); j++ { - libraryMetric := libraryMetrics.At(j) - metrics := libraryMetric.Metrics() - for k := 0; k < metrics.Len(); k++ { - metric := metrics.At(k) - - metricLines, err := serialization.SerializeMetric(e.settings.Logger, e.cfg.Prefix, metric, e.defaultDimensions, e.staticDimensions, e.prevPts) - - if err != nil { - e.settings.Logger.Warn( - "failed to serialize", - zap.String("name", metric.Name()), - zap.String("data-type", metric.Type().String()), - zap.Error(err), - ) - } - - if len(metricLines) > 0 { - lines = append(lines, metricLines...) - } - e.settings.Logger.Debug( - "Serialized metric data", - zap.String("name", metric.Name()), - zap.String("data-type", metric.Type().String()), - zap.Int("data-len", len(metricLines)), - ) - } - } - } - - return lines -} - -var lastLog int64 - -// send sends a serialized metric batch to Dynatrace. -// An error indicates all lines were dropped regardless of the returned number. -func (e *metricsExporter) send(ctx context.Context, lines []string) error { - e.settings.Logger.Debug("Exporting", zap.Int("lines", len(lines))) - - if now := time.Now().Unix(); len(lines) > apiconstants.GetPayloadLinesLimit() && now-lastLog > 60 { - e.settings.Logger.Warn( - fmt.Sprintf( - "Batch too large. Sending in chunks of %[1]d metrics. If any chunk fails, previous chunks in the batch could be retried by the batch processor. Please set send_batch_max_size to %[1]d or less. Suppressing this log for 60 seconds.", - apiconstants.GetPayloadLinesLimit(), - ), - ) - lastLog = time.Now().Unix() - } - - for i := 0; i < len(lines); i += apiconstants.GetPayloadLinesLimit() { - end := i + apiconstants.GetPayloadLinesLimit() - - if end > len(lines) { - end = len(lines) - } - - err := e.sendBatch(ctx, lines[i:end]) - if err != nil { - return err - } - } - - return nil -} - -// send sends a serialized metric batch to Dynatrace. -// An error indicates all lines were dropped regardless of the returned number. -func (e *metricsExporter) sendBatch(ctx context.Context, lines []string) error { - message := strings.Join(lines, "\n") - e.settings.Logger.Debug( - "sending a batch of metric lines", - zap.Int("lines", len(lines)), - zap.String("endpoint", e.cfg.Endpoint), - ) - - req, err := http.NewRequestWithContext(ctx, "POST", e.cfg.Endpoint, bytes.NewBufferString(message)) - - if err != nil { - return consumererror.NewPermanent(err) - } - - resp, err := e.client.Do(req) - - if err != nil { - e.settings.Logger.Error("failed to send request", zap.Error(err)) - return fmt.Errorf("sendBatch: %w", err) - } - - defer resp.Body.Close() - - responseBody, rbUnmarshalErr := e.unmarshalResponseBody(resp) - - if resp.StatusCode == http.StatusRequestEntityTooLarge { - // If a payload is too large, resending it will not help - return consumererror.NewPermanent(fmt.Errorf("payload too large")) - } - - if resp.StatusCode == http.StatusBadRequest { - // At least some metrics were not accepted - if rbUnmarshalErr != nil { - return nil - } - - e.settings.Logger.Warn( - "Response from Dynatrace", - zap.Int("accepted-lines", responseBody.Ok), - zap.Int("rejected-lines", responseBody.Invalid), - zap.String("error-message", responseBody.Error.Message), - zap.String("status", resp.Status), - ) - - for _, line := range responseBody.Error.InvalidLines { - // Enabled debug logging to see which lines were dropped - if line.Line >= 0 && line.Line < len(lines) { - e.settings.Logger.Debug( - fmt.Sprintf("rejected line %3d: [%s] %s", line.Line, line.Error, lines[line.Line]), - ) - } - } - - return nil - } - - if resp.StatusCode == http.StatusUnauthorized { - // token is missing or wrong format - e.isDisabled = true - return consumererror.NewPermanent(fmt.Errorf("API token missing or invalid")) - } - - if resp.StatusCode == http.StatusForbidden { - return consumererror.NewPermanent(fmt.Errorf("API token missing the required scope (metrics.ingest)")) - } - - if resp.StatusCode == http.StatusNotFound { - return consumererror.NewPermanent(fmt.Errorf("metrics ingest v2 module not found - ensure module is enabled and endpoint is correct")) - } - - if resp.StatusCode == http.StatusTooManyRequests { - return consumererror.NewPermanent( - fmt.Errorf("The server responded that too many requests have been sent. Please check your export interval and batch sizes and see https://www.dynatrace.com/support/help/dynatrace-api/basics/access-limit for more information"), - ) - } - - if resp.StatusCode > http.StatusBadRequest { // '400 Bad Request' itself is handled above - return consumererror.NewPermanent(fmt.Errorf(`Received error response status: "%v"`, resp.Status)) - } - - if rbUnmarshalErr == nil { - e.settings.Logger.Debug( - "Export successful. Response from Dynatrace:", - zap.Int("accepted-lines", responseBody.Ok), - zap.String("status", resp.Status), - ) - } - - // No known errors - return nil -} - -// start starts the exporter -func (e *metricsExporter) start(ctx context.Context, host component.Host) (err error) { - client, err := e.cfg.ClientConfig.ToClientContext(ctx, host, e.settings) - if err != nil { - e.settings.Logger.Error("Failed to construct HTTP client", zap.Error(err)) - return fmt.Errorf("start: %w", err) - } - - e.client = client - - return nil -} - -func (e *metricsExporter) unmarshalResponseBody(resp *http.Response) (metricsResponse, error) { - bodyBytes, err := io.ReadAll(resp.Body) - responseBody := metricsResponse{} - if err != nil { - // if the response cannot be read, do not retry the batch as it may have been successful - e.settings.Logger.Error("Failed to read response from Dynatrace", zap.Error(err)) - return responseBody, fmt.Errorf("Failed to read response") - } - - if err := json.Unmarshal(bodyBytes, &responseBody); err != nil { - // if the response cannot be read, do not retry the batch as it may have been successful - bodyStr := string(bodyBytes) - bodyStr = truncateString(bodyStr, 1000) - e.settings.Logger.Error("Failed to unmarshal response from Dynatrace", zap.Error(err), zap.String("body", bodyStr)) - return responseBody, fmt.Errorf("Failed to unmarshal response") - } - - return responseBody, nil -} - -func truncateString(str string, num int) string { - truncated := str - if len(str) > num { - if num > 3 { - num -= 3 - } - truncated = str[0:num] + "..." - } - return truncated -} - -// Response from Dynatrace is expected to be in JSON format -type metricsResponse struct { - Ok int `json:"linesOk"` - Invalid int `json:"linesInvalid"` - Error metricsResponseError `json:"error"` -} - -type metricsResponseError struct { - Code int `json:"code"` - Message string `json:"message"` - InvalidLines []metricsResponseErrorInvalidLine `json:"invalidLines"` -} - -type metricsResponseErrorInvalidLine struct { - Line int `json:"line"` - Error string `json:"error"` -} diff --git a/exporter/dynatraceexporter/metrics_exporter_test.go b/exporter/dynatraceexporter/metrics_exporter_test.go deleted file mode 100644 index 7aaa66a7dad8..000000000000 --- a/exporter/dynatraceexporter/metrics_exporter_test.go +++ /dev/null @@ -1,766 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -package dynatraceexporter - -import ( - "context" - "encoding/json" - "fmt" - "io" - "net/http" - "net/http/httptest" - "strings" - "testing" - "time" - - "github.com/dynatrace-oss/dynatrace-metric-utils-go/metric/dimensions" - "github.com/stretchr/testify/assert" - "go.opentelemetry.io/collector/component" - "go.opentelemetry.io/collector/component/componenttest" - "go.opentelemetry.io/collector/config/confighttp" - "go.opentelemetry.io/collector/config/configtls" - "go.opentelemetry.io/collector/consumer/consumererror" - "go.opentelemetry.io/collector/exporter/exportertest" - "go.opentelemetry.io/collector/pdata/pcommon" - "go.opentelemetry.io/collector/pdata/pmetric" - "go.uber.org/zap" - - "github.com/open-telemetry/opentelemetry-collector-contrib/exporter/dynatraceexporter/config" - "github.com/open-telemetry/opentelemetry-collector-contrib/internal/common/ttlmap" -) - -var testTimestamp = pcommon.Timestamp(time.Date(2021, 07, 16, 12, 30, 0, 0, time.UTC).UnixNano()) - -func Test_exporter_PushMetricsData(t *testing.T) { - sent := "not sent" - ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - bodyBytes, _ := io.ReadAll(r.Body) - sent = string(bodyBytes) - - response := metricsResponse{ - Ok: 0, - Invalid: 0, - } - body, _ := json.Marshal(response) - _, _ = w.Write(body) - })) - defer ts.Close() - - md := pmetric.NewMetrics() - md.ResourceMetrics().EnsureCapacity(2) - rm := md.ResourceMetrics().AppendEmpty() - - ilms := rm.ScopeMetrics() - ilms.EnsureCapacity(2) - ilm := ilms.AppendEmpty() - - metrics := ilm.Metrics() - - badNameMetric := metrics.AppendEmpty() - badNameMetric.SetName("") - - noneMetric := metrics.AppendEmpty() - noneMetric.SetName("none") - - intGaugeMetric := metrics.AppendEmpty() - intGaugeMetric.SetName("int_gauge") - intGauge := intGaugeMetric.SetEmptyGauge() - intGaugeDataPoints := intGauge.DataPoints() - intGaugeDataPoint := intGaugeDataPoints.AppendEmpty() - intGaugeDataPoint.SetIntValue(10) - intGaugeDataPoint.SetTimestamp(testTimestamp) - - nmIntSumMetric := metrics.AppendEmpty() - nmIntSumMetric.SetName("nonmonotonic_int_sum") - nmIntSum := nmIntSumMetric.SetEmptySum() - nmIntSumDataPoints := nmIntSum.DataPoints() - nmIntSumDataPoint := nmIntSumDataPoints.AppendEmpty() - nmIntSumDataPoint.SetIntValue(10) - nmIntSumDataPoint.SetTimestamp(testTimestamp) - - mIntSumMetric := metrics.AppendEmpty() - mIntSumMetric.SetName("monotonic_int_sum") - mIntSum := mIntSumMetric.SetEmptySum() - mIntSum.SetIsMonotonic(true) - mIntSumDataPoints := mIntSum.DataPoints() - mIntSumDataPoint := mIntSumDataPoints.AppendEmpty() - mIntSumDataPoint.SetIntValue(10) - mIntSumDataPoint.SetTimestamp(testTimestamp) - - doubleGaugeMetric := metrics.AppendEmpty() - doubleGaugeMetric.SetName("double_gauge") - doubleGauge := doubleGaugeMetric.SetEmptyGauge() - doubleGaugeDataPoints := doubleGauge.DataPoints() - doubleGaugeDataPoint := doubleGaugeDataPoints.AppendEmpty() - doubleGaugeDataPoint.SetDoubleValue(10.1) - doubleGaugeDataPoint.SetTimestamp(testTimestamp) - - nmDoubleSumMetric := metrics.AppendEmpty() - nmDoubleSumMetric.SetName("nonmonotonic_double_sum") - nmDoubleSum := nmDoubleSumMetric.SetEmptySum() - nmDoubleSumDataPoints := nmDoubleSum.DataPoints() - nmDoubleSumDataPoint := nmDoubleSumDataPoints.AppendEmpty() - nmDoubleSumDataPoint.SetDoubleValue(10.1) - nmDoubleSumDataPoint.SetTimestamp(testTimestamp) - - mDoubleSumMetric := metrics.AppendEmpty() - mDoubleSumMetric.SetName("monotonic_double_sum") - mDoubleSum := mDoubleSumMetric.SetEmptySum() - mDoubleSum.SetIsMonotonic(true) - mDoubleSumDataPoints := mDoubleSum.DataPoints() - mDoubleSumDataPoint := mDoubleSumDataPoints.AppendEmpty() - mDoubleSumDataPoint.SetDoubleValue(10.1) - mDoubleSumDataPoint.SetTimestamp(testTimestamp) - - doubleHistogramMetric := metrics.AppendEmpty() - doubleHistogramMetric.SetName("double_histogram") - doubleHistogram := doubleHistogramMetric.SetEmptyHistogram() - doubleHistogramDataPoints := doubleHistogram.DataPoints() - doubleHistogramDataPoint := doubleHistogramDataPoints.AppendEmpty() - doubleHistogramDataPoint.SetCount(2) - doubleHistogramDataPoint.SetSum(10.1) - doubleHistogramDataPoint.ExplicitBounds().FromRaw([]float64{0, 2, 4, 8}) - doubleHistogramDataPoint.BucketCounts().FromRaw([]uint64{0, 1, 0, 1, 0}) - doubleHistogramDataPoint.SetTimestamp(testTimestamp) - doubleHistogram.SetAggregationTemporality(pmetric.AggregationTemporalityDelta) - - type fields struct { - settings component.TelemetrySettings - cfg *config.Config - client *http.Client - } - type args struct { - ctx context.Context - md pmetric.Metrics - } - test := struct { - name string - fields fields - args args - wantErr bool - }{ - name: "Send metric data", - fields: fields{ - settings: componenttest.NewNopTelemetrySettings(), - cfg: &config.Config{ - APIToken: "token", - ClientConfig: confighttp.ClientConfig{Endpoint: ts.URL}, - Prefix: "prefix", - DefaultDimensions: map[string]string{}, - }, - client: ts.Client(), - }, - args: args{ - ctx: context.Background(), - md: md, - }, - wantErr: false, - } - - t.Run(test.name, func(t *testing.T) { - e := &metricsExporter{ - settings: test.fields.settings, - cfg: test.fields.cfg, - client: test.fields.client, - } - err := e.PushMetricsData(test.args.ctx, test.args.md) - if (err != nil) != test.wantErr { - t.Errorf("exporter.PushMetricsData() error = %v, wantErr %v", err, test.wantErr) - return - } - }) - - wantLines := []string{ - "prefix.int_gauge gauge,10 1626438600000", - "prefix.monotonic_int_sum count,delta=10 1626438600000", - "prefix.nonmonotonic_int_sum gauge,10 1626438600000", - "prefix.double_gauge gauge,10.1 1626438600000", - "prefix.monotonic_double_sum count,delta=10.1 1626438600000", - "prefix.nonmonotonic_double_sum gauge,10.1 1626438600000", - "prefix.double_histogram gauge,min=0,max=8,sum=10.1,count=2 1626438600000", - } - - // only succeeds if the two lists contain the same elements, ignoring their order - assert.ElementsMatch(t, wantLines, strings.Split(sent, "\n")) -} - -func Test_SumMetrics(t *testing.T) { - type args struct { - monotonic bool - temporality pmetric.AggregationTemporality - valueType string // either 'double' or 'int' - } - tests := []struct { - name string - args args - want []string - }{ - { - name: "Monotonic Delta sum (int)", - args: args{ - true, - pmetric.AggregationTemporalityDelta, - "int", - }, - want: []string{ - "prefix.metric_name count,delta=10 1626438600000", - "prefix.metric_name count,delta=20 1626438600000", - }, - }, - { - name: "Non-monotonic Delta sum (int)", - args: args{ - false, - pmetric.AggregationTemporalityDelta, - "int", - }, - want: []string{"nothing sent"}, - }, - { - name: "Monotonic Cumulative sum (int)", - args: args{ - true, - pmetric.AggregationTemporalityCumulative, - "int", - }, - want: []string{"prefix.metric_name count,delta=10 1626438600000"}, - }, - { - name: "Non-monotonic Cumulative sum (int)", - args: args{ - false, - pmetric.AggregationTemporalityCumulative, - "int", - }, - want: []string{ - "prefix.metric_name gauge,10 1626438600000", - "prefix.metric_name gauge,20 1626438600000", - }, - }, - { - name: "Monotonic Delta sum (double)", - args: args{ - true, - pmetric.AggregationTemporalityDelta, - "double", - }, - want: []string{ - "prefix.metric_name count,delta=10.1 1626438600000", - "prefix.metric_name count,delta=20.2 1626438600000", - }, - }, - { - name: "Non-monotonic Delta sum (double)", - args: args{ - false, - pmetric.AggregationTemporalityDelta, - "double", - }, - want: []string{"nothing sent"}, - }, - { - name: "Monotonic Cumulative sum (double)", - args: args{ - true, - pmetric.AggregationTemporalityCumulative, - "double", - }, - want: []string{"prefix.metric_name count,delta=10.1 1626438600000"}, - }, - { - name: "Non-monotonic Cumulative sum (double)", - args: args{ - false, - pmetric.AggregationTemporalityCumulative, - "double", - }, - want: []string{ - "prefix.metric_name gauge,10.1 1626438600000", - "prefix.metric_name gauge,20.2 1626438600000", - }, - }, - } - - // server setup: - sent := "nothing sent" - ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - bodyBytes, _ := io.ReadAll(r.Body) - sent = string(bodyBytes) - - response := metricsResponse{ - Ok: 0, - Invalid: 0, - } - body, _ := json.Marshal(response) - _, _ = w.Write(body) - })) - defer ts.Close() - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - // reset the export buffer for the HTTP client - sent = "nothing sent" - - prevPts := ttlmap.New(cSweepIntervalSeconds, cMaxAgeSeconds) - - // set up the exporter - exp := &metricsExporter{ - settings: componenttest.NewNopTelemetrySettings(), - cfg: &config.Config{ - APIToken: "token", - ClientConfig: confighttp.ClientConfig{Endpoint: ts.URL}, - Prefix: "prefix", - }, - client: ts.Client(), - prevPts: prevPts, - } - - metrics := pmetric.NewMetrics() - resourceMetric := metrics.ResourceMetrics().AppendEmpty() - scopeMetric := resourceMetric.ScopeMetrics().AppendEmpty() - metric := scopeMetric.Metrics().AppendEmpty() - metric.SetName("metric_name") - sum := metric.SetEmptySum() - sum.SetAggregationTemporality(tt.args.temporality) - sum.SetIsMonotonic(tt.args.monotonic) - - dataPoint1 := sum.DataPoints().AppendEmpty() - dataPoint1.SetTimestamp(testTimestamp) - - dataPoint2 := sum.DataPoints().AppendEmpty() - dataPoint2.SetTimestamp(testTimestamp) - - switch tt.args.valueType { - case "int": - dataPoint1.SetIntValue(10) - dataPoint2.SetIntValue(20) - case "double": - dataPoint1.SetDoubleValue(10.1) - dataPoint2.SetDoubleValue(20.2) - default: - t.Fatalf("valueType can only be 'int' or 'double' but was '%s'", tt.args.valueType) - } - - err := exp.PushMetricsData(context.Background(), metrics) - assert.NoError(t, err) - - assert.ElementsMatch(t, tt.want, strings.Split(sent, "\n")) - }) - } -} - -func Test_exporter_PushMetricsData_EmptyPayload(t *testing.T) { - ts := httptest.NewServer(http.HandlerFunc(func(_ http.ResponseWriter, _ *http.Request) { - t.Fatal("Server should not be called") - })) - defer ts.Close() - - md := pmetric.NewMetrics() - md.ResourceMetrics().EnsureCapacity(2) - rm := md.ResourceMetrics().AppendEmpty() - - ilms := rm.ScopeMetrics() - ilms.EnsureCapacity(2) - ilm := ilms.AppendEmpty() - - metrics := ilm.Metrics() - noneMetric := metrics.AppendEmpty() - noneMetric.SetName("none") - - e := &metricsExporter{ - settings: componenttest.NewNopTelemetrySettings(), - cfg: &config.Config{ - ClientConfig: confighttp.ClientConfig{Endpoint: ts.URL}, - }, - client: ts.Client(), - } - err := e.PushMetricsData(context.Background(), md) - if err != nil { - t.Errorf("exporter.PushMetricsData() error = %v", err) - return - } -} - -func Test_exporter_PushMetricsData_isDisabled(t *testing.T) { - ts := httptest.NewServer(http.HandlerFunc(func(_ http.ResponseWriter, _ *http.Request) { - t.Fatal("Server should not be called") - })) - defer ts.Close() - - md := pmetric.NewMetrics() - md.ResourceMetrics().EnsureCapacity(2) - rm := md.ResourceMetrics().AppendEmpty() - - ilms := rm.ScopeMetrics() - ilms.EnsureCapacity(2) - ilm := ilms.AppendEmpty() - - metrics := ilm.Metrics() - metric := metrics.AppendEmpty() - metric.SetName("int_gauge") - intGauge := metric.SetEmptyGauge() - intGaugeDataPoints := intGauge.DataPoints() - intGaugeDataPoint := intGaugeDataPoints.AppendEmpty() - intGaugeDataPoint.SetIntValue(10) - intGaugeDataPoint.SetTimestamp(testTimestamp) - - e := &metricsExporter{ - settings: componenttest.NewNopTelemetrySettings(), - cfg: &config.Config{ - ClientConfig: confighttp.ClientConfig{Endpoint: ts.URL}, - }, - client: ts.Client(), - isDisabled: true, - } - err := e.PushMetricsData(context.Background(), md) - if err != nil { - t.Errorf("exporter.PushMetricsData() error = %v", err) - return - } -} - -func Test_exporter_send_BadRequest(t *testing.T) { - ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { - w.WriteHeader(http.StatusBadRequest) - body, _ := json.Marshal(metricsResponse{ - Ok: 0, - Invalid: 10, - }) - _, _ = w.Write(body) - })) - defer ts.Close() - - e := &metricsExporter{ - settings: componenttest.NewNopTelemetrySettings(), - cfg: &config.Config{ - ClientConfig: confighttp.ClientConfig{Endpoint: ts.URL}, - }, - client: ts.Client(), - } - err := e.send(context.Background(), []string{""}) - if consumererror.IsPermanent(err) { - t.Errorf("Expected error to not be permanent %v", err) - return - } - if e.isDisabled { - t.Error("Expected exporter to not be disabled") - return - } -} - -func Test_exporter_send_Unauthorized(t *testing.T) { - ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { - w.WriteHeader(http.StatusUnauthorized) - _, _ = w.Write([]byte{}) - })) - defer ts.Close() - - e := &metricsExporter{ - settings: componenttest.NewNopTelemetrySettings(), - cfg: &config.Config{ - ClientConfig: confighttp.ClientConfig{Endpoint: ts.URL}, - }, - client: ts.Client(), - } - err := e.send(context.Background(), []string{""}) - if !consumererror.IsPermanent(err) { - t.Errorf("Expected error to be permanent %v", err) - return - } - if !e.isDisabled { - t.Error("Expected exporter to be disabled") - return - } -} - -func Test_exporter_send_TooLarge(t *testing.T) { - ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { - w.WriteHeader(http.StatusRequestEntityTooLarge) - _, _ = w.Write([]byte{}) - })) - defer ts.Close() - - e := &metricsExporter{ - settings: componenttest.NewNopTelemetrySettings(), - cfg: &config.Config{ - ClientConfig: confighttp.ClientConfig{Endpoint: ts.URL}, - }, - client: ts.Client(), - } - err := e.send(context.Background(), []string{""}) - if !consumererror.IsPermanent(err) { - t.Errorf("Expected error to be permanent %v", err) - return - } - if e.isDisabled { - t.Error("Expected exporter not to be disabled") - return - } -} - -func Test_exporter_send_NotFound(t *testing.T) { - ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { - w.WriteHeader(404) - _, _ = w.Write([]byte{}) - })) - defer ts.Close() - - e := &metricsExporter{ - settings: componenttest.NewNopTelemetrySettings(), - cfg: &config.Config{ - APIToken: "token", - ClientConfig: confighttp.ClientConfig{Endpoint: ts.URL}, - Prefix: "prefix", - DefaultDimensions: map[string]string{}, - }, - client: ts.Client(), - } - err := e.send(context.Background(), []string{""}) - if !consumererror.IsPermanent(err) { - t.Errorf("Expected error to be permanent %v", err) - return - } - if e.isDisabled { - t.Error("Expected exporter to not be disabled") - return - } -} - -func Test_exporter_send_TooManyRequests(t *testing.T) { - ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { - w.WriteHeader(http.StatusTooManyRequests) - _, _ = w.Write([]byte{}) - })) - defer ts.Close() - - e := &metricsExporter{ - settings: componenttest.NewNopTelemetrySettings(), - cfg: &config.Config{ - APIToken: "token", - ClientConfig: confighttp.ClientConfig{Endpoint: ts.URL}, - Prefix: "prefix", - DefaultDimensions: map[string]string{}, - }, - client: ts.Client(), - } - err := e.send(context.Background(), []string{""}) - - assert.True(t, consumererror.IsPermanent(err), "Expected error to be permanent %v", err) - assert.False(t, e.isDisabled, "Expected exporter to not be disabled") -} - -func Test_exporter_send_MiscellaneousErrorCode(t *testing.T) { - ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { - w.WriteHeader(http.StatusExpectationFailed) - _, _ = w.Write([]byte{}) - })) - defer ts.Close() - - e := &metricsExporter{ - settings: componenttest.NewNopTelemetrySettings(), - cfg: &config.Config{ - APIToken: "token", - ClientConfig: confighttp.ClientConfig{Endpoint: ts.URL}, - Prefix: "prefix", - DefaultDimensions: map[string]string{}, - }, - client: ts.Client(), - } - err := e.send(context.Background(), []string{""}) - - assert.ErrorContains(t, err, "417 Expectation Failed") - assert.True(t, consumererror.IsPermanent(err), "Expected error to be permanent %v", err) - assert.False(t, e.isDisabled, "Expected exporter to not be disabled") -} - -func Test_exporter_send_chunking(t *testing.T) { - sentChunks := 0 - - ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { - w.WriteHeader(http.StatusBadRequest) - body, _ := json.Marshal(metricsResponse{ - Ok: 0, - Invalid: 1, - }) - _, _ = w.Write(body) - sentChunks++ - })) - defer ts.Close() - - e := &metricsExporter{ - settings: componenttest.NewNopTelemetrySettings(), - cfg: &config.Config{ - ClientConfig: confighttp.ClientConfig{Endpoint: ts.URL}, - }, - client: ts.Client(), - } - - batch := make([]string, 1001) - - for i := 0; i < 1001; i++ { - batch[i] = fmt.Sprintf("%d", i) - } - - err := e.send(context.Background(), batch) - if sentChunks != 2 { - t.Errorf("Expected batch to be sent in 2 chunks") - } - if consumererror.IsPermanent(err) { - t.Errorf("Expected error to not be permanent %v", err) - return - } - if e.isDisabled { - t.Error("Expected exporter to not be disabled") - return - } -} - -func Test_exporter_PushMetricsData_Error(t *testing.T) { - ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { - w.WriteHeader(200) - })) - ts.Close() - - md := pmetric.NewMetrics() - md.ResourceMetrics().EnsureCapacity(2) - rm := md.ResourceMetrics().AppendEmpty() - - ilms := rm.ScopeMetrics() - ilms.EnsureCapacity(2) - ilm := ilms.AppendEmpty() - - metrics := ilm.Metrics() - intGaugeMetric := metrics.AppendEmpty() - intGaugeMetric.SetName("int_gauge") - intGauge := intGaugeMetric.SetEmptyGauge() - intGaugeDataPoints := intGauge.DataPoints() - intGaugeDataPoint := intGaugeDataPoints.AppendEmpty() - intGaugeDataPoint.SetIntValue(10) - intGaugeDataPoint.SetTimestamp(testTimestamp) - type fields struct { - logger *zap.Logger - cfg *config.Config - client *http.Client - } - type args struct { - ctx context.Context - md pmetric.Metrics - } - test := struct { - name string - fields fields - args args - wantErr bool - }{ - name: "When the client errors, all timeseries are assumed to be dropped", - fields: fields{ - logger: zap.NewNop(), - cfg: &config.Config{ - APIToken: "token", - ClientConfig: confighttp.ClientConfig{Endpoint: ts.URL}, - Prefix: "prefix", - DefaultDimensions: map[string]string{}, - }, - client: ts.Client(), - }, - args: args{ - ctx: context.Background(), - md: md, - }, - wantErr: true, - } - - t.Run(test.name, func(t *testing.T) { - e := &metricsExporter{ - settings: componenttest.NewNopTelemetrySettings(), - cfg: test.fields.cfg, - client: test.fields.client, - } - err := e.PushMetricsData(test.args.ctx, test.args.md) - if (err != nil) != test.wantErr { - t.Errorf("exporter.PushMetricsData() error = %v, wantErr %v", err, test.wantErr) - return - } - }) -} - -func Test_exporter_start_InvalidClientConfig(t *testing.T) { - cfg := &config.Config{ - ClientConfig: confighttp.ClientConfig{ - Endpoint: "localhost:9090", - TLSSetting: configtls.ClientConfig{ - Config: configtls.Config{ - CAFile: "/non/existent", - }, - }, - }, - } - - exp := newMetricsExporter(exportertest.NewNopCreateSettings(), cfg) - - err := exp.start(context.Background(), componenttest.NewNopHost()) - if err == nil { - t.Errorf("Expected error when creating a metrics exporter with invalid HTTP Client Settings") - return - } -} - -func Test_exporter_new_with_tags(t *testing.T) { - cfg := &config.Config{ - DefaultDimensions: map[string]string{"test_tag": "value"}, - } - - exp := newMetricsExporter(exportertest.NewNopCreateSettings(), cfg) - - assert.Equal(t, dimensions.NewNormalizedDimensionList(dimensions.NewDimension("test_tag", "value")), exp.defaultDimensions) -} - -func Test_exporter_new_with_default_dimensions(t *testing.T) { - cfg := &config.Config{ - DefaultDimensions: map[string]string{"test_dimension": "value"}, - } - - exp := newMetricsExporter(exportertest.NewNopCreateSettings(), cfg) - - assert.Equal(t, dimensions.NewNormalizedDimensionList(dimensions.NewDimension("test_dimension", "value")), exp.defaultDimensions) -} - -func Test_exporter_new_with_default_dimensions_override_tag(t *testing.T) { - cfg := &config.Config{ - Tags: []string{"from=tag"}, - DefaultDimensions: map[string]string{"from": "default_dimensions"}, - } - - exp := newMetricsExporter(exportertest.NewNopCreateSettings(), cfg) - - assert.Equal(t, dimensions.NewNormalizedDimensionList(dimensions.NewDimension("from", "default_dimensions")), exp.defaultDimensions) -} - -func Test_LineTooLong(t *testing.T) { - numDims := 50_000 / 9 - dims := make(map[string]string, numDims) - for i := 0; i < numDims; i++ { - dims[fmt.Sprintf("dim%d", i)] = fmt.Sprintf("val%d", i) - } - - md := pmetric.NewMetrics() - md.ResourceMetrics().EnsureCapacity(1) - rm := md.ResourceMetrics().AppendEmpty() - - scms := rm.ScopeMetrics() - scms.EnsureCapacity(1) - scm := scms.AppendEmpty() - - metrics := scm.Metrics() - intGaugeMetric := metrics.AppendEmpty() - intGaugeMetric.SetName("int_gauge") - intGauge := intGaugeMetric.SetEmptyGauge() - intGaugeDataPoints := intGauge.DataPoints() - intGaugeDataPoint := intGaugeDataPoints.AppendEmpty() - intGaugeDataPoint.SetIntValue(10) - intGaugeDataPoint.SetTimestamp(testTimestamp) - exp := newMetricsExporter(exportertest.NewNopCreateSettings(), &config.Config{DefaultDimensions: dims}) - - assert.Empty(t, exp.serializeMetrics(md)) -} diff --git a/exporter/dynatraceexporter/testdata/config.yml b/exporter/dynatraceexporter/testdata/config.yml deleted file mode 100644 index 1e67e53fecf0..000000000000 --- a/exporter/dynatraceexporter/testdata/config.yml +++ /dev/null @@ -1,22 +0,0 @@ -dynatrace/defaults: -dynatrace/bad_endpoint: - endpoint: not a url - api_token: token -dynatrace/missing_token: - endpoint: https://example.com -dynatrace/valid_tags: - tags: - - tag_example=tag_value - - prefix: myprefix - - endpoint: http://example.com/api/v2/metrics/ingest - api_token: token -dynatrace/valid: - default_dimensions: - dimension_example: dimension_value - - prefix: myprefix - - endpoint: http://example.com/api/v2/metrics/ingest - api_token: token diff --git a/go.mod b/go.mod index 40005aacc9b0..1e436ff63659 100644 --- a/go.mod +++ b/go.mod @@ -23,7 +23,6 @@ require ( github.com/open-telemetry/opentelemetry-collector-contrib/exporter/coralogixexporter v0.98.0 github.com/open-telemetry/opentelemetry-collector-contrib/exporter/datadogexporter v0.98.0 github.com/open-telemetry/opentelemetry-collector-contrib/exporter/datasetexporter v0.98.0 - github.com/open-telemetry/opentelemetry-collector-contrib/exporter/dynatraceexporter v0.98.0 github.com/open-telemetry/opentelemetry-collector-contrib/exporter/elasticsearchexporter v0.98.0 github.com/open-telemetry/opentelemetry-collector-contrib/exporter/fileexporter v0.98.0 github.com/open-telemetry/opentelemetry-collector-contrib/exporter/googlecloudexporter v0.98.0 @@ -332,7 +331,6 @@ require ( github.com/docker/go-units v0.5.0 // indirect github.com/dustin/go-humanize v1.0.1 // indirect github.com/dvsekhvalnov/jose2go v1.6.0 // indirect - github.com/dynatrace-oss/dynatrace-metric-utils-go v0.5.0 // indirect github.com/eapache/go-resiliency v1.6.0 // indirect github.com/eapache/go-xerial-snappy v0.0.0-20230731223053-c322873962e3 // indirect github.com/eapache/queue v1.1.0 // indirect @@ -775,8 +773,6 @@ replace github.com/open-telemetry/opentelemetry-collector-contrib/exporter/datad replace github.com/open-telemetry/opentelemetry-collector-contrib/exporter/datasetexporter => ./exporter/datasetexporter -replace github.com/open-telemetry/opentelemetry-collector-contrib/exporter/dynatraceexporter => ./exporter/dynatraceexporter - replace github.com/open-telemetry/opentelemetry-collector-contrib/exporter/fileexporter => ./exporter/fileexporter replace github.com/open-telemetry/opentelemetry-collector-contrib/exporter/googlecloudexporter => ./exporter/googlecloudexporter diff --git a/go.sum b/go.sum index b82806270afb..f35c1d17f9e5 100644 --- a/go.sum +++ b/go.sum @@ -542,8 +542,6 @@ github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+m github.com/dvsekhvalnov/jose2go v0.0.0-20200901110807-248326c1351b/go.mod h1:7BvyPhdbLxMXIYTFPLsyJRFMsKmOZnQmzh6Gb+uquuM= github.com/dvsekhvalnov/jose2go v1.6.0 h1:Y9gnSnP4qEI0+/uQkHvFXeD2PLPJeXEL+ySMEA2EjTY= github.com/dvsekhvalnov/jose2go v1.6.0/go.mod h1:QsHjhyTlD/lAVqn/NSbVZmSCGeDehTB/mPZadG+mhXU= -github.com/dynatrace-oss/dynatrace-metric-utils-go v0.5.0 h1:wHGPJSXvwKQVf/XfhjUPyrhpcPKWNy8F3ikH+eiwoBg= -github.com/dynatrace-oss/dynatrace-metric-utils-go v0.5.0/go.mod h1:PseHFo8Leko7J4A/TfZ6kkHdkzKBLUta6hRZR/OEbbc= github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= github.com/eapache/go-resiliency v1.6.0 h1:CqGDTLtpwuWKn6Nj3uNUdflaq+/kIPsg0gfNzHton30= github.com/eapache/go-resiliency v1.6.0/go.mod h1:5yPzW0MIvSe0JDsv0v+DvcjEv2FyD6iZYSs1ZI+iQho= diff --git a/internal/components/components.go b/internal/components/components.go index 979bb07bc50d..46966ee5bf78 100644 --- a/internal/components/components.go +++ b/internal/components/components.go @@ -41,7 +41,6 @@ import ( "github.com/open-telemetry/opentelemetry-collector-contrib/exporter/coralogixexporter" "github.com/open-telemetry/opentelemetry-collector-contrib/exporter/datadogexporter" "github.com/open-telemetry/opentelemetry-collector-contrib/exporter/datasetexporter" - "github.com/open-telemetry/opentelemetry-collector-contrib/exporter/dynatraceexporter" "github.com/open-telemetry/opentelemetry-collector-contrib/exporter/elasticsearchexporter" "github.com/open-telemetry/opentelemetry-collector-contrib/exporter/fileexporter" "github.com/open-telemetry/opentelemetry-collector-contrib/exporter/googlecloudexporter" @@ -323,7 +322,6 @@ func Components() (otelcol.Factories, error) { datadogexporter.NewFactory(), datasetexporter.NewFactory(), debugexporter.NewFactory(), - dynatraceexporter.NewFactory(), elasticsearchexporter.NewFactory(), fileexporter.NewFactory(), googlecloudexporter.NewFactory(), diff --git a/reports/distributions/contrib.yaml b/reports/distributions/contrib.yaml index 81610af4a5a6..f7cce5cd43bd 100644 --- a/reports/distributions/contrib.yaml +++ b/reports/distributions/contrib.yaml @@ -25,7 +25,6 @@ components: - coralogix - datadog - dataset - - dynatrace - elasticsearch - file - googlecloud diff --git a/versions.yaml b/versions.yaml index 5053d2c32f96..7c8144172cb1 100644 --- a/versions.yaml +++ b/versions.yaml @@ -38,7 +38,6 @@ module-sets: - github.com/open-telemetry/opentelemetry-collector-contrib/exporter/datadogexporter - github.com/open-telemetry/opentelemetry-collector-contrib/exporter/datadogexporter/integrationtest - github.com/open-telemetry/opentelemetry-collector-contrib/exporter/datasetexporter - - github.com/open-telemetry/opentelemetry-collector-contrib/exporter/dynatraceexporter - github.com/open-telemetry/opentelemetry-collector-contrib/exporter/elasticsearchexporter - github.com/open-telemetry/opentelemetry-collector-contrib/exporter/fileexporter - github.com/open-telemetry/opentelemetry-collector-contrib/exporter/googlecloudexporter