diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 5e8ccb4e82..dbd02d4c94 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -171,7 +171,7 @@ jobs: - 23 steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - - uses: graalvm/setup-graalvm@01ed653ac833fe80569f1ef9f25585ba2811baab # v1.3.3 + - uses: graalvm/setup-graalvm@e1df20a713a4cc6ab5b0eb03f0e0dcdc0199b805 # v1.3.4 with: java-version: ${{ matrix.test-graal-version }} distribution: 'graalvm' diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 740ae160f7..0408d94d1d 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -45,7 +45,7 @@ jobs: uses: gradle/actions/setup-gradle@ac638b010cf58a27ee6c972d7336334ccaf61c96 # v4.4.1 - name: Initialize CodeQL - uses: github/codeql-action/init@ce28f5bb42b7a9f2c824e633a3f6ee835bab6858 # v3.29.0 + uses: github/codeql-action/init@181d5eefc20863364f96762470ba6f862bdef56b # v3.29.2 with: languages: ${{ matrix.language }} # using "latest" helps to keep up with the latest Kotlin support @@ -60,6 +60,6 @@ jobs: run: ./gradlew assemble --no-build-cache --no-daemon - name: Perform CodeQL analysis - uses: github/codeql-action/analyze@ce28f5bb42b7a9f2c824e633a3f6ee835bab6858 # v3.29.0 + uses: github/codeql-action/analyze@181d5eefc20863364f96762470ba6f862bdef56b # v3.29.2 with: category: "/language:${{matrix.language}}" \ No newline at end of file diff --git a/.github/workflows/ossf-scorecard.yml b/.github/workflows/ossf-scorecard.yml index cff5b894fc..cfdb7ca62b 100644 --- a/.github/workflows/ossf-scorecard.yml +++ b/.github/workflows/ossf-scorecard.yml @@ -42,6 +42,6 @@ jobs: # Upload the results to GitHub's code scanning dashboard (optional). # Commenting out will disable upload of results to your repo's Code Scanning dashboard - name: "Upload to code-scanning" - uses: github/codeql-action/upload-sarif@ce28f5bb42b7a9f2c824e633a3f6ee835bab6858 # v3.29.0 + uses: github/codeql-action/upload-sarif@181d5eefc20863364f96762470ba6f862bdef56b # v3.29.2 with: sarif_file: results.sarif diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index abc1423885..de9d7c1533 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -133,7 +133,7 @@ jobs: echo "version=$VERSION" >> $GITHUB_OUTPUT echo "prior-version=$PRIOR_VERSION" >> $GITHUB_OUTPUT - update-apidiff-baseline-to-released-version: + update-apidiff-baseline-and-docs-to-released-version: permissions: contents: write # for git push to PR branch runs-on: ubuntu-latest @@ -187,6 +187,13 @@ jobs: ./gradlew --refresh-dependencies japicmp git add docs/apidiffs + - name: Update versions in README.md + env: + VERSION: ${{ needs.release.outputs.version }} + run: | + ./gradlew updateVersionInDocs -Prelease.version=$VERSION + git add README.md + - name: Use CLA approved bot run: .github/scripts/use-cla-approved-github-bot.sh @@ -202,9 +209,9 @@ jobs: # not using secrets.GITHUB_TOKEN since pull requests from that token do not run workflows GH_TOKEN: ${{ steps.otelbot-token.outputs.token }} run: | - message="Update apidiff baseline to released version $VERSION" - body="Update apidiff baseline to released version \`$VERSION\`." - branch="otelbot/update-apidiff-baseline-to-released-version-${VERSION}" + message="Update apidiff baseline and documentation versions to released version $VERSION" + body="Update apidiff baseline and documentation versions to released version \`$VERSION\`." + branch="otelbot/update-apidiff-baseline-and-documentation-to-released-version-${VERSION}" git checkout -b $branch git commit -m "$message" diff --git a/CHANGELOG.md b/CHANGELOG.md index f305b59d06..ca0286e2ad 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,62 @@ ## Unreleased +## Version 1.52.0 (2025-07-11) + +### API + +#### Common + +* Promote `ComponentLoader` to new `opentelemetry-common` artifact, + standardize SPI loading + ([#7446](https://github.com/open-telemetry/opentelemetry-java/pull/7446)) + +#### Context + +* LazyStorage passes its ClassLoader when loading ContextStorageProvider SPI + ([#7424](https://github.com/open-telemetry/opentelemetry-java/pull/7424)) + +#### Incubator + +* Add context and severity params to ExtendedLogger#isEnabled + ([#7268](https://github.com/open-telemetry/opentelemetry-java/pull/7268)) +* Add new convenience methods for converting DeclarativeConfigProperties to config model + ([#7453](https://github.com/open-telemetry/opentelemetry-java/pull/7453)) + +### SDK + +* Add custom stacktrace renderer which is length limit aware + ([#7281](https://github.com/open-telemetry/opentelemetry-java/pull/7281)) + +#### Metrics + +* Propagate flush to PeriodicMetricReader's metricExporter. + ([#7410](https://github.com/open-telemetry/opentelemetry-java/pull/7410)) + +#### Exporters + +* OTLP - JdkHttpSender: ensure proper closure of HttpClient in shutdown method + ([#7390](https://github.com/open-telemetry/opentelemetry-java/pull/7390)) +* OTLP: profile exporters fix and test improvements + ([#7442](https://github.com/open-telemetry/opentelemetry-java/pull/7442)) +* OTLP: Loading Compressor SPI via ComponentLoader configured through setComponentLoader + ([#7428](https://github.com/open-telemetry/opentelemetry-java/pull/7428)) +* Prometheus: add scope schema URL and attributes + ([#7356](https://github.com/open-telemetry/opentelemetry-java/pull/7356)) +* Prometheus: extend prometheus declarative config support to include without_scope_info, + with_resource_constant_labels + ([#6840](https://github.com/open-telemetry/opentelemetry-java/pull/6840)) + +#### Extensions + +* Autoconfigure: fix race condition of `GlobalOpenTelemetry` initialization with + `AutoConfiguredOpenTelemetrySdkBuilder` + ([#7365](https://github.com/open-telemetry/opentelemetry-java/pull/7365)) +* Declarative config: update to declarative config 1.0-rc.1 + ([#7436](https://github.com/open-telemetry/opentelemetry-java/pull/7436)) +* Declarative config: resolve environment variable substitution for mixed quotes + ([#7433](https://github.com/open-telemetry/opentelemetry-java/pull/7433)) + ## Version 1.51.0 (2025-06-06) ### API diff --git a/README.md b/README.md index 88b2de8e15..a289bde1e2 100644 --- a/README.md +++ b/README.md @@ -57,8 +57,8 @@ A bill of materials (or BOM) helps sync dependency versions of related artifacts | Component | Description | Artifact ID | Version | Javadoc | |----------------------------------------------|----------------------------------------|---------------------------|-------------------------------------------------------------|---------| -| [Bill of Materials (BOM)](./bom) | Bill of materials for stable artifacts | `opentelemetry-bom` | 1.49.0 | N/A | -| [Alpha Bill of Materials (BOM)](./bom-alpha) | Bill of materials for alpha artifacts | `opentelemetry-bom-alpha` | 1.49.0-alpha | N/A | +| [Bill of Materials (BOM)](./bom) | Bill of materials for stable artifacts | `opentelemetry-bom` | 1.52.0 | N/A | +| [Alpha Bill of Materials (BOM)](./bom-alpha) | Bill of materials for alpha artifacts | `opentelemetry-bom-alpha` | 1.52.0-alpha | N/A |
@@ -68,9 +68,10 @@ The OpenTelemetry API for recording telemetry. | Component | Description | Artifact ID | Version | Javadoc | |-----------------------------------|--------------------------------------------------------------------------------------|-------------------------------|-------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| [API](./api/all) | OpenTelemetry API, including metrics, traces, baggage, context | `opentelemetry-api` | 1.49.0 | [![Javadocs](https://www.javadoc.io/badge/io.opentelemetry/opentelemetry-api.svg)](https://www.javadoc.io/doc/io.opentelemetry/opentelemetry-api) | -| [API Incubator](./api/incubator) | API incubator, including pass through propagator, and extended tracer, and Event API | `opentelemetry-api-incubator` | 1.49.0-alpha | [![Javadocs](https://www.javadoc.io/badge/io.opentelemetry/opentelemetry-api-incubator.svg)](https://www.javadoc.io/doc/io.opentelemetry/opentelemetry-api-incubator) | -| [Context API](./context) | OpenTelemetry context API | `opentelemetry-context` | 1.49.0 | [![Javadocs](https://www.javadoc.io/badge/io.opentelemetry/opentelemetry-context.svg)](https://www.javadoc.io/doc/io.opentelemetry/opentelemetry-context) | +| [API](./api/all) | OpenTelemetry API, including metrics, traces, baggage, context | `opentelemetry-api` | 1.52.0 | [![Javadocs](https://www.javadoc.io/badge/io.opentelemetry/opentelemetry-api.svg)](https://www.javadoc.io/doc/io.opentelemetry/opentelemetry-api) | +| [API Incubator](./api/incubator) | API incubator, including pass through propagator, and extended tracer, and Event API | `opentelemetry-api-incubator` | 1.52.0-alpha | [![Javadocs](https://www.javadoc.io/badge/io.opentelemetry/opentelemetry-api-incubator.svg)](https://www.javadoc.io/doc/io.opentelemetry/opentelemetry-api-incubator) | +| [Context API](./context) | OpenTelemetry context API | `opentelemetry-context` | 1.52.0 | [![Javadocs](https://www.javadoc.io/badge/io.opentelemetry/opentelemetry-context.svg)](https://www.javadoc.io/doc/io.opentelemetry/opentelemetry-context) | +| [Common](./common) | Common utility methods used across API components | `opentelemetry-common` | 1.52.0 | [![Javadocs](https://www.javadoc.io/badge/io.opentelemetry/opentelemetry-common.svg)](https://www.javadoc.io/doc/io.opentelemetry/opentelemetry-common) |
@@ -80,8 +81,8 @@ Extensions to the OpenTelemetry API. | Component | Description | Artifact ID | Version | Javadoc | |---------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------|-------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| [Kotlin Extension](./extensions/kotlin) | Context extension for coroutines | `opentelemetry-extension-kotlin` | 1.49.0 | [![Javadocs](https://www.javadoc.io/badge/io.opentelemetry/opentelemetry-extension-kotlin.svg)](https://www.javadoc.io/doc/io.opentelemetry/opentelemetry-extension-kotlin) | -| [Trace Propagators Extension](./extensions/trace-propagators) | Trace propagators, including B3, Jaeger, OT Trace | `opentelemetry-extension-trace-propagators` | 1.49.0 | [![Javadocs](https://www.javadoc.io/badge/io.opentelemetry/opentelemetry-extension-trace-propagators.svg)](https://www.javadoc.io/doc/io.opentelemetry/opentelemetry-extension-trace-propagators) | +| [Kotlin Extension](./extensions/kotlin) | Context extension for coroutines | `opentelemetry-extension-kotlin` | 1.52.0 | [![Javadocs](https://www.javadoc.io/badge/io.opentelemetry/opentelemetry-extension-kotlin.svg)](https://www.javadoc.io/doc/io.opentelemetry/opentelemetry-extension-kotlin) | +| [Trace Propagators Extension](./extensions/trace-propagators) | Trace propagators, including B3, Jaeger, OT Trace | `opentelemetry-extension-trace-propagators` | 1.52.0 | [![Javadocs](https://www.javadoc.io/badge/io.opentelemetry/opentelemetry-extension-trace-propagators.svg)](https://www.javadoc.io/doc/io.opentelemetry/opentelemetry-extension-trace-propagators) |
@@ -91,12 +92,12 @@ The OpenTelemetry SDK for managing telemetry producing by the API. | Component | Description | Artifact ID | Version | Javadoc | |------------------------------|--------------------------------------------------------|-----------------------------|---------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| [SDK](./sdk/all) | OpenTelemetry SDK, including metrics, traces, and logs | `opentelemetry-sdk` | 1.49.0 | [![Javadocs](https://www.javadoc.io/badge/io.opentelemetry/opentelemetry-sdk.svg)](https://www.javadoc.io/doc/io.opentelemetry/opentelemetry-sdk) | -| [Metrics SDK](./sdk/metrics) | OpenTelemetry metrics SDK | `opentelemetry-sdk-metrics` | 1.49.0 | [![Javadocs](https://www.javadoc.io/badge/io.opentelemetry/opentelemetry-sdk-metrics.svg)](https://www.javadoc.io/doc/io.opentelemetry/opentelemetry-sdk-metrics) | -| [Trace SDK](./sdk/trace) | OpenTelemetry trace SDK | `opentelemetry-sdk-trace` | 1.49.0 | [![Javadocs](https://www.javadoc.io/badge/io.opentelemetry/opentelemetry-sdk-trace.svg)](https://www.javadoc.io/doc/io.opentelemetry/opentelemetry-sdk-trace) | -| [Log SDK](./sdk/logs) | OpenTelemetry log SDK | `opentelemetry-sdk-logs` | 1.49.0 | [![Javadocs](https://www.javadoc.io/badge/io.opentelemetry/opentelemetry-sdk-logs.svg)](https://www.javadoc.io/doc/io.opentelemetry/opentelemetry-sdk-logs) | -| [SDK Common](./sdk/common) | Shared SDK components | `opentelemetry-sdk-common` | 1.49.0 | [![Javadocs](https://www.javadoc.io/badge/io.opentelemetry/opentelemetry-sdk-common.svg)](https://www.javadoc.io/doc/io.opentelemetry/opentelemetry-sdk-common) | -| [SDK Testing](./sdk/testing) | Components for testing OpenTelemetry instrumentation | `opentelemetry-sdk-testing` | 1.49.0 | [![Javadocs](https://www.javadoc.io/badge/io.opentelemetry/opentelemetry-sdk-testing.svg)](https://www.javadoc.io/doc/io.opentelemetry/opentelemetry-sdk-testing) | +| [SDK](./sdk/all) | OpenTelemetry SDK, including metrics, traces, and logs | `opentelemetry-sdk` | 1.52.0 | [![Javadocs](https://www.javadoc.io/badge/io.opentelemetry/opentelemetry-sdk.svg)](https://www.javadoc.io/doc/io.opentelemetry/opentelemetry-sdk) | +| [Metrics SDK](./sdk/metrics) | OpenTelemetry metrics SDK | `opentelemetry-sdk-metrics` | 1.52.0 | [![Javadocs](https://www.javadoc.io/badge/io.opentelemetry/opentelemetry-sdk-metrics.svg)](https://www.javadoc.io/doc/io.opentelemetry/opentelemetry-sdk-metrics) | +| [Trace SDK](./sdk/trace) | OpenTelemetry trace SDK | `opentelemetry-sdk-trace` | 1.52.0 | [![Javadocs](https://www.javadoc.io/badge/io.opentelemetry/opentelemetry-sdk-trace.svg)](https://www.javadoc.io/doc/io.opentelemetry/opentelemetry-sdk-trace) | +| [Log SDK](./sdk/logs) | OpenTelemetry log SDK | `opentelemetry-sdk-logs` | 1.52.0 | [![Javadocs](https://www.javadoc.io/badge/io.opentelemetry/opentelemetry-sdk-logs.svg)](https://www.javadoc.io/doc/io.opentelemetry/opentelemetry-sdk-logs) | +| [SDK Common](./sdk/common) | Shared SDK components | `opentelemetry-sdk-common` | 1.52.0 | [![Javadocs](https://www.javadoc.io/badge/io.opentelemetry/opentelemetry-sdk-common.svg)](https://www.javadoc.io/doc/io.opentelemetry/opentelemetry-sdk-common) | +| [SDK Testing](./sdk/testing) | Components for testing OpenTelemetry instrumentation | `opentelemetry-sdk-testing` | 1.52.0 | [![Javadocs](https://www.javadoc.io/badge/io.opentelemetry/opentelemetry-sdk-testing.svg)](https://www.javadoc.io/doc/io.opentelemetry/opentelemetry-sdk-testing) |
@@ -106,16 +107,16 @@ SDK exporters for shipping traces, metrics, and logs out of process. | Component | Description | Artifact ID | Version | Javadoc | |-----------------------------------------------------------------------|------------------------------------------------------------------------------|------------------------------------------------------|-------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| [OTLP Exporters](./exporters/otlp/all) | OTLP gRPC & HTTP exporters, including traces, metrics, and logs | `opentelemetry-exporter-otlp` | 1.49.0 | [![Javadocs](https://www.javadoc.io/badge/io.opentelemetry/opentelemetry-exporter-otlp.svg)](https://www.javadoc.io/doc/io.opentelemetry/opentelemetry-exporter-otlp) | -| [OTLP Logging Exporters](./exporters/logging-otlp) | Logging exporters in OTLP JSON encoding, including traces, metrics, and logs | `opentelemetry-exporter-logging-otlp` | 1.49.0 | [![Javadocs](https://www.javadoc.io/badge/io.opentelemetry/opentelemetry-exporter-logging-otlp.svg)](https://www.javadoc.io/doc/io.opentelemetry/opentelemetry-exporter-logging-otlp) | -| [OTLP Common](./exporters/otlp/common) | Shared OTLP components (internal) | `opentelemetry-exporter-otlp-common` | 1.49.0 | [![Javadocs](https://www.javadoc.io/badge/io.opentelemetry/opentelemetry-exporter-otlp-common.svg)](https://www.javadoc.io/doc/io.opentelemetry/opentelemetry-exporter-otlp-common) | -| [Logging Exporter](./exporters/logging) | Logging exporters, including metrics, traces, and logs | `opentelemetry-exporter-logging` | 1.49.0 | [![Javadocs](https://www.javadoc.io/badge/io.opentelemetry/opentelemetry-exporter-logging.svg)](https://www.javadoc.io/doc/io.opentelemetry/opentelemetry-exporter-logging) | -| [Zipkin Exporter](./exporters/zipkin) | Zipkin trace exporter | `opentelemetry-exporter-zipkin` | 1.49.0 | [![Javadocs](https://www.javadoc.io/badge/io.opentelemetry/opentelemetry-exporter-zipkin.svg)](https://www.javadoc.io/doc/io.opentelemetry/opentelemetry-exporter-zipkin) | -| [Prometheus Exporter](./exporters/prometheus) | Prometheus metric exporter | `opentelemetry-exporter-prometheus` | 1.49.0-alpha | [![Javadocs](https://www.javadoc.io/badge/io.opentelemetry/opentelemetry-exporter-prometheus.svg)](https://www.javadoc.io/doc/io.opentelemetry/opentelemetry-exporter-prometheus) | -| [Exporter Common](./exporters/common) | Shared exporter components (internal) | `opentelemetry-exporter-common` | 1.49.0 | [![Javadocs](https://www.javadoc.io/badge/io.opentelemetry/opentelemetry-exporter-common.svg)](https://www.javadoc.io/doc/io.opentelemetry/opentelemetry-exporter-common) | -| [OkHttp Sender](./exporters/sender/okhttp) | OkHttp implementation of HttpSender (internal) | `opentelemetry-exporter-sender-okhttp` | 1.49.0 | [![Javadocs](https://www.javadoc.io/badge/io.opentelemetry/opentelemetry-exporter-sender-okhttp.svg)](https://www.javadoc.io/doc/io.opentelemetry/opentelemetry-exporter-sender-okhttp) | -| [JDK Sender](./exporters/sender/jdk) | Java 11+ native HttpClient implementation of HttpSender (internal) | `opentelemetry-exporter-sender-jdk` | 1.49.0 | [![Javadocs](https://www.javadoc.io/badge/io.opentelemetry/opentelemetry-exporter-sender-jdk.svg)](https://www.javadoc.io/doc/io.opentelemetry/opentelemetry-exporter-sender-jdk) | | -| [gRPC ManagedChannel Sender](./exporters/sender/grpc-managed-channel) | gRPC ManagedChannel implementation of GrpcSender (internal) | `opentelemetry-exporter-sender-grpc-managed-channel` | 1.49.0 | [![Javadocs](https://www.javadoc.io/badge/io.opentelemetry/opentelemetry-exporter-sender-grpc-managed-channel.svg)](https://www.javadoc.io/doc/io.opentelemetry/opentelemetry-exporter-sender-grpc-managed-channel) | | +| [OTLP Exporters](./exporters/otlp/all) | OTLP gRPC & HTTP exporters, including traces, metrics, and logs | `opentelemetry-exporter-otlp` | 1.52.0 | [![Javadocs](https://www.javadoc.io/badge/io.opentelemetry/opentelemetry-exporter-otlp.svg)](https://www.javadoc.io/doc/io.opentelemetry/opentelemetry-exporter-otlp) | +| [OTLP Logging Exporters](./exporters/logging-otlp) | Logging exporters in OTLP JSON encoding, including traces, metrics, and logs | `opentelemetry-exporter-logging-otlp` | 1.52.0 | [![Javadocs](https://www.javadoc.io/badge/io.opentelemetry/opentelemetry-exporter-logging-otlp.svg)](https://www.javadoc.io/doc/io.opentelemetry/opentelemetry-exporter-logging-otlp) | +| [OTLP Common](./exporters/otlp/common) | Shared OTLP components (internal) | `opentelemetry-exporter-otlp-common` | 1.52.0 | [![Javadocs](https://www.javadoc.io/badge/io.opentelemetry/opentelemetry-exporter-otlp-common.svg)](https://www.javadoc.io/doc/io.opentelemetry/opentelemetry-exporter-otlp-common) | +| [Logging Exporter](./exporters/logging) | Logging exporters, including metrics, traces, and logs | `opentelemetry-exporter-logging` | 1.52.0 | [![Javadocs](https://www.javadoc.io/badge/io.opentelemetry/opentelemetry-exporter-logging.svg)](https://www.javadoc.io/doc/io.opentelemetry/opentelemetry-exporter-logging) | +| [Zipkin Exporter](./exporters/zipkin) | Zipkin trace exporter | `opentelemetry-exporter-zipkin` | 1.52.0 | [![Javadocs](https://www.javadoc.io/badge/io.opentelemetry/opentelemetry-exporter-zipkin.svg)](https://www.javadoc.io/doc/io.opentelemetry/opentelemetry-exporter-zipkin) | +| [Prometheus Exporter](./exporters/prometheus) | Prometheus metric exporter | `opentelemetry-exporter-prometheus` | 1.52.0-alpha | [![Javadocs](https://www.javadoc.io/badge/io.opentelemetry/opentelemetry-exporter-prometheus.svg)](https://www.javadoc.io/doc/io.opentelemetry/opentelemetry-exporter-prometheus) | +| [Exporter Common](./exporters/common) | Shared exporter components (internal) | `opentelemetry-exporter-common` | 1.52.0 | [![Javadocs](https://www.javadoc.io/badge/io.opentelemetry/opentelemetry-exporter-common.svg)](https://www.javadoc.io/doc/io.opentelemetry/opentelemetry-exporter-common) | +| [OkHttp Sender](./exporters/sender/okhttp) | OkHttp implementation of HttpSender (internal) | `opentelemetry-exporter-sender-okhttp` | 1.52.0 | [![Javadocs](https://www.javadoc.io/badge/io.opentelemetry/opentelemetry-exporter-sender-okhttp.svg)](https://www.javadoc.io/doc/io.opentelemetry/opentelemetry-exporter-sender-okhttp) | +| [JDK Sender](./exporters/sender/jdk) | Java 11+ native HttpClient implementation of HttpSender (internal) | `opentelemetry-exporter-sender-jdk` | 1.52.0 | [![Javadocs](https://www.javadoc.io/badge/io.opentelemetry/opentelemetry-exporter-sender-jdk.svg)](https://www.javadoc.io/doc/io.opentelemetry/opentelemetry-exporter-sender-jdk) | | +| [gRPC ManagedChannel Sender](./exporters/sender/grpc-managed-channel) | gRPC ManagedChannel implementation of GrpcSender (internal) | `opentelemetry-exporter-sender-grpc-managed-channel` | 1.52.0 | [![Javadocs](https://www.javadoc.io/badge/io.opentelemetry/opentelemetry-exporter-sender-grpc-managed-channel.svg)](https://www.javadoc.io/doc/io.opentelemetry/opentelemetry-exporter-sender-grpc-managed-channel) | |
@@ -125,10 +126,10 @@ Extensions to the OpenTelemetry SDK. | Component | Description | Artifact ID | Version | Javadoc | |-------------------------------------------------------------------------------|------------------------------------------------------------------------------------|-----------------------------------------------------|-------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| [SDK Autoconfigure](./sdk-extensions/autoconfigure) | Autoconfigure OpenTelemetry SDK from env vars, system properties, and SPI | `opentelemetry-sdk-extension-autoconfigure` | 1.49.0 | [![Javadocs](https://www.javadoc.io/badge/io.opentelemetry/opentelemetry-sdk-extension-autoconfigure.svg)](https://www.javadoc.io/doc/io.opentelemetry/opentelemetry-sdk-extension-autoconfigure) | -| [SDK Autoconfigure SPI](./sdk-extensions/autoconfigure-spi) | Service Provider Interface (SPI) definitions for autoconfigure | `opentelemetry-sdk-extension-autoconfigure-spi` | 1.49.0 | [![Javadocs](https://www.javadoc.io/badge/io.opentelemetry/opentelemetry-sdk-extension-autoconfigure-spi.svg)](https://www.javadoc.io/doc/io.opentelemetry/opentelemetry-sdk-extension-autoconfigure-spi) | -| [SDK Jaeger Remote Sampler Extension](./sdk-extensions/jaeger-remote-sampler) | Sampler which obtains sampling configuration from remote Jaeger server | `opentelemetry-sdk-extension-jaeger-remote-sampler` | 1.49.0 | [![Javadocs](https://www.javadoc.io/badge/io.opentelemetry/opentelemetry-sdk-extension-jaeger-remote-sampler.svg)](https://www.javadoc.io/doc/io.opentelemetry/opentelemetry-sdk-extension-jaeger-remote-sampler) | -| [SDK Incubator](./sdk-extensions/incubator) | SDK incubator, including YAML based view configuration, LeakDetectingSpanProcessor | `opentelemetry-sdk-extension-incubator` | 1.49.0-alpha | [![Javadocs](https://www.javadoc.io/badge/io.opentelemetry/opentelemetry-sdk-extension-incubator.svg)](https://www.javadoc.io/doc/io.opentelemetry/opentelemetry-sdk-extension-incubator) | +| [SDK Autoconfigure](./sdk-extensions/autoconfigure) | Autoconfigure OpenTelemetry SDK from env vars, system properties, and SPI | `opentelemetry-sdk-extension-autoconfigure` | 1.52.0 | [![Javadocs](https://www.javadoc.io/badge/io.opentelemetry/opentelemetry-sdk-extension-autoconfigure.svg)](https://www.javadoc.io/doc/io.opentelemetry/opentelemetry-sdk-extension-autoconfigure) | +| [SDK Autoconfigure SPI](./sdk-extensions/autoconfigure-spi) | Service Provider Interface (SPI) definitions for autoconfigure | `opentelemetry-sdk-extension-autoconfigure-spi` | 1.52.0 | [![Javadocs](https://www.javadoc.io/badge/io.opentelemetry/opentelemetry-sdk-extension-autoconfigure-spi.svg)](https://www.javadoc.io/doc/io.opentelemetry/opentelemetry-sdk-extension-autoconfigure-spi) | +| [SDK Jaeger Remote Sampler Extension](./sdk-extensions/jaeger-remote-sampler) | Sampler which obtains sampling configuration from remote Jaeger server | `opentelemetry-sdk-extension-jaeger-remote-sampler` | 1.52.0 | [![Javadocs](https://www.javadoc.io/badge/io.opentelemetry/opentelemetry-sdk-extension-jaeger-remote-sampler.svg)](https://www.javadoc.io/doc/io.opentelemetry/opentelemetry-sdk-extension-jaeger-remote-sampler) | +| [SDK Incubator](./sdk-extensions/incubator) | SDK incubator, including YAML based view configuration, LeakDetectingSpanProcessor | `opentelemetry-sdk-extension-incubator` | 1.52.0-alpha | [![Javadocs](https://www.javadoc.io/badge/io.opentelemetry/opentelemetry-sdk-extension-incubator.svg)](https://www.javadoc.io/doc/io.opentelemetry/opentelemetry-sdk-extension-incubator) |
@@ -138,8 +139,8 @@ Shims for bridging data from one observability library to another. | Component | Description | Artifact ID | Version | Javadoc | |----------------------------------------|--------------------------------------------------------------|----------------------------------|-------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| [OpenCensus Shim](./opencensus-shim) | Bridge opencensus metrics into the OpenTelemetry metrics SDK | `opentelemetry-opencensus-shim` | 1.49.0-alpha | [![Javadocs](https://www.javadoc.io/badge/io.opentelemetry/opentelemetry-opencensus-shim.svg)](https://www.javadoc.io/doc/io.opentelemetry/opentelemetry-opencensus-shim) | -| [OpenTracing Shim](./opentracing-shim) | Bridge opentracing spans into the OpenTelemetry trace API | `opentelemetry-opentracing-shim` | 1.49.0 | [![Javadocs](https://www.javadoc.io/badge/io.opentelemetry/opentelemetry-opentracing-shim.svg)](https://www.javadoc.io/doc/io.opentelemetry/opentelemetry-opentracing-shim) | +| [OpenCensus Shim](./opencensus-shim) | Bridge opencensus metrics into the OpenTelemetry metrics SDK | `opentelemetry-opencensus-shim` | 1.52.0-alpha | [![Javadocs](https://www.javadoc.io/badge/io.opentelemetry/opentelemetry-opencensus-shim.svg)](https://www.javadoc.io/doc/io.opentelemetry/opentelemetry-opencensus-shim) | +| [OpenTracing Shim](./opentracing-shim) | Bridge opentracing spans into the OpenTelemetry trace API | `opentelemetry-opentracing-shim` | 1.52.0 | [![Javadocs](https://www.javadoc.io/badge/io.opentelemetry/opentelemetry-opentracing-shim.svg)](https://www.javadoc.io/doc/io.opentelemetry/opentelemetry-opentracing-shim) |
## Dependencies @@ -180,7 +181,7 @@ repositories { } dependencies { - implementation platform("io.opentelemetry:opentelemetry-bom:1.50.0-SNAPSHOT") + implementation platform("io.opentelemetry:opentelemetry-bom:1.53.0-SNAPSHOT") implementation('io.opentelemetry:opentelemetry-api') } ``` @@ -202,7 +203,7 @@ dependencies { io.opentelemetry opentelemetry-bom - 1.50.0-SNAPSHOT + 1.53.0-SNAPSHOT pom import @@ -254,16 +255,14 @@ See [CONTRIBUTING.md](CONTRIBUTING.md) for: * Keys to successful PRs * Guide to using gradle composite builds -### Code owners - -#### Maintainers +### Maintainers - [Jack Berg](https://github.com/jack-berg), New Relic - [John Watson](https://github.com/jkwatson), Verta.ai For more information about the maintainer role, see the [community repository](https://github.com/open-telemetry/community/blob/main/guides/contributor/membership.md#maintainer). -#### Approvers +### Approvers - [Jason Plumb](https://github.com/breedx-splk), Splunk - [Josh Suereth](https://github.com/jsuereth), Google @@ -272,13 +271,13 @@ For more information about the maintainer role, see the [community repository](h For more information about the approver role, see the [community repository](https://github.com/open-telemetry/community/blob/main/guides/contributor/membership.md#approver). -#### Triagers +### Triagers - [Gregor Zeitlinger](https://github.com/zeitlinger), Grafana Labs For more information about the triager role, see the [community repository](https://github.com/open-telemetry/community/blob/main/guides/contributor/membership.md#triager). -#### Emeritus +### Emeritus - Maintainer [Bogdan Drutu](https://github.com/BogdanDrutu) - Maintainer [Carlos Alberto](https://github.com/carlosalberto) diff --git a/RELEASING.md b/RELEASING.md index 01e34f95e1..aa40c2105b 100644 --- a/RELEASING.md +++ b/RELEASING.md @@ -57,6 +57,11 @@ and deadlocks. * Review and merge the pull request that it creates for updating the change log in main (note that if this is not a patch release then the change log on main may already be up-to-date, in which case no pull request will be created). + * Once the release artifacts become available on Maven Central, the system will automatically + generate a new pull request titled `Update apidiff baseline and documentation versions to + released version $VERSION`. This pull request will contain updates to both the API diff baseline + and version references in the documentation files (README.md). Please review and merge this + automated pull request. * The [website](https://github.com/open-telemetry/opentelemetry.io) contains automation to update to the newly released version. Review and approve the pull request when available. * The [website](https://opentelemetry.io/docs/languages/java/configuration/#zero-code-sdk-autoconfigure) diff --git a/all/src/test/java/io/opentelemetry/all/SdkDesignTest.java b/all/src/test/java/io/opentelemetry/all/SdkDesignTest.java index 1a1271b7c2..fe2fb0045e 100644 --- a/all/src/test/java/io/opentelemetry/all/SdkDesignTest.java +++ b/all/src/test/java/io/opentelemetry/all/SdkDesignTest.java @@ -28,7 +28,7 @@ class SdkDesignTest { * Ensures that all SDK methods that: - are defined in classes that extend or implement API model * and are public (to exclude protected builders) - are public (avoids issues with protected * methods returning classes unavailable to test's CL) - override or implement parent method - * return only API, Context or generic Java type. + * return only API, Context, Common, or generic Java type. */ @Test void sdkImplementationOfApiClassesShouldReturnApiTypeOnly() { @@ -45,7 +45,11 @@ class SdkDesignTest { .and(implementOrOverride()) .should() .haveRawReturnType( - inPackage("io.opentelemetry.api..", "io.opentelemetry.context..", "java..")) + inPackage( + "io.opentelemetry.api..", + "io.opentelemetry.context..", + "io.opentelemetry.common..", + "java..")) .orShould() .haveRawReturnType("void"); diff --git a/api/all/src/main/java/io/opentelemetry/api/GlobalOpenTelemetry.java b/api/all/src/main/java/io/opentelemetry/api/GlobalOpenTelemetry.java index 83e70bf7d8..5141307c34 100644 --- a/api/all/src/main/java/io/opentelemetry/api/GlobalOpenTelemetry.java +++ b/api/all/src/main/java/io/opentelemetry/api/GlobalOpenTelemetry.java @@ -122,6 +122,8 @@ public final class GlobalOpenTelemetry { * *

This method calls the given {@code supplier} and calls {@link #set(OpenTelemetry)}, all * while holding the {@link GlobalOpenTelemetry} mutex. + * + * @since 1.52.0 */ public static void set(Supplier supplier) { synchronized (mutex) { diff --git a/api/incubator/build.gradle.kts b/api/incubator/build.gradle.kts index 205b5504a0..6bd0669222 100644 --- a/api/incubator/build.gradle.kts +++ b/api/incubator/build.gradle.kts @@ -12,6 +12,9 @@ otelJava.moduleName.set("io.opentelemetry.api.incubator") dependencies { api(project(":api:all")) + // Supports optional InstrumentationConfigUtil#convertToModel + compileOnly("com.fasterxml.jackson.core:jackson-databind") + annotationProcessor("com.google.auto.value:auto-value") // To use parsed config file as input for InstrumentationConfigUtilTest @@ -24,3 +27,16 @@ dependencies { testImplementation("com.google.guava:guava") } + +testing { + suites { + register("testConvertToModel") { + dependencies { + implementation("com.fasterxml.jackson.core:jackson-databind") + implementation(project(":sdk-extensions:incubator")) + implementation(project(":sdk-extensions:autoconfigure")) + implementation("com.google.guava:guava") + } + } + } +} diff --git a/api/incubator/src/main/java/io/opentelemetry/api/incubator/config/DeclarativeConfigProperties.java b/api/incubator/src/main/java/io/opentelemetry/api/incubator/config/DeclarativeConfigProperties.java index ef8e2343f2..cf98312c9e 100644 --- a/api/incubator/src/main/java/io/opentelemetry/api/incubator/config/DeclarativeConfigProperties.java +++ b/api/incubator/src/main/java/io/opentelemetry/api/incubator/config/DeclarativeConfigProperties.java @@ -7,7 +7,9 @@ package io.opentelemetry.api.incubator.config; import static io.opentelemetry.api.internal.ConfigUtil.defaultIfNull; +import io.opentelemetry.common.ComponentLoader; import java.util.List; +import java.util.Map; import java.util.Set; import javax.annotation.Nullable; @@ -36,6 +38,11 @@ public interface DeclarativeConfigProperties { return EmptyDeclarativeConfigProperties.getInstance(); } + /** Return a map representation of the {@code declarativeConfigProperties}. */ + static Map toMap(DeclarativeConfigProperties declarativeConfigProperties) { + return DeclarativeConfigPropertyUtil.toMap(declarativeConfigProperties); + } + /** * Returns a {@link String} configuration property. * @@ -222,4 +229,7 @@ public interface DeclarativeConfigProperties { * @return the configuration property keys */ Set getPropertyKeys(); + + /** Return a {@link ComponentLoader} that should be used to load SPIs. */ + ComponentLoader getComponentLoader(); } diff --git a/api/incubator/src/main/java/io/opentelemetry/api/incubator/config/DeclarativeConfigPropertyUtil.java b/api/incubator/src/main/java/io/opentelemetry/api/incubator/config/DeclarativeConfigPropertyUtil.java new file mode 100644 index 0000000000..46722f4d49 --- /dev/null +++ b/api/incubator/src/main/java/io/opentelemetry/api/incubator/config/DeclarativeConfigPropertyUtil.java @@ -0,0 +1,126 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.api.incubator.config; + +import static java.util.stream.Collectors.toList; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import java.util.function.BiFunction; +import javax.annotation.Nullable; + +final class DeclarativeConfigPropertyUtil { + + private DeclarativeConfigPropertyUtil() {} + + private static final List> + valueResolvers = + Arrays.asList( + DeclarativeConfigPropertyUtil::getString, + DeclarativeConfigPropertyUtil::getBoolean, + DeclarativeConfigPropertyUtil::getLong, + DeclarativeConfigPropertyUtil::getDouble, + DeclarativeConfigPropertyUtil::getStringList, + DeclarativeConfigPropertyUtil::getBooleanList, + DeclarativeConfigPropertyUtil::getLongList, + DeclarativeConfigPropertyUtil::getDoubleList, + DeclarativeConfigPropertyUtil::getStringList, + DeclarativeConfigPropertyUtil::getStructuredList, + DeclarativeConfigPropertyUtil::getStructured); + + static Map toMap(DeclarativeConfigProperties declarativeConfigProperties) { + Set propertyKeys = declarativeConfigProperties.getPropertyKeys(); + Map result = new HashMap<>(propertyKeys.size()); + for (String key : declarativeConfigProperties.getPropertyKeys()) { + result.put(key, resolveValue(key, declarativeConfigProperties)); + } + return result; + } + + @Nullable + private static Object resolveValue( + String key, DeclarativeConfigProperties declarativeConfigProperties) { + for (int i = 0; i < valueResolvers.size(); i++) { + try { + Object value = valueResolvers.get(i).apply(key, declarativeConfigProperties); + if (value != null) { + return value; + } + } catch (DeclarativeConfigException e) { + // Ignore and continue + } + } + return null; + } + + @Nullable + private static Object getString( + String key, DeclarativeConfigProperties declarativeConfigProperties) { + return declarativeConfigProperties.getString(key); + } + + @Nullable + private static Object getBoolean( + String key, DeclarativeConfigProperties declarativeConfigProperties) { + return declarativeConfigProperties.getBoolean(key); + } + + @Nullable + private static Object getLong( + String key, DeclarativeConfigProperties declarativeConfigProperties) { + return declarativeConfigProperties.getLong(key); + } + + @Nullable + private static Object getDouble( + String key, DeclarativeConfigProperties declarativeConfigProperties) { + return declarativeConfigProperties.getDouble(key); + } + + @Nullable + private static Object getStringList( + String key, DeclarativeConfigProperties declarativeConfigProperties) { + return declarativeConfigProperties.getScalarList(key, String.class); + } + + @Nullable + private static Object getBooleanList( + String key, DeclarativeConfigProperties declarativeConfigProperties) { + return declarativeConfigProperties.getScalarList(key, Boolean.class); + } + + @Nullable + private static Object getLongList( + String key, DeclarativeConfigProperties declarativeConfigProperties) { + return declarativeConfigProperties.getScalarList(key, Long.class); + } + + @Nullable + private static Object getDoubleList( + String key, DeclarativeConfigProperties declarativeConfigProperties) { + return declarativeConfigProperties.getScalarList(key, Double.class); + } + + @Nullable + private static Object getStructuredList( + String key, DeclarativeConfigProperties declarativeConfigProperties) { + return Optional.ofNullable(declarativeConfigProperties.getStructuredList(key)) + .map(list -> list.stream().map(DeclarativeConfigPropertyUtil::toMap).collect(toList())) + .orElse(null); + } + + @Nullable + private static Object getStructured( + String key, DeclarativeConfigProperties declarativeConfigProperties) { + return Optional.ofNullable(declarativeConfigProperties.getStructured(key)) + .map(DeclarativeConfigPropertyUtil::toMap) + .orElse(null); + } +} diff --git a/api/incubator/src/main/java/io/opentelemetry/api/incubator/config/EmptyDeclarativeConfigProperties.java b/api/incubator/src/main/java/io/opentelemetry/api/incubator/config/EmptyDeclarativeConfigProperties.java index 77b8a26549..6c3a85f1ee 100644 --- a/api/incubator/src/main/java/io/opentelemetry/api/incubator/config/EmptyDeclarativeConfigProperties.java +++ b/api/incubator/src/main/java/io/opentelemetry/api/incubator/config/EmptyDeclarativeConfigProperties.java @@ -5,6 +5,7 @@ package io.opentelemetry.api.incubator.config; +import io.opentelemetry.common.ComponentLoader; import java.util.Collections; import java.util.List; import java.util.Set; @@ -15,6 +16,8 @@ final class EmptyDeclarativeConfigProperties implements DeclarativeConfigPropert private static final EmptyDeclarativeConfigProperties INSTANCE = new EmptyDeclarativeConfigProperties(); + private static final ComponentLoader COMPONENT_LOADER = + ComponentLoader.forClassLoader(EmptyDeclarativeConfigProperties.class.getClassLoader()); private EmptyDeclarativeConfigProperties() {} @@ -74,4 +77,9 @@ final class EmptyDeclarativeConfigProperties implements DeclarativeConfigPropert public Set getPropertyKeys() { return Collections.emptySet(); } + + @Override + public ComponentLoader getComponentLoader() { + return COMPONENT_LOADER; + } } diff --git a/api/incubator/src/main/java/io/opentelemetry/api/incubator/config/InstrumentationConfigUtil.java b/api/incubator/src/main/java/io/opentelemetry/api/incubator/config/InstrumentationConfigUtil.java index e1e95e9315..c5cfcc474e 100644 --- a/api/incubator/src/main/java/io/opentelemetry/api/incubator/config/InstrumentationConfigUtil.java +++ b/api/incubator/src/main/java/io/opentelemetry/api/incubator/config/InstrumentationConfigUtil.java @@ -5,6 +5,7 @@ package io.opentelemetry.api.incubator.config; +import com.fasterxml.jackson.databind.ObjectMapper; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; @@ -148,4 +149,49 @@ public class InstrumentationConfigUtil { } private InstrumentationConfigUtil() {} + + /** + * Return {@code .instrumentation.java.}, after converting it to the {@code + * modelType} using the {@code objectMapper}. If no configuration exists for the {@code + * instrumentationName}, returns {@code null}. + * + *

This method is a convenience method for a common instrumentation library workflow: + * + *

+ * + *

Conversion is done using {@link ObjectMapper#convertValue(Object, Class)} from {@code + * com.fasterxml.jackson.databind}, and assumes the {@code modelType} is a POJO written / + * annotated to support jackson databinding. + * + *

NOTE: callers MUST add their own dependency on {@code + * com.fasterxml.jackson.core:jackson-databind}. This module's dependency is {@code compileOnly} + * since jackson is a large dependency that many users will not require. It's very possible to + * convert between {@link DeclarativeConfigProperties} (or a map representation from {@link + * DeclarativeConfigProperties#toMap(DeclarativeConfigProperties)}) and a target model type + * without jackson. This method is provided as an optional convenience method. + * + * @throws IllegalArgumentException if conversion fails. See {@link + * ObjectMapper#convertValue(Object, Class)} for details. + */ + @Nullable + public static T getInstrumentationConfigModel( + ConfigProvider configProvider, + String instrumentationName, + ObjectMapper objectMapper, + Class modelType) { + DeclarativeConfigProperties properties = + javaInstrumentationConfig(configProvider, instrumentationName); + if (properties == null) { + return null; + } + Map configPropertiesMap = DeclarativeConfigProperties.toMap(properties); + return objectMapper.convertValue(configPropertiesMap, modelType); + } } diff --git a/api/incubator/src/main/java/io/opentelemetry/api/incubator/logs/ExtendedDefaultLogger.java b/api/incubator/src/main/java/io/opentelemetry/api/incubator/logs/ExtendedDefaultLogger.java index 239f42d7aa..1473149560 100644 --- a/api/incubator/src/main/java/io/opentelemetry/api/incubator/logs/ExtendedDefaultLogger.java +++ b/api/incubator/src/main/java/io/opentelemetry/api/incubator/logs/ExtendedDefaultLogger.java @@ -27,6 +27,11 @@ class ExtendedDefaultLogger implements ExtendedLogger { return INSTANCE; } + @Override + public boolean isEnabled(Severity severity, Context context) { + return false; + } + @Override public ExtendedLogRecordBuilder logRecordBuilder() { return NOOP_LOG_RECORD_BUILDER; diff --git a/api/incubator/src/main/java/io/opentelemetry/api/incubator/logs/ExtendedLogger.java b/api/incubator/src/main/java/io/opentelemetry/api/incubator/logs/ExtendedLogger.java index de159406c0..60f3668590 100644 --- a/api/incubator/src/main/java/io/opentelemetry/api/incubator/logs/ExtendedLogger.java +++ b/api/incubator/src/main/java/io/opentelemetry/api/incubator/logs/ExtendedLogger.java @@ -6,21 +6,41 @@ package io.opentelemetry.api.incubator.logs; import io.opentelemetry.api.logs.Logger; +import io.opentelemetry.api.logs.Severity; +import io.opentelemetry.context.Context; /** Extended {@link Logger} with experimental APIs. */ public interface ExtendedLogger extends Logger { /** - * Returns {@code true} if the logger is enabled. + * Returns {@code true} if the logger is enabled for the given {@code context} and {@code + * severity}. * *

This allows callers to avoid unnecessary compute when nothing is consuming the data. Because * the response is subject to change over the application, callers should call this before each * call to {@link #logRecordBuilder()}. */ - default boolean isEnabled() { + default boolean isEnabled(Severity severity, Context context) { return true; } + /** Overload of {@link #isEnabled(Severity, Context)} assuming {@link Context#current()}. */ + default boolean isEnabled(Severity severity) { + return isEnabled(severity, Context.current()); + } + + /** + * Overload of {@link #isEnabled(Severity, Context)} assuming {@link + * Severity#UNDEFINED_SEVERITY_NUMBER} and {@link Context#current()}. + * + * @deprecated for removal after 1.55.0. Use {@link #isEnabled(Severity, Context)} or {@link + * #isEnabled(Severity)} instead. + */ + @Deprecated + default boolean isEnabled() { + return isEnabled(Severity.UNDEFINED_SEVERITY_NUMBER); + } + @Override ExtendedLogRecordBuilder logRecordBuilder(); } diff --git a/api/incubator/src/main/java/io/opentelemetry/api/incubator/metrics/ExtendedDefaultMeter.java b/api/incubator/src/main/java/io/opentelemetry/api/incubator/metrics/ExtendedDefaultMeter.java index de1ec1fdef..8283c7bb0b 100644 --- a/api/incubator/src/main/java/io/opentelemetry/api/incubator/metrics/ExtendedDefaultMeter.java +++ b/api/incubator/src/main/java/io/opentelemetry/api/incubator/metrics/ExtendedDefaultMeter.java @@ -94,6 +94,11 @@ class ExtendedDefaultMeter implements Meter { private ExtendedDefaultMeter() {} private static class NoopLongCounter implements ExtendedLongCounter { + @Override + public boolean isEnabled() { + return false; + } + @Override public void add(long value, Attributes attributes, Context context) {} @@ -105,6 +110,11 @@ class ExtendedDefaultMeter implements Meter { } private static class NoopDoubleCounter implements ExtendedDoubleCounter { + @Override + public boolean isEnabled() { + return false; + } + @Override public void add(double value, Attributes attributes, Context context) {} @@ -186,6 +196,11 @@ class ExtendedDefaultMeter implements Meter { } private static class NoopLongUpDownCounter implements ExtendedLongUpDownCounter { + @Override + public boolean isEnabled() { + return false; + } + @Override public void add(long value, Attributes attributes, Context context) {} @@ -197,6 +212,11 @@ class ExtendedDefaultMeter implements Meter { } private static class NoopDoubleUpDownCounter implements ExtendedDoubleUpDownCounter { + @Override + public boolean isEnabled() { + return false; + } + @Override public void add(double value, Attributes attributes, Context context) {} @@ -281,6 +301,11 @@ class ExtendedDefaultMeter implements Meter { } private static class NoopDoubleHistogram implements ExtendedDoubleHistogram { + @Override + public boolean isEnabled() { + return false; + } + @Override public void record(double value, Attributes attributes, Context context) {} @@ -292,6 +317,11 @@ class ExtendedDefaultMeter implements Meter { } private static class NoopLongHistogram implements ExtendedLongHistogram { + @Override + public boolean isEnabled() { + return false; + } + @Override public void record(long value, Attributes attributes, Context context) {} @@ -385,6 +415,11 @@ class ExtendedDefaultMeter implements Meter { } private static class NoopDoubleGauge implements ExtendedDoubleGauge { + @Override + public boolean isEnabled() { + return false; + } + @Override public void set(double value) {} @@ -426,6 +461,11 @@ class ExtendedDefaultMeter implements Meter { } private static class NoopLongGauge implements ExtendedLongGauge { + @Override + public boolean isEnabled() { + return false; + } + @Override public void set(long value) {} diff --git a/api/incubator/src/main/java/io/opentelemetry/api/incubator/trace/ExtendedDefaultTracer.java b/api/incubator/src/main/java/io/opentelemetry/api/incubator/trace/ExtendedDefaultTracer.java index 45346099a5..fe35d64666 100644 --- a/api/incubator/src/main/java/io/opentelemetry/api/incubator/trace/ExtendedDefaultTracer.java +++ b/api/incubator/src/main/java/io/opentelemetry/api/incubator/trace/ExtendedDefaultTracer.java @@ -31,6 +31,11 @@ final class ExtendedDefaultTracer implements ExtendedTracer { return INSTANCE; } + @Override + public boolean isEnabled() { + return false; + } + @Override public ExtendedSpanBuilder spanBuilder(String spanName) { return NoopSpanBuilder.create(); diff --git a/api/incubator/src/test/java/io/opentelemetry/api/incubator/config/InstrumentationConfigUtilTest.java b/api/incubator/src/test/java/io/opentelemetry/api/incubator/config/InstrumentationConfigUtilTest.java index d31001397a..8cb76972e7 100644 --- a/api/incubator/src/test/java/io/opentelemetry/api/incubator/config/InstrumentationConfigUtilTest.java +++ b/api/incubator/src/test/java/io/opentelemetry/api/incubator/config/InstrumentationConfigUtilTest.java @@ -150,7 +150,8 @@ class InstrumentationConfigUtilTest { .isInstanceOfSatisfying( YamlDeclarativeConfigProperties.class, exampleConfig -> - assertThat(exampleConfig.toMap()).isEqualTo(ImmutableMap.of("property", "value"))); + assertThat(DeclarativeConfigProperties.toMap(exampleConfig)) + .isEqualTo(ImmutableMap.of("property", "value"))); assertThat( InstrumentationConfigUtil.javaInstrumentationConfig(kitchenSinkConfigProvider, "foo")) .isNull(); diff --git a/api/incubator/src/test/java/io/opentelemetry/api/incubator/logs/ExtendedDefaultLoggerTest.java b/api/incubator/src/test/java/io/opentelemetry/api/incubator/logs/ExtendedDefaultLoggerTest.java index e92a3c160a..ef20161077 100644 --- a/api/incubator/src/test/java/io/opentelemetry/api/incubator/logs/ExtendedDefaultLoggerTest.java +++ b/api/incubator/src/test/java/io/opentelemetry/api/incubator/logs/ExtendedDefaultLoggerTest.java @@ -10,7 +10,9 @@ import static org.assertj.core.api.Assertions.assertThat; import io.opentelemetry.api.common.Value; import io.opentelemetry.api.logs.Logger; import io.opentelemetry.api.logs.LoggerProvider; +import io.opentelemetry.api.logs.Severity; import io.opentelemetry.api.testing.internal.AbstractDefaultLoggerTest; +import io.opentelemetry.context.Context; import org.junit.jupiter.api.Test; class ExtendedDefaultLoggerTest extends AbstractDefaultLoggerTest { @@ -26,10 +28,18 @@ class ExtendedDefaultLoggerTest extends AbstractDefaultLoggerTest { } @Test + @SuppressWarnings("deprecation") // testing deprecated code void incubatingApiIsLoaded() { Logger logger = LoggerProvider.noop().get("test"); - assertThat(logger).isInstanceOf(ExtendedLogger.class); + assertThat(logger) + .isInstanceOfSatisfying( + ExtendedLogger.class, + extendedLogger -> { + assertThat(extendedLogger.isEnabled(Severity.ERROR, Context.current())).isFalse(); + assertThat(extendedLogger.isEnabled(Severity.ERROR)).isFalse(); + assertThat(extendedLogger.isEnabled()).isFalse(); + }); ExtendedLogRecordBuilder builder = (ExtendedLogRecordBuilder) logger.logRecordBuilder(); assertThat(builder).isInstanceOf(ExtendedLogRecordBuilder.class); assertThat(builder.setBody(Value.of(0))).isSameAs(builder); diff --git a/api/incubator/src/test/java/io/opentelemetry/api/incubator/logs/ExtendedLogsBridgeApiUsageTest.java b/api/incubator/src/test/java/io/opentelemetry/api/incubator/logs/ExtendedLogsBridgeApiUsageTest.java index f7fdb51128..05c3e65f85 100644 --- a/api/incubator/src/test/java/io/opentelemetry/api/incubator/logs/ExtendedLogsBridgeApiUsageTest.java +++ b/api/incubator/src/test/java/io/opentelemetry/api/incubator/logs/ExtendedLogsBridgeApiUsageTest.java @@ -14,6 +14,7 @@ import io.opentelemetry.api.common.Attributes; import io.opentelemetry.api.incubator.common.ExtendedAttributeKey; import io.opentelemetry.api.incubator.common.ExtendedAttributes; import io.opentelemetry.api.logs.Logger; +import io.opentelemetry.api.logs.Severity; import io.opentelemetry.internal.testing.slf4j.SuppressLogger; import io.opentelemetry.sdk.logs.SdkLoggerProvider; import io.opentelemetry.sdk.logs.SdkLoggerProviderBuilder; @@ -53,24 +54,26 @@ class ExtendedLogsBridgeApiUsageTest { ExtendedLogger loggerB = (ExtendedLogger) loggerProvider.get("loggerB"); // Check if logger is enabled before emitting log and avoid unnecessary computation - if (loggerA.isEnabled()) { + if (loggerA.isEnabled(Severity.INFO)) { loggerA .logRecordBuilder() + .setSeverity(Severity.INFO) .setBody("hello world!") .setAllAttributes(Attributes.builder().put("result", flipCoin()).build()) .emit(); } - if (loggerB.isEnabled()) { + if (loggerB.isEnabled(Severity.INFO)) { loggerB .logRecordBuilder() + .setSeverity(Severity.INFO) .setBody("hello world!") .setAllAttributes(Attributes.builder().put("result", flipCoin()).build()) .emit(); } // loggerA is enabled, loggerB is disabled - assertThat(loggerA.isEnabled()).isTrue(); - assertThat(loggerB.isEnabled()).isFalse(); + assertThat(loggerA.isEnabled(Severity.INFO)).isTrue(); + assertThat(loggerB.isEnabled(Severity.INFO)).isFalse(); // Collected data only consists of logs from loggerA. Note, loggerB's logs would be // omitted from the results even if logs were emitted. The check if enabled simply avoids diff --git a/api/incubator/src/test/java/io/opentelemetry/api/incubator/metrics/ExtendedDefaultMeterTest.java b/api/incubator/src/test/java/io/opentelemetry/api/incubator/metrics/ExtendedDefaultMeterTest.java index a56a8740b0..f864c5d44a 100644 --- a/api/incubator/src/test/java/io/opentelemetry/api/incubator/metrics/ExtendedDefaultMeterTest.java +++ b/api/incubator/src/test/java/io/opentelemetry/api/incubator/metrics/ExtendedDefaultMeterTest.java @@ -11,7 +11,6 @@ import io.opentelemetry.api.OpenTelemetry; import io.opentelemetry.api.metrics.Meter; import io.opentelemetry.api.metrics.MeterProvider; import io.opentelemetry.api.testing.internal.AbstractDefaultMeterTest; -import org.assertj.core.api.Assertions; import org.junit.jupiter.api.Test; class ExtendedDefaultMeterTest extends AbstractDefaultMeterTest { @@ -27,44 +26,53 @@ class ExtendedDefaultMeterTest extends AbstractDefaultMeterTest { } @Test - public void incubatingApiIsLoaded() { + void incubatingApiIsLoaded() { Meter meter = MeterProvider.noop().get("test"); assertThat(meter).isSameAs(OpenTelemetry.noop().getMeter("test")); - Assertions.assertThat(meter.gaugeBuilder("test").ofLongs()) - .isInstanceOf(ExtendedLongGaugeBuilder.class); - Assertions.assertThat(meter.gaugeBuilder("test").ofLongs().build()) - .isInstanceOf(ExtendedLongGauge.class); - Assertions.assertThat(meter.gaugeBuilder("test")) - .isInstanceOf(ExtendedDoubleGaugeBuilder.class); - Assertions.assertThat(meter.gaugeBuilder("test").build()) - .isInstanceOf(ExtendedDoubleGauge.class); + assertThat(meter.gaugeBuilder("test").ofLongs()).isInstanceOf(ExtendedLongGaugeBuilder.class); + assertThat(meter.gaugeBuilder("test").ofLongs().build()) + .isInstanceOfSatisfying( + ExtendedLongGauge.class, instrument -> assertThat(instrument.isEnabled()).isFalse()); + assertThat(meter.gaugeBuilder("test")).isInstanceOf(ExtendedDoubleGaugeBuilder.class); + assertThat(meter.gaugeBuilder("test").build()) + .isInstanceOfSatisfying( + ExtendedDoubleGauge.class, instrument -> assertThat(instrument.isEnabled()).isFalse()); - Assertions.assertThat(meter.histogramBuilder("test").ofLongs()) + assertThat(meter.histogramBuilder("test").ofLongs()) .isInstanceOf(ExtendedLongHistogramBuilder.class); - Assertions.assertThat(meter.histogramBuilder("test").ofLongs().build()) - .isInstanceOf(ExtendedLongHistogram.class); - Assertions.assertThat(meter.histogramBuilder("test")) - .isInstanceOf(ExtendedDoubleHistogramBuilder.class); - Assertions.assertThat(meter.histogramBuilder("test").build()) - .isInstanceOf(ExtendedDoubleHistogram.class); + assertThat(meter.histogramBuilder("test").ofLongs().build()) + .isInstanceOfSatisfying( + ExtendedLongHistogram.class, + instrument -> assertThat(instrument.isEnabled()).isFalse()); + assertThat(meter.histogramBuilder("test")).isInstanceOf(ExtendedDoubleHistogramBuilder.class); + assertThat(meter.histogramBuilder("test").build()) + .isInstanceOfSatisfying( + ExtendedDoubleHistogram.class, + instrument -> assertThat(instrument.isEnabled()).isFalse()); - Assertions.assertThat(meter.counterBuilder("test")) - .isInstanceOf(ExtendedLongCounterBuilder.class); - Assertions.assertThat(meter.counterBuilder("test").build()) - .isInstanceOf(ExtendedLongCounter.class); - Assertions.assertThat(meter.counterBuilder("test").ofDoubles()) + assertThat(meter.counterBuilder("test")).isInstanceOf(ExtendedLongCounterBuilder.class); + assertThat(meter.counterBuilder("test").build()) + .isInstanceOfSatisfying( + ExtendedLongCounter.class, instrument -> assertThat(instrument.isEnabled()).isFalse()); + assertThat(meter.counterBuilder("test").ofDoubles()) .isInstanceOf(ExtendedDoubleCounterBuilder.class); - Assertions.assertThat(meter.counterBuilder("test").ofDoubles().build()) - .isInstanceOf(ExtendedDoubleCounter.class); + assertThat(meter.counterBuilder("test").ofDoubles().build()) + .isInstanceOfSatisfying( + ExtendedDoubleCounter.class, + instrument -> assertThat(instrument.isEnabled()).isFalse()); - Assertions.assertThat(meter.upDownCounterBuilder("test")) + assertThat(meter.upDownCounterBuilder("test")) .isInstanceOf(ExtendedLongUpDownCounterBuilder.class); - Assertions.assertThat(meter.upDownCounterBuilder("test").build()) - .isInstanceOf(ExtendedLongUpDownCounter.class); - Assertions.assertThat(meter.upDownCounterBuilder("test").ofDoubles()) + assertThat(meter.upDownCounterBuilder("test").build()) + .isInstanceOfSatisfying( + ExtendedLongUpDownCounter.class, + instrument -> assertThat(instrument.isEnabled()).isFalse()); + assertThat(meter.upDownCounterBuilder("test").ofDoubles()) .isInstanceOf(ExtendedDoubleUpDownCounterBuilder.class); - Assertions.assertThat(meter.upDownCounterBuilder("test").ofDoubles().build()) - .isInstanceOf(ExtendedDoubleUpDownCounter.class); + assertThat(meter.upDownCounterBuilder("test").ofDoubles().build()) + .isInstanceOfSatisfying( + ExtendedDoubleUpDownCounter.class, + instrument -> assertThat(instrument.isEnabled()).isFalse()); } } diff --git a/api/incubator/src/test/java/io/opentelemetry/api/incubator/trace/ExtendedDefaultTracerTest.java b/api/incubator/src/test/java/io/opentelemetry/api/incubator/trace/ExtendedDefaultTracerTest.java index 791c6b7d47..f13d072e43 100644 --- a/api/incubator/src/test/java/io/opentelemetry/api/incubator/trace/ExtendedDefaultTracerTest.java +++ b/api/incubator/src/test/java/io/opentelemetry/api/incubator/trace/ExtendedDefaultTracerTest.java @@ -6,6 +6,8 @@ package io.opentelemetry.api.incubator.trace; import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.reset; +import static org.mockito.Mockito.verify; import io.opentelemetry.api.OpenTelemetry; import io.opentelemetry.api.testing.internal.AbstractDefaultTracerTest; @@ -27,17 +29,20 @@ class ExtendedDefaultTracerTest extends AbstractDefaultTracerTest { } @Test - public void incubatingApiIsLoaded() { + void incubatingApiIsLoaded() { Tracer tracer = TracerProvider.noop().get("test"); assertThat(tracer).isSameAs(OpenTelemetry.noop().getTracer("test")); - assertThat(tracer).isInstanceOf(ExtendedTracer.class); + assertThat(tracer) + .isInstanceOfSatisfying( + ExtendedTracer.class, + extendedTracer -> assertThat(extendedTracer.isEnabled()).isFalse()); assertThat(tracer.spanBuilder("test")).isInstanceOf(ExtendedSpanBuilder.class); } @SuppressWarnings("unchecked") @Test - public void incubatingApi() { + void incubatingApi() { ExtendedSpanBuilder spanBuilder = (ExtendedSpanBuilder) ExtendedDefaultTracer.getNoop().spanBuilder("test"); assertThat(spanBuilder.setParentFrom(null, null)).isSameAs(spanBuilder); @@ -45,21 +50,21 @@ class ExtendedDefaultTracerTest extends AbstractDefaultTracerTest { SpanRunnable spanRunnable = Mockito.mock(SpanRunnable.class); spanBuilder.startAndRun(spanRunnable); - Mockito.verify(spanRunnable).runInSpan(); - Mockito.reset(spanRunnable); + verify(spanRunnable).runInSpan(); + reset(spanRunnable); spanBuilder.startAndRun(spanRunnable, null); - Mockito.verify(spanRunnable).runInSpan(); - Mockito.reset(spanRunnable); + verify(spanRunnable).runInSpan(); + reset(spanRunnable); SpanCallable spanCallable = Mockito.mock(SpanCallable.class); spanBuilder.startAndCall(spanCallable); - Mockito.verify(spanCallable).callInSpan(); - Mockito.reset(spanCallable); + verify(spanCallable).callInSpan(); + reset(spanCallable); spanBuilder.startAndCall(spanCallable, null); - Mockito.verify(spanCallable).callInSpan(); - Mockito.reset(spanCallable); + verify(spanCallable).callInSpan(); + reset(spanCallable); } } diff --git a/api/incubator/src/testConvertToModel/java/io/opentelemetry/api/incubator/InstrumentationConfigUtilTest.java b/api/incubator/src/testConvertToModel/java/io/opentelemetry/api/incubator/InstrumentationConfigUtilTest.java new file mode 100644 index 0000000000..ef936a5fd5 --- /dev/null +++ b/api/incubator/src/testConvertToModel/java/io/opentelemetry/api/incubator/InstrumentationConfigUtilTest.java @@ -0,0 +1,259 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.api.incubator; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.common.collect.ImmutableMap; +import io.opentelemetry.api.incubator.config.ConfigProvider; +import io.opentelemetry.api.incubator.config.DeclarativeConfigProperties; +import io.opentelemetry.api.incubator.config.InstrumentationConfigUtil; +import io.opentelemetry.sdk.extension.incubator.fileconfig.DeclarativeConfiguration; +import io.opentelemetry.sdk.extension.incubator.fileconfig.SdkConfigProvider; +import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.ExperimentalLanguageSpecificInstrumentationModel; +import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.InstrumentationModel; +import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.OpenTelemetryConfigurationModel; +import java.io.ByteArrayInputStream; +import java.nio.charset.StandardCharsets; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import org.junit.jupiter.api.Test; + +class InstrumentationConfigUtilTest { + + private static final ObjectMapper MAPPER = new ObjectMapper(); + + @Test + void toMap_RoundTrip() throws JsonProcessingException { + Map map = new HashMap<>(); + map.put("string", "val"); + map.put("boolean", true); + map.put("long", 1L); + map.put("double", 1.1); + map.put("null", null); + map.put("stringList", Arrays.asList("val1", "val2")); + map.put("boolList", Arrays.asList(true, false)); + map.put("longList", Arrays.asList(1L, 2L)); + map.put("doubleList", Arrays.asList(1.1d, 2.2d)); + map.put( + "structuredList", Collections.singletonList(Collections.singletonMap("childKey", "val"))); + map.put("emptyList", Collections.emptyList()); + map.put("structured", Collections.singletonMap("childKey", "val")); + map.put("emptyStructured", Collections.emptyMap()); + + String mapJson = MAPPER.writeValueAsString(map); + DeclarativeConfigProperties properties = + DeclarativeConfiguration.toConfigProperties( + new ByteArrayInputStream(mapJson.getBytes(StandardCharsets.UTF_8))); + + assertThat(DeclarativeConfigProperties.toMap(properties)).isEqualTo(map); + } + + @Test + void getInstrumentationConfigModel_UnsetConfig() { + ConfigProvider configProvider = () -> null; + + assertThat( + InstrumentationConfigUtil.getInstrumentationConfigModel( + configProvider, "my_instrumentation_library", MAPPER, Model.class)) + .isEqualTo(null); + } + + @Test + void getInstrumentationConfigModel_EmptyConfig() { + ConfigProvider configProvider = + withInstrumentationConfig("my_instrumentation_library", Collections.emptyMap()); + + assertThat( + InstrumentationConfigUtil.getInstrumentationConfigModel( + configProvider, "my_instrumentation_library", MAPPER, Model.class)) + .isEqualTo(new Model()); + } + + @Test + void getInstrumentationConfigModel_KitchenSink() { + ConfigProvider configProvider = + withInstrumentationConfig( + "my_instrumentation_library", + ImmutableMap.builder() + .put("string_property", "value") + .put("boolean_property", true) + .put("long_property", 1L) + .put("double_property", 1.1d) + .put("string_list_property", Arrays.asList("val1", "val2")) + .put("boolean_list_property", Arrays.asList(true, false)) + .put("long_list_property", Arrays.asList(1L, 2L)) + .put("double_list_property", Arrays.asList(1.1d, 2.2d)) + .put("map_property", Collections.singletonMap("childKey", "val")) + .put( + "structured_list_property", + Collections.singletonList( + ImmutableMap.of("key", "the_key", "value", "the_value"))) + .build()); + + Model expected = new Model(); + expected.stringProperty = "value"; + expected.booleanProperty = true; + expected.longProperty = 1L; + expected.doubleProperty = 1.1d; + expected.stringListProperty = Arrays.asList("val1", "val2"); + expected.booleanListProperty = Arrays.asList(true, false); + expected.longListProperty = Arrays.asList(1L, 2L); + expected.doubleListProperty = Arrays.asList(1.1d, 2.2d); + expected.mapProperty = Collections.singletonMap("childKey", "val"); + ListEntryModel listEntryModel = new ListEntryModel(); + listEntryModel.key = "the_key"; + listEntryModel.value = "the_value"; + expected.structuredListProperty = Collections.singletonList(listEntryModel); + + assertThat( + InstrumentationConfigUtil.getInstrumentationConfigModel( + configProvider, "my_instrumentation_library", MAPPER, Model.class)) + .isEqualTo(expected); + } + + private static ConfigProvider withInstrumentationConfig( + String instrumentationName, Map instrumentationConfig) { + ExperimentalLanguageSpecificInstrumentationModel javaConfig = + new ExperimentalLanguageSpecificInstrumentationModel(); + javaConfig.setAdditionalProperty(instrumentationName, instrumentationConfig); + + return SdkConfigProvider.create( + new OpenTelemetryConfigurationModel() + .withInstrumentationDevelopment(new InstrumentationModel().withJava(javaConfig))); + } + + private static class Model { + @JsonProperty("string_property") + private String stringProperty; + + @JsonProperty("boolean_property") + private Boolean booleanProperty; + + @JsonProperty("long_property") + private Long longProperty; + + @JsonProperty("double_property") + private Double doubleProperty; + + @JsonProperty("string_list_property") + private List stringListProperty; + + @JsonProperty("boolean_list_property") + private List booleanListProperty; + + @JsonProperty("long_list_property") + private List longListProperty; + + @JsonProperty("double_list_property") + private List doubleListProperty; + + ; + + @JsonProperty("map_property") + private Map mapProperty; + + @JsonProperty("structured_list_property") + private List structuredListProperty; + + @Override + public boolean equals(Object o) { + if (!(o instanceof Model)) { + return false; + } + Model model = (Model) o; + return Objects.equals(stringProperty, model.stringProperty) + && Objects.equals(booleanProperty, model.booleanProperty) + && Objects.equals(longProperty, model.longProperty) + && Objects.equals(doubleProperty, model.doubleProperty) + && Objects.equals(stringListProperty, model.stringListProperty) + && Objects.equals(booleanListProperty, model.booleanListProperty) + && Objects.equals(longListProperty, model.longListProperty) + && Objects.equals(doubleListProperty, model.doubleListProperty) + && Objects.equals(mapProperty, model.mapProperty) + && Objects.equals(structuredListProperty, model.structuredListProperty); + } + + @Override + public int hashCode() { + return Objects.hash( + stringProperty, + booleanProperty, + longProperty, + doubleProperty, + stringListProperty, + booleanListProperty, + longListProperty, + doubleListProperty, + mapProperty, + structuredListProperty); + } + + @Override + public String toString() { + return "Model{" + + "stringProperty='" + + stringProperty + + '\'' + + ", booleanProperty='" + + booleanProperty + + '\'' + + ", longProperty='" + + longProperty + + '\'' + + ", doubleProperty='" + + doubleProperty + + '\'' + + ", stringListProperty=" + + stringListProperty + + ", booleanListProperty=" + + booleanListProperty + + ", longListProperty=" + + longListProperty + + ", doubleListProperty=" + + doubleListProperty + + ", mapProperty=" + + mapProperty + + ", structuredListProperty=" + + structuredListProperty + + '}'; + } + } + + private static final class ListEntryModel { + @JsonProperty("key") + private String key; + + @JsonProperty("value") + private String value; + + @Override + public boolean equals(Object o) { + if (o == null || getClass() != o.getClass()) { + return false; + } + ListEntryModel that = (ListEntryModel) o; + return Objects.equals(key, that.key) && Objects.equals(value, that.value); + } + + @Override + public int hashCode() { + return Objects.hash(key, value); + } + + @Override + public String toString() { + return "ListEntryModel{" + "key='" + key + '\'' + ", value='" + value + '\'' + '}'; + } + } +} diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts index 2f8e81d1f1..d81397ebeb 100644 --- a/buildSrc/build.gradle.kts +++ b/buildSrc/build.gradle.kts @@ -2,7 +2,7 @@ plugins { `kotlin-dsl` // When updating, update below in dependencies too - id("com.diffplug.spotless") version "7.0.4" + id("com.diffplug.spotless") version "7.1.0" } if (!hasLauncherForJavaVersion(17)) { @@ -50,10 +50,10 @@ repositories { } dependencies { - implementation(enforcedPlatform("com.squareup.wire:wire-bom:5.3.3")) + implementation(enforcedPlatform("com.squareup.wire:wire-bom:5.3.5")) implementation("com.google.auto.value:auto-value-annotations:1.11.0") // When updating, update above in plugins too - implementation("com.diffplug.spotless:spotless-plugin-gradle:7.0.4") + implementation("com.diffplug.spotless:spotless-plugin-gradle:7.1.0") implementation("com.gradle.develocity:com.gradle.develocity.gradle.plugin:4.0.2") implementation("com.squareup:javapoet:1.13.0") implementation("com.squareup.wire:wire-compiler") diff --git a/buildSrc/src/main/kotlin/otel.java-conventions.gradle.kts b/buildSrc/src/main/kotlin/otel.java-conventions.gradle.kts index 54b6269d81..55998eb4f3 100644 --- a/buildSrc/src/main/kotlin/otel.java-conventions.gradle.kts +++ b/buildSrc/src/main/kotlin/otel.java-conventions.gradle.kts @@ -42,7 +42,7 @@ java { checkstyle { configDirectory.set(file("$rootDir/buildscripts/")) - toolVersion = "10.26.0" + toolVersion = "10.26.1" isIgnoreFailures = false configProperties["rootDir"] = rootDir } diff --git a/common/build.gradle.kts b/common/build.gradle.kts new file mode 100644 index 0000000000..ffeef6877f --- /dev/null +++ b/common/build.gradle.kts @@ -0,0 +1,12 @@ +plugins { + id("otel.java-conventions") + id("otel.publish-conventions") + + id("otel.animalsniffer-conventions") +} + +description = "OpenTelemetry API Common" +otelJava.moduleName.set("io.opentelemetry.common") + +dependencies { +} diff --git a/common/src/main/java/io/opentelemetry/common/ComponentLoader.java b/common/src/main/java/io/opentelemetry/common/ComponentLoader.java new file mode 100644 index 0000000000..17dab36a1b --- /dev/null +++ b/common/src/main/java/io/opentelemetry/common/ComponentLoader.java @@ -0,0 +1,28 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.common; + +import java.util.ServiceLoader; + +/** A loader for components that are discovered via SPI. */ +public interface ComponentLoader { + /** + * Load implementations of an SPI. + * + * @param spiClass the SPI class + * @param the SPI type + * @return iterable of SPI implementations + */ + Iterable load(Class spiClass); + + /** + * Create an instance for the {@code classLoader} using {@link ServiceLoader#load(Class, + * ClassLoader)}. + */ + static ComponentLoader forClassLoader(ClassLoader classLoader) { + return new ServiceLoaderComponentLoader(classLoader); + } +} diff --git a/common/src/main/java/io/opentelemetry/common/ServiceLoaderComponentLoader.java b/common/src/main/java/io/opentelemetry/common/ServiceLoaderComponentLoader.java new file mode 100644 index 0000000000..5f2d0c58cd --- /dev/null +++ b/common/src/main/java/io/opentelemetry/common/ServiceLoaderComponentLoader.java @@ -0,0 +1,27 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.common; + +import java.util.ServiceLoader; + +class ServiceLoaderComponentLoader implements ComponentLoader { + + private final ClassLoader classLoader; + + ServiceLoaderComponentLoader(ClassLoader classLoader) { + this.classLoader = classLoader; + } + + @Override + public Iterable load(Class spiClass) { + return ServiceLoader.load(spiClass, classLoader); + } + + @Override + public String toString() { + return "ServiceLoaderComponentLoader{classLoader=" + classLoader + "}"; + } +} diff --git a/context/build.gradle.kts b/context/build.gradle.kts index 6c8fa63837..b3a3562051 100644 --- a/context/build.gradle.kts +++ b/context/build.gradle.kts @@ -10,6 +10,7 @@ description = "OpenTelemetry Context (Incubator)" otelJava.moduleName.set("io.opentelemetry.context") dependencies { + api(project(":common")) // MustBeClosed compileOnly("com.google.errorprone:error_prone_annotations") diff --git a/context/src/main/java/io/opentelemetry/context/LazyStorage.java b/context/src/main/java/io/opentelemetry/context/LazyStorage.java index 5cce59d253..428b87dc48 100644 --- a/context/src/main/java/io/opentelemetry/context/LazyStorage.java +++ b/context/src/main/java/io/opentelemetry/context/LazyStorage.java @@ -37,9 +37,9 @@ package io.opentelemetry.context; +import io.opentelemetry.common.ComponentLoader; import java.util.ArrayList; import java.util.List; -import java.util.ServiceLoader; import java.util.concurrent.atomic.AtomicReference; import java.util.function.Function; import java.util.logging.Level; @@ -103,9 +103,10 @@ final class LazyStorage { return ContextStorage.defaultStorage(); } + ComponentLoader componentLoader = + ComponentLoader.forClassLoader(LazyStorage.class.getClassLoader()); List providers = new ArrayList<>(); - for (ContextStorageProvider provider : - ServiceLoader.load(ContextStorageProvider.class, LazyStorage.class.getClassLoader())) { + for (ContextStorageProvider provider : componentLoader.load(ContextStorageProvider.class)) { if (provider .getClass() .getName() diff --git a/dependencyManagement/build.gradle.kts b/dependencyManagement/build.gradle.kts index b01c72f28a..bd1ba29e68 100644 --- a/dependencyManagement/build.gradle.kts +++ b/dependencyManagement/build.gradle.kts @@ -16,14 +16,14 @@ val DEPENDENCY_BOMS = listOf( "com.fasterxml.jackson:jackson-bom:2.19.1", "com.google.guava:guava-bom:33.4.8-jre", "com.google.protobuf:protobuf-bom:4.31.1", - "com.squareup.okhttp3:okhttp-bom:4.12.0", - "com.squareup.okio:okio-bom:3.13.0", // applies to transitive dependencies of okhttp + "com.squareup.okhttp3:okhttp-bom:5.1.0", + "com.squareup.okio:okio-bom:3.15.0", // applies to transitive dependencies of okhttp "io.grpc:grpc-bom:1.73.0", "io.netty:netty-bom:4.2.2.Final", "io.zipkin.brave:brave-bom:6.3.0", "io.zipkin.reporter2:zipkin-reporter-bom:3.5.1", "org.assertj:assertj-bom:3.27.3", - "org.testcontainers:testcontainers-bom:1.21.2", + "org.testcontainers:testcontainers-bom:1.21.3", "org.snakeyaml:snakeyaml-engine:2.9" ) @@ -68,7 +68,7 @@ val DEPENDENCIES = listOf( "io.prometheus:prometheus-metrics-exposition-formats-no-protobuf:${prometheusServerVersion}", "javax.annotation:javax.annotation-api:1.3.2", "com.github.stefanbirkner:system-rules:1.19.0", - "com.google.api.grpc:proto-google-common-protos:2.58.2", + "com.google.api.grpc:proto-google-common-protos:2.59.1", "com.google.code.findbugs:jsr305:3.0.2", "com.google.guava:guava-beta-checker:1.0", "com.sun.net.httpserver:http:20070405", @@ -78,7 +78,7 @@ val DEPENDENCIES = listOf( "eu.rekawek.toxiproxy:toxiproxy-java:2.1.7", "io.github.netmikey.logunit:logunit-jul:2.0.0", "io.jaegertracing:jaeger-client:1.8.1", - "io.opentelemetry.contrib:opentelemetry-aws-xray-propagator:1.46.0-alpha", + "io.opentelemetry.contrib:opentelemetry-aws-xray-propagator:1.47.0-alpha", "io.opentelemetry.semconv:opentelemetry-semconv-incubating:1.34.0-alpha", "io.opentelemetry.proto:opentelemetry-proto:1.7.0-alpha", "io.opentracing:opentracing-api:0.33.0", diff --git a/docs/apidiffs/1.52.0_vs_1.51.0/opentelemetry-api.txt b/docs/apidiffs/1.52.0_vs_1.51.0/opentelemetry-api.txt new file mode 100644 index 0000000000..ed3e1e076a --- /dev/null +++ b/docs/apidiffs/1.52.0_vs_1.51.0/opentelemetry-api.txt @@ -0,0 +1,4 @@ +Comparing source compatibility of opentelemetry-api-1.52.0.jar against opentelemetry-api-1.51.0.jar +*** MODIFIED CLASS: PUBLIC FINAL io.opentelemetry.api.GlobalOpenTelemetry (not serializable) + === CLASS FILE FORMAT VERSION: 52.0 <- 52.0 + +++ NEW METHOD: PUBLIC(+) STATIC(+) void set(java.util.function.Supplier) diff --git a/docs/apidiffs/1.52.0_vs_1.51.0/opentelemetry-common.txt b/docs/apidiffs/1.52.0_vs_1.51.0/opentelemetry-common.txt new file mode 100644 index 0000000000..439922d61d --- /dev/null +++ b/docs/apidiffs/1.52.0_vs_1.51.0/opentelemetry-common.txt @@ -0,0 +1,7 @@ +Comparing source compatibility of opentelemetry-common-1.52.0.jar against ++++ NEW INTERFACE: PUBLIC(+) ABSTRACT(+) io.opentelemetry.common.ComponentLoader (not serializable) + +++ CLASS FILE FORMAT VERSION: 52.0 <- n.a. + +++ NEW SUPERCLASS: java.lang.Object + +++ NEW METHOD: PUBLIC(+) STATIC(+) io.opentelemetry.common.ComponentLoader forClassLoader(java.lang.ClassLoader) + +++ NEW METHOD: PUBLIC(+) ABSTRACT(+) java.lang.Iterable load(java.lang.Class) + GENERIC TEMPLATES: +++ T:java.lang.Object diff --git a/docs/apidiffs/1.52.0_vs_1.51.0/opentelemetry-context.txt b/docs/apidiffs/1.52.0_vs_1.51.0/opentelemetry-context.txt new file mode 100644 index 0000000000..f628e7c6de --- /dev/null +++ b/docs/apidiffs/1.52.0_vs_1.51.0/opentelemetry-context.txt @@ -0,0 +1,2 @@ +Comparing source compatibility of opentelemetry-context-1.52.0.jar against opentelemetry-context-1.51.0.jar +No changes. \ No newline at end of file diff --git a/docs/apidiffs/1.52.0_vs_1.51.0/opentelemetry-exporter-common.txt b/docs/apidiffs/1.52.0_vs_1.51.0/opentelemetry-exporter-common.txt new file mode 100644 index 0000000000..7ce59a2590 --- /dev/null +++ b/docs/apidiffs/1.52.0_vs_1.51.0/opentelemetry-exporter-common.txt @@ -0,0 +1,2 @@ +Comparing source compatibility of opentelemetry-exporter-common-1.52.0.jar against opentelemetry-exporter-common-1.51.0.jar +No changes. \ No newline at end of file diff --git a/docs/apidiffs/1.52.0_vs_1.51.0/opentelemetry-exporter-logging-otlp.txt b/docs/apidiffs/1.52.0_vs_1.51.0/opentelemetry-exporter-logging-otlp.txt new file mode 100644 index 0000000000..81183679a7 --- /dev/null +++ b/docs/apidiffs/1.52.0_vs_1.51.0/opentelemetry-exporter-logging-otlp.txt @@ -0,0 +1,2 @@ +Comparing source compatibility of opentelemetry-exporter-logging-otlp-1.52.0.jar against opentelemetry-exporter-logging-otlp-1.51.0.jar +No changes. \ No newline at end of file diff --git a/docs/apidiffs/1.52.0_vs_1.51.0/opentelemetry-exporter-logging.txt b/docs/apidiffs/1.52.0_vs_1.51.0/opentelemetry-exporter-logging.txt new file mode 100644 index 0000000000..3ce4ed1852 --- /dev/null +++ b/docs/apidiffs/1.52.0_vs_1.51.0/opentelemetry-exporter-logging.txt @@ -0,0 +1,2 @@ +Comparing source compatibility of opentelemetry-exporter-logging-1.52.0.jar against opentelemetry-exporter-logging-1.51.0.jar +No changes. \ No newline at end of file diff --git a/docs/apidiffs/1.52.0_vs_1.51.0/opentelemetry-exporter-otlp-common.txt b/docs/apidiffs/1.52.0_vs_1.51.0/opentelemetry-exporter-otlp-common.txt new file mode 100644 index 0000000000..12f0ce982e --- /dev/null +++ b/docs/apidiffs/1.52.0_vs_1.51.0/opentelemetry-exporter-otlp-common.txt @@ -0,0 +1,2 @@ +Comparing source compatibility of opentelemetry-exporter-otlp-common-1.52.0.jar against opentelemetry-exporter-otlp-common-1.51.0.jar +No changes. \ No newline at end of file diff --git a/docs/apidiffs/1.52.0_vs_1.51.0/opentelemetry-exporter-otlp.txt b/docs/apidiffs/1.52.0_vs_1.51.0/opentelemetry-exporter-otlp.txt new file mode 100644 index 0000000000..d9395d450c --- /dev/null +++ b/docs/apidiffs/1.52.0_vs_1.51.0/opentelemetry-exporter-otlp.txt @@ -0,0 +1,19 @@ +Comparing source compatibility of opentelemetry-exporter-otlp-1.52.0.jar against opentelemetry-exporter-otlp-1.51.0.jar +*** MODIFIED CLASS: PUBLIC FINAL io.opentelemetry.exporter.otlp.http.logs.OtlpHttpLogRecordExporterBuilder (not serializable) + === CLASS FILE FORMAT VERSION: 52.0 <- 52.0 + +++ NEW METHOD: PUBLIC(+) io.opentelemetry.exporter.otlp.http.logs.OtlpHttpLogRecordExporterBuilder setComponentLoader(io.opentelemetry.common.ComponentLoader) +*** MODIFIED CLASS: PUBLIC FINAL io.opentelemetry.exporter.otlp.http.metrics.OtlpHttpMetricExporterBuilder (not serializable) + === CLASS FILE FORMAT VERSION: 52.0 <- 52.0 + +++ NEW METHOD: PUBLIC(+) io.opentelemetry.exporter.otlp.http.metrics.OtlpHttpMetricExporterBuilder setComponentLoader(io.opentelemetry.common.ComponentLoader) +*** MODIFIED CLASS: PUBLIC FINAL io.opentelemetry.exporter.otlp.http.trace.OtlpHttpSpanExporterBuilder (not serializable) + === CLASS FILE FORMAT VERSION: 52.0 <- 52.0 + +++ NEW METHOD: PUBLIC(+) io.opentelemetry.exporter.otlp.http.trace.OtlpHttpSpanExporterBuilder setComponentLoader(io.opentelemetry.common.ComponentLoader) +*** MODIFIED CLASS: PUBLIC FINAL io.opentelemetry.exporter.otlp.logs.OtlpGrpcLogRecordExporterBuilder (not serializable) + === CLASS FILE FORMAT VERSION: 52.0 <- 52.0 + +++ NEW METHOD: PUBLIC(+) io.opentelemetry.exporter.otlp.logs.OtlpGrpcLogRecordExporterBuilder setComponentLoader(io.opentelemetry.common.ComponentLoader) +*** MODIFIED CLASS: PUBLIC FINAL io.opentelemetry.exporter.otlp.metrics.OtlpGrpcMetricExporterBuilder (not serializable) + === CLASS FILE FORMAT VERSION: 52.0 <- 52.0 + +++ NEW METHOD: PUBLIC(+) io.opentelemetry.exporter.otlp.metrics.OtlpGrpcMetricExporterBuilder setComponentLoader(io.opentelemetry.common.ComponentLoader) +*** MODIFIED CLASS: PUBLIC FINAL io.opentelemetry.exporter.otlp.trace.OtlpGrpcSpanExporterBuilder (not serializable) + === CLASS FILE FORMAT VERSION: 52.0 <- 52.0 + +++ NEW METHOD: PUBLIC(+) io.opentelemetry.exporter.otlp.trace.OtlpGrpcSpanExporterBuilder setComponentLoader(io.opentelemetry.common.ComponentLoader) diff --git a/docs/apidiffs/1.52.0_vs_1.51.0/opentelemetry-exporter-sender-grpc-managed-channel.txt b/docs/apidiffs/1.52.0_vs_1.51.0/opentelemetry-exporter-sender-grpc-managed-channel.txt new file mode 100644 index 0000000000..a4f7152db3 --- /dev/null +++ b/docs/apidiffs/1.52.0_vs_1.51.0/opentelemetry-exporter-sender-grpc-managed-channel.txt @@ -0,0 +1,2 @@ +Comparing source compatibility of opentelemetry-exporter-sender-grpc-managed-channel-1.52.0.jar against opentelemetry-exporter-sender-grpc-managed-channel-1.51.0.jar +No changes. \ No newline at end of file diff --git a/docs/apidiffs/1.52.0_vs_1.51.0/opentelemetry-exporter-sender-jdk.txt b/docs/apidiffs/1.52.0_vs_1.51.0/opentelemetry-exporter-sender-jdk.txt new file mode 100644 index 0000000000..4f97d2b4cd --- /dev/null +++ b/docs/apidiffs/1.52.0_vs_1.51.0/opentelemetry-exporter-sender-jdk.txt @@ -0,0 +1,2 @@ +Comparing source compatibility of opentelemetry-exporter-sender-jdk-1.52.0.jar against opentelemetry-exporter-sender-jdk-1.51.0.jar +No changes. \ No newline at end of file diff --git a/docs/apidiffs/1.52.0_vs_1.51.0/opentelemetry-exporter-sender-okhttp.txt b/docs/apidiffs/1.52.0_vs_1.51.0/opentelemetry-exporter-sender-okhttp.txt new file mode 100644 index 0000000000..ec31b89a9e --- /dev/null +++ b/docs/apidiffs/1.52.0_vs_1.51.0/opentelemetry-exporter-sender-okhttp.txt @@ -0,0 +1,2 @@ +Comparing source compatibility of opentelemetry-exporter-sender-okhttp-1.52.0.jar against opentelemetry-exporter-sender-okhttp-1.51.0.jar +No changes. \ No newline at end of file diff --git a/docs/apidiffs/1.52.0_vs_1.51.0/opentelemetry-exporter-zipkin.txt b/docs/apidiffs/1.52.0_vs_1.51.0/opentelemetry-exporter-zipkin.txt new file mode 100644 index 0000000000..08630dd1b7 --- /dev/null +++ b/docs/apidiffs/1.52.0_vs_1.51.0/opentelemetry-exporter-zipkin.txt @@ -0,0 +1,2 @@ +Comparing source compatibility of opentelemetry-exporter-zipkin-1.52.0.jar against opentelemetry-exporter-zipkin-1.51.0.jar +No changes. \ No newline at end of file diff --git a/docs/apidiffs/1.52.0_vs_1.51.0/opentelemetry-extension-kotlin.txt b/docs/apidiffs/1.52.0_vs_1.51.0/opentelemetry-extension-kotlin.txt new file mode 100644 index 0000000000..c7b73267ea --- /dev/null +++ b/docs/apidiffs/1.52.0_vs_1.51.0/opentelemetry-extension-kotlin.txt @@ -0,0 +1,2 @@ +Comparing source compatibility of opentelemetry-extension-kotlin-1.52.0.jar against opentelemetry-extension-kotlin-1.51.0.jar +No changes. \ No newline at end of file diff --git a/docs/apidiffs/1.52.0_vs_1.51.0/opentelemetry-extension-trace-propagators.txt b/docs/apidiffs/1.52.0_vs_1.51.0/opentelemetry-extension-trace-propagators.txt new file mode 100644 index 0000000000..ae33ad2808 --- /dev/null +++ b/docs/apidiffs/1.52.0_vs_1.51.0/opentelemetry-extension-trace-propagators.txt @@ -0,0 +1,2 @@ +Comparing source compatibility of opentelemetry-extension-trace-propagators-1.52.0.jar against opentelemetry-extension-trace-propagators-1.51.0.jar +No changes. \ No newline at end of file diff --git a/docs/apidiffs/1.52.0_vs_1.51.0/opentelemetry-opentracing-shim.txt b/docs/apidiffs/1.52.0_vs_1.51.0/opentelemetry-opentracing-shim.txt new file mode 100644 index 0000000000..872c028cb3 --- /dev/null +++ b/docs/apidiffs/1.52.0_vs_1.51.0/opentelemetry-opentracing-shim.txt @@ -0,0 +1,2 @@ +Comparing source compatibility of opentelemetry-opentracing-shim-1.52.0.jar against opentelemetry-opentracing-shim-1.51.0.jar +No changes. \ No newline at end of file diff --git a/docs/apidiffs/1.52.0_vs_1.51.0/opentelemetry-sdk-common.txt b/docs/apidiffs/1.52.0_vs_1.51.0/opentelemetry-sdk-common.txt new file mode 100644 index 0000000000..eb715b8fdd --- /dev/null +++ b/docs/apidiffs/1.52.0_vs_1.51.0/opentelemetry-sdk-common.txt @@ -0,0 +1,2 @@ +Comparing source compatibility of opentelemetry-sdk-common-1.52.0.jar against opentelemetry-sdk-common-1.51.0.jar +No changes. \ No newline at end of file diff --git a/docs/apidiffs/1.52.0_vs_1.51.0/opentelemetry-sdk-extension-autoconfigure-spi.txt b/docs/apidiffs/1.52.0_vs_1.51.0/opentelemetry-sdk-extension-autoconfigure-spi.txt new file mode 100644 index 0000000000..72353441e6 --- /dev/null +++ b/docs/apidiffs/1.52.0_vs_1.51.0/opentelemetry-sdk-extension-autoconfigure-spi.txt @@ -0,0 +1,4 @@ +Comparing source compatibility of opentelemetry-sdk-extension-autoconfigure-spi-1.52.0.jar against opentelemetry-sdk-extension-autoconfigure-spi-1.51.0.jar +*** MODIFIED INTERFACE: PUBLIC ABSTRACT io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties (not serializable) + === CLASS FILE FORMAT VERSION: 52.0 <- 52.0 + +++ NEW METHOD: PUBLIC(+) io.opentelemetry.common.ComponentLoader getComponentLoader() diff --git a/docs/apidiffs/1.52.0_vs_1.51.0/opentelemetry-sdk-extension-autoconfigure.txt b/docs/apidiffs/1.52.0_vs_1.51.0/opentelemetry-sdk-extension-autoconfigure.txt new file mode 100644 index 0000000000..0b818e8d0f --- /dev/null +++ b/docs/apidiffs/1.52.0_vs_1.51.0/opentelemetry-sdk-extension-autoconfigure.txt @@ -0,0 +1,4 @@ +Comparing source compatibility of opentelemetry-sdk-extension-autoconfigure-1.52.0.jar against opentelemetry-sdk-extension-autoconfigure-1.51.0.jar +*** MODIFIED CLASS: PUBLIC FINAL io.opentelemetry.sdk.autoconfigure.AutoConfiguredOpenTelemetrySdkBuilder (not serializable) + === CLASS FILE FORMAT VERSION: 52.0 <- 52.0 + +++ NEW METHOD: PUBLIC(+) io.opentelemetry.sdk.autoconfigure.AutoConfiguredOpenTelemetrySdkBuilder setComponentLoader(io.opentelemetry.common.ComponentLoader) diff --git a/docs/apidiffs/1.52.0_vs_1.51.0/opentelemetry-sdk-extension-jaeger-remote-sampler.txt b/docs/apidiffs/1.52.0_vs_1.51.0/opentelemetry-sdk-extension-jaeger-remote-sampler.txt new file mode 100644 index 0000000000..97b9523a61 --- /dev/null +++ b/docs/apidiffs/1.52.0_vs_1.51.0/opentelemetry-sdk-extension-jaeger-remote-sampler.txt @@ -0,0 +1,2 @@ +Comparing source compatibility of opentelemetry-sdk-extension-jaeger-remote-sampler-1.52.0.jar against opentelemetry-sdk-extension-jaeger-remote-sampler-1.51.0.jar +No changes. \ No newline at end of file diff --git a/docs/apidiffs/1.52.0_vs_1.51.0/opentelemetry-sdk-logs.txt b/docs/apidiffs/1.52.0_vs_1.51.0/opentelemetry-sdk-logs.txt new file mode 100644 index 0000000000..8cd9d9ff50 --- /dev/null +++ b/docs/apidiffs/1.52.0_vs_1.51.0/opentelemetry-sdk-logs.txt @@ -0,0 +1,2 @@ +Comparing source compatibility of opentelemetry-sdk-logs-1.52.0.jar against opentelemetry-sdk-logs-1.51.0.jar +No changes. \ No newline at end of file diff --git a/docs/apidiffs/1.52.0_vs_1.51.0/opentelemetry-sdk-metrics.txt b/docs/apidiffs/1.52.0_vs_1.51.0/opentelemetry-sdk-metrics.txt new file mode 100644 index 0000000000..f806e04900 --- /dev/null +++ b/docs/apidiffs/1.52.0_vs_1.51.0/opentelemetry-sdk-metrics.txt @@ -0,0 +1,2 @@ +Comparing source compatibility of opentelemetry-sdk-metrics-1.52.0.jar against opentelemetry-sdk-metrics-1.51.0.jar +No changes. \ No newline at end of file diff --git a/docs/apidiffs/1.52.0_vs_1.51.0/opentelemetry-sdk-testing.txt b/docs/apidiffs/1.52.0_vs_1.51.0/opentelemetry-sdk-testing.txt new file mode 100644 index 0000000000..bd6d78ab7d --- /dev/null +++ b/docs/apidiffs/1.52.0_vs_1.51.0/opentelemetry-sdk-testing.txt @@ -0,0 +1,2 @@ +Comparing source compatibility of opentelemetry-sdk-testing-1.52.0.jar against opentelemetry-sdk-testing-1.51.0.jar +No changes. \ No newline at end of file diff --git a/docs/apidiffs/1.52.0_vs_1.51.0/opentelemetry-sdk-trace.txt b/docs/apidiffs/1.52.0_vs_1.51.0/opentelemetry-sdk-trace.txt new file mode 100644 index 0000000000..7aea6b071c --- /dev/null +++ b/docs/apidiffs/1.52.0_vs_1.51.0/opentelemetry-sdk-trace.txt @@ -0,0 +1,2 @@ +Comparing source compatibility of opentelemetry-sdk-trace-1.52.0.jar against opentelemetry-sdk-trace-1.51.0.jar +No changes. \ No newline at end of file diff --git a/docs/apidiffs/1.52.0_vs_1.51.0/opentelemetry-sdk.txt b/docs/apidiffs/1.52.0_vs_1.51.0/opentelemetry-sdk.txt new file mode 100644 index 0000000000..31036408d0 --- /dev/null +++ b/docs/apidiffs/1.52.0_vs_1.51.0/opentelemetry-sdk.txt @@ -0,0 +1,2 @@ +Comparing source compatibility of opentelemetry-sdk-1.52.0.jar against opentelemetry-sdk-1.51.0.jar +No changes. \ No newline at end of file diff --git a/docs/apidiffs/current_vs_latest/opentelemetry-api.txt b/docs/apidiffs/current_vs_latest/opentelemetry-api.txt index 3a33f42b55..16a39e345a 100644 --- a/docs/apidiffs/current_vs_latest/opentelemetry-api.txt +++ b/docs/apidiffs/current_vs_latest/opentelemetry-api.txt @@ -1,4 +1,2 @@ -Comparing source compatibility of opentelemetry-api-1.52.0-SNAPSHOT.jar against opentelemetry-api-1.51.0.jar -*** MODIFIED CLASS: PUBLIC FINAL io.opentelemetry.api.GlobalOpenTelemetry (not serializable) - === CLASS FILE FORMAT VERSION: 52.0 <- 52.0 - +++ NEW METHOD: PUBLIC(+) STATIC(+) void set(java.util.function.Supplier) +Comparing source compatibility of opentelemetry-api-1.53.0-SNAPSHOT.jar against opentelemetry-api-1.52.0.jar +No changes. \ No newline at end of file diff --git a/docs/apidiffs/current_vs_latest/opentelemetry-common.txt b/docs/apidiffs/current_vs_latest/opentelemetry-common.txt new file mode 100644 index 0000000000..fbf00f472b --- /dev/null +++ b/docs/apidiffs/current_vs_latest/opentelemetry-common.txt @@ -0,0 +1,2 @@ +Comparing source compatibility of opentelemetry-common-1.53.0-SNAPSHOT.jar against opentelemetry-common-1.52.0.jar +No changes. \ No newline at end of file diff --git a/docs/apidiffs/current_vs_latest/opentelemetry-context.txt b/docs/apidiffs/current_vs_latest/opentelemetry-context.txt index cb58011e0e..8f1bc99ad3 100644 --- a/docs/apidiffs/current_vs_latest/opentelemetry-context.txt +++ b/docs/apidiffs/current_vs_latest/opentelemetry-context.txt @@ -1,2 +1,2 @@ -Comparing source compatibility of opentelemetry-context-1.52.0-SNAPSHOT.jar against opentelemetry-context-1.51.0.jar +Comparing source compatibility of opentelemetry-context-1.53.0-SNAPSHOT.jar against opentelemetry-context-1.52.0.jar No changes. \ No newline at end of file diff --git a/docs/apidiffs/current_vs_latest/opentelemetry-exporter-common.txt b/docs/apidiffs/current_vs_latest/opentelemetry-exporter-common.txt index 4adbdc7eab..8bf7d0f8ef 100644 --- a/docs/apidiffs/current_vs_latest/opentelemetry-exporter-common.txt +++ b/docs/apidiffs/current_vs_latest/opentelemetry-exporter-common.txt @@ -1,2 +1,2 @@ -Comparing source compatibility of opentelemetry-exporter-common-1.52.0-SNAPSHOT.jar against opentelemetry-exporter-common-1.51.0.jar +Comparing source compatibility of opentelemetry-exporter-common-1.53.0-SNAPSHOT.jar against opentelemetry-exporter-common-1.52.0.jar No changes. \ No newline at end of file diff --git a/docs/apidiffs/current_vs_latest/opentelemetry-exporter-logging.txt b/docs/apidiffs/current_vs_latest/opentelemetry-exporter-logging.txt index 87aceeae92..b1c7bbe5b9 100644 --- a/docs/apidiffs/current_vs_latest/opentelemetry-exporter-logging.txt +++ b/docs/apidiffs/current_vs_latest/opentelemetry-exporter-logging.txt @@ -1,2 +1,2 @@ -Comparing source compatibility of opentelemetry-exporter-logging-1.52.0-SNAPSHOT.jar against opentelemetry-exporter-logging-1.51.0.jar +Comparing source compatibility of opentelemetry-exporter-logging-1.53.0-SNAPSHOT.jar against opentelemetry-exporter-logging-1.52.0.jar No changes. \ No newline at end of file diff --git a/docs/apidiffs/current_vs_latest/opentelemetry-exporter-otlp-common.txt b/docs/apidiffs/current_vs_latest/opentelemetry-exporter-otlp-common.txt index 848c6d856a..a53541427a 100644 --- a/docs/apidiffs/current_vs_latest/opentelemetry-exporter-otlp-common.txt +++ b/docs/apidiffs/current_vs_latest/opentelemetry-exporter-otlp-common.txt @@ -1,2 +1,2 @@ -Comparing source compatibility of opentelemetry-exporter-otlp-common-1.52.0-SNAPSHOT.jar against opentelemetry-exporter-otlp-common-1.51.0.jar +Comparing source compatibility of opentelemetry-exporter-otlp-common-1.53.0-SNAPSHOT.jar against opentelemetry-exporter-otlp-common-1.52.0.jar No changes. \ No newline at end of file diff --git a/docs/apidiffs/current_vs_latest/opentelemetry-exporter-otlp.txt b/docs/apidiffs/current_vs_latest/opentelemetry-exporter-otlp.txt index 50c76ab468..49c16fd7d0 100644 --- a/docs/apidiffs/current_vs_latest/opentelemetry-exporter-otlp.txt +++ b/docs/apidiffs/current_vs_latest/opentelemetry-exporter-otlp.txt @@ -1,2 +1,2 @@ -Comparing source compatibility of opentelemetry-exporter-otlp-1.52.0-SNAPSHOT.jar against opentelemetry-exporter-otlp-1.51.0.jar +Comparing source compatibility of opentelemetry-exporter-otlp-1.53.0-SNAPSHOT.jar against opentelemetry-exporter-otlp-1.52.0.jar No changes. \ No newline at end of file diff --git a/docs/apidiffs/current_vs_latest/opentelemetry-exporter-sender-grpc-managed-channel.txt b/docs/apidiffs/current_vs_latest/opentelemetry-exporter-sender-grpc-managed-channel.txt index b38200896b..7abad03a3c 100644 --- a/docs/apidiffs/current_vs_latest/opentelemetry-exporter-sender-grpc-managed-channel.txt +++ b/docs/apidiffs/current_vs_latest/opentelemetry-exporter-sender-grpc-managed-channel.txt @@ -1,2 +1,2 @@ -Comparing source compatibility of opentelemetry-exporter-sender-grpc-managed-channel-1.52.0-SNAPSHOT.jar against opentelemetry-exporter-sender-grpc-managed-channel-1.51.0.jar +Comparing source compatibility of opentelemetry-exporter-sender-grpc-managed-channel-1.53.0-SNAPSHOT.jar against opentelemetry-exporter-sender-grpc-managed-channel-1.52.0.jar No changes. \ No newline at end of file diff --git a/docs/apidiffs/current_vs_latest/opentelemetry-exporter-sender-jdk.txt b/docs/apidiffs/current_vs_latest/opentelemetry-exporter-sender-jdk.txt index 85aede2379..aa1d482da5 100644 --- a/docs/apidiffs/current_vs_latest/opentelemetry-exporter-sender-jdk.txt +++ b/docs/apidiffs/current_vs_latest/opentelemetry-exporter-sender-jdk.txt @@ -1,2 +1,2 @@ -Comparing source compatibility of opentelemetry-exporter-sender-jdk-1.52.0-SNAPSHOT.jar against opentelemetry-exporter-sender-jdk-1.51.0.jar +Comparing source compatibility of opentelemetry-exporter-sender-jdk-1.53.0-SNAPSHOT.jar against opentelemetry-exporter-sender-jdk-1.52.0.jar No changes. \ No newline at end of file diff --git a/docs/apidiffs/current_vs_latest/opentelemetry-exporter-sender-okhttp.txt b/docs/apidiffs/current_vs_latest/opentelemetry-exporter-sender-okhttp.txt index 6ab8855cde..9f01583dcb 100644 --- a/docs/apidiffs/current_vs_latest/opentelemetry-exporter-sender-okhttp.txt +++ b/docs/apidiffs/current_vs_latest/opentelemetry-exporter-sender-okhttp.txt @@ -1,2 +1,2 @@ -Comparing source compatibility of opentelemetry-exporter-sender-okhttp-1.52.0-SNAPSHOT.jar against opentelemetry-exporter-sender-okhttp-1.51.0.jar +Comparing source compatibility of opentelemetry-exporter-sender-okhttp-1.53.0-SNAPSHOT.jar against opentelemetry-exporter-sender-okhttp-1.52.0.jar No changes. \ No newline at end of file diff --git a/docs/apidiffs/current_vs_latest/opentelemetry-exporter-zipkin.txt b/docs/apidiffs/current_vs_latest/opentelemetry-exporter-zipkin.txt index 76f764476b..64142ed242 100644 --- a/docs/apidiffs/current_vs_latest/opentelemetry-exporter-zipkin.txt +++ b/docs/apidiffs/current_vs_latest/opentelemetry-exporter-zipkin.txt @@ -1,2 +1,2 @@ -Comparing source compatibility of opentelemetry-exporter-zipkin-1.52.0-SNAPSHOT.jar against opentelemetry-exporter-zipkin-1.51.0.jar +Comparing source compatibility of opentelemetry-exporter-zipkin-1.53.0-SNAPSHOT.jar against opentelemetry-exporter-zipkin-1.52.0.jar No changes. \ No newline at end of file diff --git a/docs/apidiffs/current_vs_latest/opentelemetry-extension-kotlin.txt b/docs/apidiffs/current_vs_latest/opentelemetry-extension-kotlin.txt index 9af4a4a8cc..6e45651ba6 100644 --- a/docs/apidiffs/current_vs_latest/opentelemetry-extension-kotlin.txt +++ b/docs/apidiffs/current_vs_latest/opentelemetry-extension-kotlin.txt @@ -1,2 +1,2 @@ -Comparing source compatibility of opentelemetry-extension-kotlin-1.52.0-SNAPSHOT.jar against opentelemetry-extension-kotlin-1.51.0.jar +Comparing source compatibility of opentelemetry-extension-kotlin-1.53.0-SNAPSHOT.jar against opentelemetry-extension-kotlin-1.52.0.jar No changes. \ No newline at end of file diff --git a/docs/apidiffs/current_vs_latest/opentelemetry-extension-trace-propagators.txt b/docs/apidiffs/current_vs_latest/opentelemetry-extension-trace-propagators.txt index 891f62ba90..1ac8ae240e 100644 --- a/docs/apidiffs/current_vs_latest/opentelemetry-extension-trace-propagators.txt +++ b/docs/apidiffs/current_vs_latest/opentelemetry-extension-trace-propagators.txt @@ -1,2 +1,2 @@ -Comparing source compatibility of opentelemetry-extension-trace-propagators-1.52.0-SNAPSHOT.jar against opentelemetry-extension-trace-propagators-1.51.0.jar +Comparing source compatibility of opentelemetry-extension-trace-propagators-1.53.0-SNAPSHOT.jar against opentelemetry-extension-trace-propagators-1.52.0.jar No changes. \ No newline at end of file diff --git a/docs/apidiffs/current_vs_latest/opentelemetry-opentracing-shim.txt b/docs/apidiffs/current_vs_latest/opentelemetry-opentracing-shim.txt index d6ab0403f9..548888915b 100644 --- a/docs/apidiffs/current_vs_latest/opentelemetry-opentracing-shim.txt +++ b/docs/apidiffs/current_vs_latest/opentelemetry-opentracing-shim.txt @@ -1,2 +1,2 @@ -Comparing source compatibility of opentelemetry-opentracing-shim-1.52.0-SNAPSHOT.jar against opentelemetry-opentracing-shim-1.51.0.jar +Comparing source compatibility of opentelemetry-opentracing-shim-1.53.0-SNAPSHOT.jar against opentelemetry-opentracing-shim-1.52.0.jar No changes. \ No newline at end of file diff --git a/docs/apidiffs/current_vs_latest/opentelemetry-sdk-common.txt b/docs/apidiffs/current_vs_latest/opentelemetry-sdk-common.txt index 5d251deb80..ccf7dfc193 100644 --- a/docs/apidiffs/current_vs_latest/opentelemetry-sdk-common.txt +++ b/docs/apidiffs/current_vs_latest/opentelemetry-sdk-common.txt @@ -1,2 +1,2 @@ -Comparing source compatibility of opentelemetry-sdk-common-1.52.0-SNAPSHOT.jar against opentelemetry-sdk-common-1.51.0.jar +Comparing source compatibility of opentelemetry-sdk-common-1.53.0-SNAPSHOT.jar against opentelemetry-sdk-common-1.52.0.jar No changes. \ No newline at end of file diff --git a/docs/apidiffs/current_vs_latest/opentelemetry-sdk-extension-autoconfigure-spi.txt b/docs/apidiffs/current_vs_latest/opentelemetry-sdk-extension-autoconfigure-spi.txt index e39912ab8a..831fedd5bc 100644 --- a/docs/apidiffs/current_vs_latest/opentelemetry-sdk-extension-autoconfigure-spi.txt +++ b/docs/apidiffs/current_vs_latest/opentelemetry-sdk-extension-autoconfigure-spi.txt @@ -1,2 +1,2 @@ -Comparing source compatibility of opentelemetry-sdk-extension-autoconfigure-spi-1.52.0-SNAPSHOT.jar against opentelemetry-sdk-extension-autoconfigure-spi-1.51.0.jar +Comparing source compatibility of opentelemetry-sdk-extension-autoconfigure-spi-1.53.0-SNAPSHOT.jar against opentelemetry-sdk-extension-autoconfigure-spi-1.52.0.jar No changes. \ No newline at end of file diff --git a/docs/apidiffs/current_vs_latest/opentelemetry-sdk-extension-autoconfigure.txt b/docs/apidiffs/current_vs_latest/opentelemetry-sdk-extension-autoconfigure.txt index 6dcf9a630a..8e69e46ffe 100644 --- a/docs/apidiffs/current_vs_latest/opentelemetry-sdk-extension-autoconfigure.txt +++ b/docs/apidiffs/current_vs_latest/opentelemetry-sdk-extension-autoconfigure.txt @@ -1,2 +1,2 @@ -Comparing source compatibility of opentelemetry-sdk-extension-autoconfigure-1.52.0-SNAPSHOT.jar against opentelemetry-sdk-extension-autoconfigure-1.51.0.jar +Comparing source compatibility of opentelemetry-sdk-extension-autoconfigure-1.53.0-SNAPSHOT.jar against opentelemetry-sdk-extension-autoconfigure-1.52.0.jar No changes. \ No newline at end of file diff --git a/docs/apidiffs/current_vs_latest/opentelemetry-sdk-extension-jaeger-remote-sampler.txt b/docs/apidiffs/current_vs_latest/opentelemetry-sdk-extension-jaeger-remote-sampler.txt index a89cd3410c..b10c5dd623 100644 --- a/docs/apidiffs/current_vs_latest/opentelemetry-sdk-extension-jaeger-remote-sampler.txt +++ b/docs/apidiffs/current_vs_latest/opentelemetry-sdk-extension-jaeger-remote-sampler.txt @@ -1,2 +1,2 @@ -Comparing source compatibility of opentelemetry-sdk-extension-jaeger-remote-sampler-1.52.0-SNAPSHOT.jar against opentelemetry-sdk-extension-jaeger-remote-sampler-1.51.0.jar +Comparing source compatibility of opentelemetry-sdk-extension-jaeger-remote-sampler-1.53.0-SNAPSHOT.jar against opentelemetry-sdk-extension-jaeger-remote-sampler-1.52.0.jar No changes. \ No newline at end of file diff --git a/docs/apidiffs/current_vs_latest/opentelemetry-sdk-logs.txt b/docs/apidiffs/current_vs_latest/opentelemetry-sdk-logs.txt index a953ebebc8..7032490f7d 100644 --- a/docs/apidiffs/current_vs_latest/opentelemetry-sdk-logs.txt +++ b/docs/apidiffs/current_vs_latest/opentelemetry-sdk-logs.txt @@ -1,2 +1,2 @@ -Comparing source compatibility of opentelemetry-sdk-logs-1.52.0-SNAPSHOT.jar against opentelemetry-sdk-logs-1.51.0.jar +Comparing source compatibility of opentelemetry-sdk-logs-1.53.0-SNAPSHOT.jar against opentelemetry-sdk-logs-1.52.0.jar No changes. \ No newline at end of file diff --git a/docs/apidiffs/current_vs_latest/opentelemetry-sdk-metrics.txt b/docs/apidiffs/current_vs_latest/opentelemetry-sdk-metrics.txt index abe77806ba..0ad874a656 100644 --- a/docs/apidiffs/current_vs_latest/opentelemetry-sdk-metrics.txt +++ b/docs/apidiffs/current_vs_latest/opentelemetry-sdk-metrics.txt @@ -1,2 +1,2 @@ -Comparing source compatibility of opentelemetry-sdk-metrics-1.52.0-SNAPSHOT.jar against opentelemetry-sdk-metrics-1.51.0.jar +Comparing source compatibility of opentelemetry-sdk-metrics-1.53.0-SNAPSHOT.jar against opentelemetry-sdk-metrics-1.52.0.jar No changes. \ No newline at end of file diff --git a/docs/apidiffs/current_vs_latest/opentelemetry-sdk-testing.txt b/docs/apidiffs/current_vs_latest/opentelemetry-sdk-testing.txt index 2c87bd8600..e933234970 100644 --- a/docs/apidiffs/current_vs_latest/opentelemetry-sdk-testing.txt +++ b/docs/apidiffs/current_vs_latest/opentelemetry-sdk-testing.txt @@ -1,2 +1,2 @@ -Comparing source compatibility of opentelemetry-sdk-testing-1.52.0-SNAPSHOT.jar against opentelemetry-sdk-testing-1.51.0.jar +Comparing source compatibility of opentelemetry-sdk-testing-1.53.0-SNAPSHOT.jar against opentelemetry-sdk-testing-1.52.0.jar No changes. \ No newline at end of file diff --git a/docs/apidiffs/current_vs_latest/opentelemetry-sdk-trace.txt b/docs/apidiffs/current_vs_latest/opentelemetry-sdk-trace.txt index e4b6afee82..b02f275437 100644 --- a/docs/apidiffs/current_vs_latest/opentelemetry-sdk-trace.txt +++ b/docs/apidiffs/current_vs_latest/opentelemetry-sdk-trace.txt @@ -1,2 +1,2 @@ -Comparing source compatibility of opentelemetry-sdk-trace-1.52.0-SNAPSHOT.jar against opentelemetry-sdk-trace-1.51.0.jar +Comparing source compatibility of opentelemetry-sdk-trace-1.53.0-SNAPSHOT.jar against opentelemetry-sdk-trace-1.52.0.jar No changes. \ No newline at end of file diff --git a/docs/apidiffs/current_vs_latest/opentelemetry-sdk.txt b/docs/apidiffs/current_vs_latest/opentelemetry-sdk.txt index 2a991fb12f..407cc50046 100644 --- a/docs/apidiffs/current_vs_latest/opentelemetry-sdk.txt +++ b/docs/apidiffs/current_vs_latest/opentelemetry-sdk.txt @@ -1,2 +1,2 @@ -Comparing source compatibility of opentelemetry-sdk-1.52.0-SNAPSHOT.jar against opentelemetry-sdk-1.51.0.jar +Comparing source compatibility of opentelemetry-sdk-1.53.0-SNAPSHOT.jar against opentelemetry-sdk-1.52.0.jar No changes. \ No newline at end of file diff --git a/exporters/common/src/main/java/io/opentelemetry/exporter/internal/compression/CompressorUtil.java b/exporters/common/src/main/java/io/opentelemetry/exporter/internal/compression/CompressorUtil.java index 9748ea508a..92b7d2d9c6 100644 --- a/exporters/common/src/main/java/io/opentelemetry/exporter/internal/compression/CompressorUtil.java +++ b/exporters/common/src/main/java/io/opentelemetry/exporter/internal/compression/CompressorUtil.java @@ -8,9 +8,9 @@ package io.opentelemetry.exporter.internal.compression; import static io.opentelemetry.api.internal.Utils.checkArgument; import static java.util.stream.Collectors.joining; +import io.opentelemetry.common.ComponentLoader; import java.util.HashMap; import java.util.Map; -import java.util.ServiceLoader; import java.util.Set; import javax.annotation.Nullable; @@ -24,7 +24,9 @@ import javax.annotation.Nullable; */ public final class CompressorUtil { - private static final Map compressorRegistry = buildCompressorRegistry(); + private static final Map compressorRegistry = + buildCompressorRegistry( + ComponentLoader.forClassLoader(CompressorUtil.class.getClassLoader())); private CompressorUtil() {} @@ -36,8 +38,26 @@ public final class CompressorUtil { */ @Nullable public static Compressor validateAndResolveCompressor(String compressionMethod) { - Set supportedEncodings = compressorRegistry.keySet(); - Compressor compressor = compressorRegistry.get(compressionMethod); + return validateAndResolveCompressor(compressionMethod, null); + } + + /** + * Validate that the {@code compressionMethod} is "none" or matches a registered compressor. + * + * @param compressionMethod the compression method to validate and resolve + * @param componentLoader the component loader to use for loading SPI implementations, or null to + * use the default + * @return {@code null} if {@code compressionMethod} is "none" or the registered compressor + * @throws IllegalArgumentException if no match is found + */ + @Nullable + public static Compressor validateAndResolveCompressor( + String compressionMethod, @Nullable ComponentLoader componentLoader) { + Map registry = + componentLoader == null ? compressorRegistry : buildCompressorRegistry(componentLoader); + + Set supportedEncodings = registry.keySet(); + Compressor compressor = registry.get(compressionMethod); checkArgument( "none".equals(compressionMethod) || compressor != null, "Unsupported compressionMethod. Compression method must be \"none\" or one of: " @@ -45,10 +65,9 @@ public final class CompressorUtil { return compressor; } - private static Map buildCompressorRegistry() { + private static Map buildCompressorRegistry(ComponentLoader componentLoader) { Map compressors = new HashMap<>(); - for (CompressorProvider spi : - ServiceLoader.load(CompressorProvider.class, CompressorUtil.class.getClassLoader())) { + for (CompressorProvider spi : componentLoader.load(CompressorProvider.class)) { Compressor compressor = spi.getInstance(); compressors.put(compressor.getEncoding(), compressor); } diff --git a/exporters/common/src/main/java/io/opentelemetry/exporter/internal/grpc/GrpcExporterBuilder.java b/exporters/common/src/main/java/io/opentelemetry/exporter/internal/grpc/GrpcExporterBuilder.java index 6f2ae64b86..27d06b7116 100644 --- a/exporters/common/src/main/java/io/opentelemetry/exporter/internal/grpc/GrpcExporterBuilder.java +++ b/exporters/common/src/main/java/io/opentelemetry/exporter/internal/grpc/GrpcExporterBuilder.java @@ -10,9 +10,12 @@ import io.grpc.ManagedChannel; import io.opentelemetry.api.GlobalOpenTelemetry; import io.opentelemetry.api.internal.ConfigUtil; import io.opentelemetry.api.metrics.MeterProvider; +import io.opentelemetry.common.ComponentLoader; import io.opentelemetry.exporter.internal.ExporterBuilderUtil; import io.opentelemetry.exporter.internal.TlsConfigHelper; import io.opentelemetry.exporter.internal.compression.Compressor; +import io.opentelemetry.exporter.internal.compression.CompressorProvider; +import io.opentelemetry.exporter.internal.compression.CompressorUtil; import io.opentelemetry.exporter.internal.marshal.Marshaler; import io.opentelemetry.sdk.common.InternalTelemetryVersion; import io.opentelemetry.sdk.common.export.RetryPolicy; @@ -26,7 +29,6 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Optional; -import java.util.ServiceLoader; import java.util.StringJoiner; import java.util.concurrent.ExecutorService; import java.util.concurrent.TimeUnit; @@ -67,7 +69,8 @@ public class GrpcExporterBuilder { private Supplier meterProviderSupplier = GlobalOpenTelemetry::getMeterProvider; private InternalTelemetryVersion internalTelemetryVersion = InternalTelemetryVersion.LEGACY; - private ClassLoader serviceClassLoader = GrpcExporterBuilder.class.getClassLoader(); + private ComponentLoader componentLoader = + ComponentLoader.forClassLoader(GrpcExporterBuilder.class.getClassLoader()); @Nullable private ExecutorService executorService; // Use Object type since gRPC may not be on the classpath. @@ -115,6 +118,17 @@ public class GrpcExporterBuilder { return this; } + /** + * Sets the method used to compress payloads. If unset, compression is disabled. Compression + * method "gzip" and "none" are supported out of the box. Support for additional compression + * methods is available by implementing {@link Compressor} and {@link CompressorProvider}. + */ + public GrpcExporterBuilder setCompression(String compressionMethod) { + Compressor compressor = + CompressorUtil.validateAndResolveCompressor(compressionMethod, componentLoader); + return setCompression(compressor); + } + public GrpcExporterBuilder setTrustManagerFromCerts(byte[] trustedCertificatesPem) { tlsConfigHelper.setTrustManagerFromCerts(trustedCertificatesPem); return this; @@ -158,8 +172,8 @@ public class GrpcExporterBuilder { return this; } - public GrpcExporterBuilder setServiceClassLoader(ClassLoader servieClassLoader) { - this.serviceClassLoader = servieClassLoader; + public GrpcExporterBuilder setComponentLoader(ComponentLoader componentLoader) { + this.componentLoader = componentLoader; return this; } @@ -268,7 +282,7 @@ public class GrpcExporterBuilder { if (grpcChannel != null) { joiner.add("grpcChannel=" + grpcChannel); } - joiner.add("serviceClassLoader=" + serviceClassLoader); + joiner.add("componentLoader=" + componentLoader); if (executorService != null) { joiner.add("executorService=" + executorService); } @@ -302,8 +316,7 @@ public class GrpcExporterBuilder { */ private GrpcSenderProvider resolveGrpcSenderProvider() { Map grpcSenderProviders = new HashMap<>(); - for (GrpcSenderProvider spi : - ServiceLoader.load(GrpcSenderProvider.class, serviceClassLoader)) { + for (GrpcSenderProvider spi : componentLoader.load(GrpcSenderProvider.class)) { grpcSenderProviders.put(spi.getClass().getName(), spi); } diff --git a/exporters/common/src/main/java/io/opentelemetry/exporter/internal/http/HttpExporterBuilder.java b/exporters/common/src/main/java/io/opentelemetry/exporter/internal/http/HttpExporterBuilder.java index 4cd671c32c..42d55cb0a2 100644 --- a/exporters/common/src/main/java/io/opentelemetry/exporter/internal/http/HttpExporterBuilder.java +++ b/exporters/common/src/main/java/io/opentelemetry/exporter/internal/http/HttpExporterBuilder.java @@ -8,9 +8,12 @@ package io.opentelemetry.exporter.internal.http; import io.opentelemetry.api.GlobalOpenTelemetry; import io.opentelemetry.api.internal.ConfigUtil; import io.opentelemetry.api.metrics.MeterProvider; +import io.opentelemetry.common.ComponentLoader; import io.opentelemetry.exporter.internal.ExporterBuilderUtil; import io.opentelemetry.exporter.internal.TlsConfigHelper; import io.opentelemetry.exporter.internal.compression.Compressor; +import io.opentelemetry.exporter.internal.compression.CompressorProvider; +import io.opentelemetry.exporter.internal.compression.CompressorUtil; import io.opentelemetry.exporter.internal.marshal.Marshaler; import io.opentelemetry.sdk.common.InternalTelemetryVersion; import io.opentelemetry.sdk.common.export.ProxyOptions; @@ -24,7 +27,6 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Optional; -import java.util.ServiceLoader; import java.util.StringJoiner; import java.util.concurrent.ExecutorService; import java.util.concurrent.TimeUnit; @@ -64,7 +66,8 @@ public final class HttpExporterBuilder { @Nullable private RetryPolicy retryPolicy = RetryPolicy.getDefault(); private Supplier meterProviderSupplier = GlobalOpenTelemetry::getMeterProvider; private InternalTelemetryVersion internalTelemetryVersion = InternalTelemetryVersion.LEGACY; - private ClassLoader serviceClassLoader = HttpExporterBuilder.class.getClassLoader(); + private ComponentLoader componentLoader = + ComponentLoader.forClassLoader(HttpExporterBuilder.class.getClassLoader()); @Nullable private ExecutorService executorService; public HttpExporterBuilder( @@ -95,6 +98,17 @@ public final class HttpExporterBuilder { return this; } + /** + * Sets the method used to compress payloads. If unset, compression is disabled. Compression + * method "gzip" and "none" are supported out of the box. Support for additional compression + * methods is available by implementing {@link Compressor} and {@link CompressorProvider}. + */ + public HttpExporterBuilder setCompression(String compressionMethod) { + Compressor compressor = + CompressorUtil.validateAndResolveCompressor(compressionMethod, componentLoader); + return setCompression(compressor); + } + public HttpExporterBuilder addConstantHeaders(String key, String value) { constantHeaders.put(key, value); return this; @@ -143,8 +157,8 @@ public final class HttpExporterBuilder { return this; } - public HttpExporterBuilder setServiceClassLoader(ClassLoader servieClassLoader) { - this.serviceClassLoader = servieClassLoader; + public HttpExporterBuilder setComponentLoader(ComponentLoader componentLoader) { + this.componentLoader = componentLoader; return this; } @@ -265,7 +279,7 @@ public final class HttpExporterBuilder { if (retryPolicy != null) { joiner.add("retryPolicy=" + retryPolicy); } - joiner.add("serviceClassLoader=" + serviceClassLoader); + joiner.add("componentLoader=" + componentLoader); if (executorService != null) { joiner.add("executorService=" + executorService); } @@ -299,8 +313,7 @@ public final class HttpExporterBuilder { */ private HttpSenderProvider resolveHttpSenderProvider() { Map httpSenderProviders = new HashMap<>(); - for (HttpSenderProvider spi : - ServiceLoader.load(HttpSenderProvider.class, serviceClassLoader)) { + for (HttpSenderProvider spi : componentLoader.load(HttpSenderProvider.class)) { httpSenderProviders.put(spi.getClass().getName(), spi); } diff --git a/exporters/common/src/test/java/io/opentelemetry/exporter/internal/compression/CompressorUtilTest.java b/exporters/common/src/test/java/io/opentelemetry/exporter/internal/compression/CompressorUtilTest.java new file mode 100644 index 0000000000..d8dcffd3c4 --- /dev/null +++ b/exporters/common/src/test/java/io/opentelemetry/exporter/internal/compression/CompressorUtilTest.java @@ -0,0 +1,87 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.exporter.internal.compression; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import io.opentelemetry.common.ComponentLoader; +import java.net.URL; +import java.net.URLClassLoader; +import org.junit.jupiter.api.Test; + +class CompressorUtilTest { + + private final ComponentLoader componentLoader = + ComponentLoader.forClassLoader(CompressorUtilTest.class.getClassLoader()); + + @Test + void validateAndResolveCompressor_none() { + assertThat(CompressorUtil.validateAndResolveCompressor("none")).isNull(); + } + + @Test + void validateAndResolveCompressor_gzip() { + assertThat(CompressorUtil.validateAndResolveCompressor("gzip")) + .isEqualTo(GzipCompressor.getInstance()); + } + + @Test + void validateAndResolveCompressor_invalid() { + assertThatThrownBy(() -> CompressorUtil.validateAndResolveCompressor("invalid")) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("Unsupported compressionMethod"); + } + + @Test + void validateAndResolveCompressor_withClassLoader_none() { + assertThat(CompressorUtil.validateAndResolveCompressor("none", componentLoader)).isNull(); + } + + @Test + void validateAndResolveCompressor_withClassLoader_gzip() { + assertThat(CompressorUtil.validateAndResolveCompressor("gzip", componentLoader)) + .isEqualTo(GzipCompressor.getInstance()); + } + + @Test + void validateAndResolveCompressor_withClassLoader_invalid() { + assertThatThrownBy( + () -> CompressorUtil.validateAndResolveCompressor("invalid", componentLoader)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("Unsupported compressionMethod"); + } + + @Test + void validateAndResolveCompressor_emptyClassLoader() { + // Create a class loader that cannot load CompressorProvider services + ComponentLoader emptyComponentLoader = + ComponentLoader.forClassLoader(new URLClassLoader(new URL[0], null)); + + // Gzip should still work because it's hardcoded + assertThat(CompressorUtil.validateAndResolveCompressor("gzip", emptyComponentLoader)) + .isEqualTo(GzipCompressor.getInstance()); + + // None should still work because it doesn't require loading services + assertThat(CompressorUtil.validateAndResolveCompressor("none", emptyComponentLoader)).isNull(); + + // Any SPI-based compressor should not be available + assertThatThrownBy( + () -> CompressorUtil.validateAndResolveCompressor("base64", emptyComponentLoader)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("Unsupported compressionMethod"); + } + + @Test + void validateAndResolveCompressor_delegatesCorrectly() { + // Test that single-parameter method delegates to two-parameter method + assertThat(CompressorUtil.validateAndResolveCompressor("gzip")) + .isEqualTo(CompressorUtil.validateAndResolveCompressor("gzip", componentLoader)); + + assertThat(CompressorUtil.validateAndResolveCompressor("none")) + .isEqualTo(CompressorUtil.validateAndResolveCompressor("none", componentLoader)); + } +} diff --git a/exporters/common/src/test/java/io/opentelemetry/exporter/internal/grpc/GrpcExporterBuilderTest.java b/exporters/common/src/test/java/io/opentelemetry/exporter/internal/grpc/GrpcExporterBuilderTest.java index cdf797df32..b17f3cdb59 100644 --- a/exporters/common/src/test/java/io/opentelemetry/exporter/internal/grpc/GrpcExporterBuilderTest.java +++ b/exporters/common/src/test/java/io/opentelemetry/exporter/internal/grpc/GrpcExporterBuilderTest.java @@ -6,11 +6,16 @@ package io.opentelemetry.exporter.internal.grpc; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import io.opentelemetry.common.ComponentLoader; +import io.opentelemetry.exporter.internal.compression.Compressor; import io.opentelemetry.exporter.internal.compression.GzipCompressor; import io.opentelemetry.exporter.internal.marshal.Marshaler; import io.opentelemetry.sdk.internal.StandardComponentId; import java.net.URI; +import java.net.URL; +import java.net.URLClassLoader; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -36,7 +41,7 @@ class GrpcExporterBuilderTest { @Test void compressionNone() { - builder.setCompression(null); + builder.setCompression((Compressor) null); assertThat(builder).extracting("compressor").isNull(); } @@ -50,8 +55,45 @@ class GrpcExporterBuilderTest { @Test void compressionEnabledAndDisabled() { - builder.setCompression(GzipCompressor.getInstance()).setCompression(null); + builder.setCompression(GzipCompressor.getInstance()).setCompression((Compressor) null); assertThat(builder).extracting("compressor").isNull(); } + + @Test + void compressionString_none() { + builder.setCompression("none"); + + assertThat(builder).extracting("compressor").isNull(); + } + + @Test + void compressionString_gzip() { + builder.setCompression("gzip"); + + assertThat(builder).extracting("compressor").isEqualTo(GzipCompressor.getInstance()); + } + + @Test + void compressionString_invalid() { + assertThatThrownBy(() -> builder.setCompression("invalid-compression")) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("Unsupported compressionMethod"); + } + + @Test + void compressionString_usesServiceClassLoader() { + // Create a class loader that cannot load CompressorProvider services + ComponentLoader emptyComponentLoader = + ComponentLoader.forClassLoader(new URLClassLoader(new URL[0], null)); + builder.setComponentLoader(emptyComponentLoader); + + // This should still work because gzip compressor is hardcoded + builder.setCompression("gzip"); + assertThat(builder).extracting("compressor").isEqualTo(GzipCompressor.getInstance()); + + // This should still work because "none" doesn't require loading services + builder.setCompression("none"); + assertThat(builder).extracting("compressor").isNull(); + } } diff --git a/exporters/common/src/test/java/io/opentelemetry/exporter/internal/http/HttpExporterBuilderTest.java b/exporters/common/src/test/java/io/opentelemetry/exporter/internal/http/HttpExporterBuilderTest.java new file mode 100644 index 0000000000..c924dcd9af --- /dev/null +++ b/exporters/common/src/test/java/io/opentelemetry/exporter/internal/http/HttpExporterBuilderTest.java @@ -0,0 +1,94 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.exporter.internal.http; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import io.opentelemetry.common.ComponentLoader; +import io.opentelemetry.exporter.internal.compression.Compressor; +import io.opentelemetry.exporter.internal.compression.GzipCompressor; +import io.opentelemetry.exporter.internal.marshal.Marshaler; +import io.opentelemetry.sdk.internal.StandardComponentId; +import java.net.URL; +import java.net.URLClassLoader; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +class HttpExporterBuilderTest { + + private HttpExporterBuilder builder; + + @BeforeEach + void setUp() { + builder = + new HttpExporterBuilder<>( + StandardComponentId.ExporterType.OTLP_HTTP_SPAN_EXPORTER, "http://localhost:4318"); + } + + @Test + void compressionDefault() { + assertThat(builder).extracting("compressor").isNull(); + } + + @Test + void compressionNone() { + builder.setCompression((Compressor) null); + + assertThat(builder).extracting("compressor").isNull(); + } + + @Test + void compressionGzip() { + builder.setCompression(GzipCompressor.getInstance()); + + assertThat(builder).extracting("compressor").isEqualTo(GzipCompressor.getInstance()); + } + + @Test + void compressionEnabledAndDisabled() { + builder.setCompression(GzipCompressor.getInstance()).setCompression((Compressor) null); + + assertThat(builder).extracting("compressor").isNull(); + } + + @Test + void compressionString_none() { + builder.setCompression("none"); + + assertThat(builder).extracting("compressor").isNull(); + } + + @Test + void compressionString_gzip() { + builder.setCompression("gzip"); + + assertThat(builder).extracting("compressor").isEqualTo(GzipCompressor.getInstance()); + } + + @Test + void compressionString_invalid() { + assertThatThrownBy(() -> builder.setCompression("invalid-compression")) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("Unsupported compressionMethod"); + } + + @Test + void compressionString_usesServiceClassLoader() { + // Create a class loader that cannot load CompressorProvider services + ComponentLoader emptyComponentLoader = + ComponentLoader.forClassLoader(new URLClassLoader(new URL[0], null)); + builder.setComponentLoader(emptyComponentLoader); + + // This should still work because gzip compressor is hardcoded + builder.setCompression("gzip"); + assertThat(builder).extracting("compressor").isEqualTo(GzipCompressor.getInstance()); + + // This should still work because "none" doesn't require loading services + builder.setCompression("none"); + assertThat(builder).extracting("compressor").isNull(); + } +} diff --git a/exporters/logging-otlp/src/test/java/io/opentelemetry/exporter/logging/otlp/AbstractOtlpStdoutExporterTest.java b/exporters/logging-otlp/src/test/java/io/opentelemetry/exporter/logging/otlp/AbstractOtlpStdoutExporterTest.java index ed7b79f65e..44b046aca4 100644 --- a/exporters/logging-otlp/src/test/java/io/opentelemetry/exporter/logging/otlp/AbstractOtlpStdoutExporterTest.java +++ b/exporters/logging-otlp/src/test/java/io/opentelemetry/exporter/logging/otlp/AbstractOtlpStdoutExporterTest.java @@ -9,11 +9,10 @@ import static java.util.Collections.emptyMap; import static java.util.Collections.singletonMap; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; -import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; import static org.mockito.Mockito.when; import com.google.common.collect.ImmutableList; -import com.google.common.collect.Streams; import io.github.netmikey.logunit.api.LogCapturer; import io.opentelemetry.api.incubator.config.DeclarativeConfigProperties; import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; @@ -29,10 +28,10 @@ import java.io.UnsupportedEncodingException; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; -import java.util.ServiceLoader; import java.util.concurrent.TimeUnit; import java.util.function.Supplier; import java.util.stream.Stream; +import java.util.stream.StreamSupport; import javax.annotation.Nullable; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; @@ -306,7 +305,7 @@ abstract class AbstractOtlpStdoutExporterTest { @Test void componentProviderConfig() { - DeclarativeConfigProperties properties = mock(DeclarativeConfigProperties.class); + DeclarativeConfigProperties properties = spy(DeclarativeConfigProperties.empty()); T exporter = exporterFromComponentProvider(properties); assertThat(exporter).extracting("wrapperJsonObject").isEqualTo(true); @@ -330,22 +329,22 @@ abstract class AbstractOtlpStdoutExporterTest { @SuppressWarnings("unchecked") protected T exporterFromComponentProvider(DeclarativeConfigProperties properties) { return (T) - ((ComponentProvider) - loadSpi(ComponentProvider.class) - .filter( - p -> { - ComponentProvider c = (ComponentProvider) p; - return "otlp_file/development".equals(c.getName()) - && c.getType().equals(componentProviderType); - }) - .findFirst() - .orElseThrow(() -> new IllegalStateException("No provider found"))) + StreamSupport.stream( + properties.getComponentLoader().load(ComponentProvider.class).spliterator(), false) + .filter( + p -> { + ComponentProvider c = p; + return "otlp_file/development".equals(c.getName()) + && c.getType().equals(componentProviderType); + }) + .findFirst() + .orElseThrow(() -> new IllegalStateException("No provider found")) .create(properties); } @SuppressWarnings("unchecked") protected T exporterFromProvider(ConfigProperties config) { - Object provider = loadProvider(); + Object provider = loadProvider(config); try { return (T) @@ -358,8 +357,9 @@ abstract class AbstractOtlpStdoutExporterTest { } } - private Object loadProvider() { - return loadSpi(providerClass) + private Object loadProvider(ConfigProperties config) { + return StreamSupport.stream( + config.getComponentLoader().load(providerClass).spliterator(), false) .filter( p -> { try { @@ -372,8 +372,4 @@ abstract class AbstractOtlpStdoutExporterTest { .findFirst() .orElseThrow(() -> new IllegalStateException("No provider found")); } - - protected static Stream loadSpi(Class type) { - return Streams.stream(ServiceLoader.load(type, type.getClassLoader()).iterator()); - } } diff --git a/exporters/logging-otlp/src/test/java/io/opentelemetry/exporter/logging/otlp/OtlpStdoutMetricExporterTest.java b/exporters/logging-otlp/src/test/java/io/opentelemetry/exporter/logging/otlp/OtlpStdoutMetricExporterTest.java index df91f1e2cb..28b0b2e358 100644 --- a/exporters/logging-otlp/src/test/java/io/opentelemetry/exporter/logging/otlp/OtlpStdoutMetricExporterTest.java +++ b/exporters/logging-otlp/src/test/java/io/opentelemetry/exporter/logging/otlp/OtlpStdoutMetricExporterTest.java @@ -7,7 +7,7 @@ package io.opentelemetry.exporter.logging.otlp; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat; import static org.assertj.core.api.Assertions.assertThatCode; -import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; import static org.mockito.Mockito.when; import com.google.common.collect.ImmutableMap; @@ -81,7 +81,7 @@ class OtlpStdoutMetricExporterTest @Test void componentProviderMetricConfig() { - DeclarativeConfigProperties properties = mock(DeclarativeConfigProperties.class); + DeclarativeConfigProperties properties = spy(DeclarativeConfigProperties.empty()); when(properties.getString("temporality_preference")).thenReturn("DELTA"); when(properties.getString("default_histogram_aggregation")) .thenReturn("BASE2_EXPONENTIAL_BUCKET_HISTOGRAM"); diff --git a/exporters/otlp/all/src/main/java/io/opentelemetry/exporter/otlp/http/logs/OtlpHttpLogRecordExporterBuilder.java b/exporters/otlp/all/src/main/java/io/opentelemetry/exporter/otlp/http/logs/OtlpHttpLogRecordExporterBuilder.java index 7dfe6f7426..b960691907 100644 --- a/exporters/otlp/all/src/main/java/io/opentelemetry/exporter/otlp/http/logs/OtlpHttpLogRecordExporterBuilder.java +++ b/exporters/otlp/all/src/main/java/io/opentelemetry/exporter/otlp/http/logs/OtlpHttpLogRecordExporterBuilder.java @@ -10,9 +10,8 @@ import static java.util.Objects.requireNonNull; import io.opentelemetry.api.GlobalOpenTelemetry; import io.opentelemetry.api.metrics.MeterProvider; +import io.opentelemetry.common.ComponentLoader; import io.opentelemetry.exporter.internal.compression.Compressor; -import io.opentelemetry.exporter.internal.compression.CompressorProvider; -import io.opentelemetry.exporter.internal.compression.CompressorUtil; import io.opentelemetry.exporter.internal.http.HttpExporterBuilder; import io.opentelemetry.exporter.internal.marshal.Marshaler; import io.opentelemetry.exporter.otlp.internal.OtlpUserAgent; @@ -112,13 +111,12 @@ public final class OtlpHttpLogRecordExporterBuilder { /** * Sets the method used to compress payloads. If unset, compression is disabled. Compression - * method "gzip" and "none" are supported out of the box. Support for additional compression - * methods is available by implementing {@link Compressor} and {@link CompressorProvider}. + * method "gzip" and "none" are supported out of the box. Additional compression methods can be + * supported by providing custom {@link Compressor} implementations via the service loader. */ public OtlpHttpLogRecordExporterBuilder setCompression(String compressionMethod) { requireNonNull(compressionMethod, "compressionMethod"); - Compressor compressor = CompressorUtil.validateAndResolveCompressor(compressionMethod); - delegate.setCompression(compressor); + delegate.setCompression(compressionMethod); return this; } @@ -245,13 +243,20 @@ public final class OtlpHttpLogRecordExporterBuilder { } /** - * Set the {@link ClassLoader} used to load the sender API. + * Set the {@link ClassLoader} used to load the sender API. Variant of {@link + * #setComponentLoader(ComponentLoader)}. * * @since 1.48.0 */ public OtlpHttpLogRecordExporterBuilder setServiceClassLoader(ClassLoader serviceClassLoader) { requireNonNull(serviceClassLoader, "serviceClassLoader"); - delegate.setServiceClassLoader(serviceClassLoader); + return setComponentLoader(ComponentLoader.forClassLoader(serviceClassLoader)); + } + + /** Set the {@link ComponentLoader} used to load the sender API. */ + public OtlpHttpLogRecordExporterBuilder setComponentLoader(ComponentLoader componentLoader) { + requireNonNull(componentLoader, "componentLoader"); + delegate.setComponentLoader(componentLoader); return this; } diff --git a/exporters/otlp/all/src/main/java/io/opentelemetry/exporter/otlp/http/metrics/OtlpHttpMetricExporterBuilder.java b/exporters/otlp/all/src/main/java/io/opentelemetry/exporter/otlp/http/metrics/OtlpHttpMetricExporterBuilder.java index 3d6e6b97e5..07f24dac91 100644 --- a/exporters/otlp/all/src/main/java/io/opentelemetry/exporter/otlp/http/metrics/OtlpHttpMetricExporterBuilder.java +++ b/exporters/otlp/all/src/main/java/io/opentelemetry/exporter/otlp/http/metrics/OtlpHttpMetricExporterBuilder.java @@ -10,9 +10,8 @@ import static java.util.Objects.requireNonNull; import io.opentelemetry.api.GlobalOpenTelemetry; import io.opentelemetry.api.metrics.MeterProvider; +import io.opentelemetry.common.ComponentLoader; import io.opentelemetry.exporter.internal.compression.Compressor; -import io.opentelemetry.exporter.internal.compression.CompressorProvider; -import io.opentelemetry.exporter.internal.compression.CompressorUtil; import io.opentelemetry.exporter.internal.http.HttpExporterBuilder; import io.opentelemetry.exporter.internal.marshal.Marshaler; import io.opentelemetry.exporter.otlp.internal.OtlpUserAgent; @@ -132,13 +131,12 @@ public final class OtlpHttpMetricExporterBuilder { /** * Sets the method used to compress payloads. If unset, compression is disabled. Compression - * method "gzip" and "none" are supported out of the box. Support for additional compression - * methods is available by implementing {@link Compressor} and {@link CompressorProvider}. + * method "gzip" and "none" are supported out of the box. Additional compression methods can be + * supported by providing custom {@link Compressor} implementations via the service loader. */ public OtlpHttpMetricExporterBuilder setCompression(String compressionMethod) { requireNonNull(compressionMethod, "compressionMethod"); - Compressor compressor = CompressorUtil.validateAndResolveCompressor(compressionMethod); - delegate.setCompression(compressor); + delegate.setCompression(compressionMethod); return this; } @@ -304,13 +302,20 @@ public final class OtlpHttpMetricExporterBuilder { } /** - * Set the {@link ClassLoader} used to load the sender API. + * Set the {@link ClassLoader} used to load the sender API. Variant of {@link + * #setComponentLoader(ComponentLoader)}. * * @since 1.48.0 */ public OtlpHttpMetricExporterBuilder setServiceClassLoader(ClassLoader serviceClassLoader) { requireNonNull(serviceClassLoader, "serviceClassLoader"); - delegate.setServiceClassLoader(serviceClassLoader); + return setComponentLoader(ComponentLoader.forClassLoader(serviceClassLoader)); + } + + /** Set the {@link ComponentLoader} used to load the sender API. */ + public OtlpHttpMetricExporterBuilder setComponentLoader(ComponentLoader componentLoader) { + requireNonNull(componentLoader, "componentLoader"); + delegate.setComponentLoader(componentLoader); return this; } diff --git a/exporters/otlp/all/src/main/java/io/opentelemetry/exporter/otlp/http/trace/OtlpHttpSpanExporterBuilder.java b/exporters/otlp/all/src/main/java/io/opentelemetry/exporter/otlp/http/trace/OtlpHttpSpanExporterBuilder.java index 4c2737845d..f8289010b5 100644 --- a/exporters/otlp/all/src/main/java/io/opentelemetry/exporter/otlp/http/trace/OtlpHttpSpanExporterBuilder.java +++ b/exporters/otlp/all/src/main/java/io/opentelemetry/exporter/otlp/http/trace/OtlpHttpSpanExporterBuilder.java @@ -10,9 +10,8 @@ import static java.util.Objects.requireNonNull; import io.opentelemetry.api.GlobalOpenTelemetry; import io.opentelemetry.api.metrics.MeterProvider; +import io.opentelemetry.common.ComponentLoader; import io.opentelemetry.exporter.internal.compression.Compressor; -import io.opentelemetry.exporter.internal.compression.CompressorProvider; -import io.opentelemetry.exporter.internal.compression.CompressorUtil; import io.opentelemetry.exporter.internal.http.HttpExporterBuilder; import io.opentelemetry.exporter.internal.marshal.Marshaler; import io.opentelemetry.exporter.otlp.internal.OtlpUserAgent; @@ -112,13 +111,12 @@ public final class OtlpHttpSpanExporterBuilder { /** * Sets the method used to compress payloads. If unset, compression is disabled. Compression - * method "gzip" and "none" are supported out of the box. Support for additional compression - * methods is available by implementing {@link Compressor} and {@link CompressorProvider}. + * method "gzip" and "none" are supported out of the box. Additional compression methods can be + * supported by providing custom {@link Compressor} implementations via the service loader. */ public OtlpHttpSpanExporterBuilder setCompression(String compressionMethod) { requireNonNull(compressionMethod, "compressionMethod"); - Compressor compressor = CompressorUtil.validateAndResolveCompressor(compressionMethod); - delegate.setCompression(compressor); + delegate.setCompression(compressionMethod); return this; } @@ -246,13 +244,20 @@ public final class OtlpHttpSpanExporterBuilder { } /** - * Set the {@link ClassLoader} used to load the sender API. + * Set the {@link ClassLoader} used to load the sender API. Variant of {@link + * #setComponentLoader(ComponentLoader)}. * * @since 1.48.0 */ public OtlpHttpSpanExporterBuilder setServiceClassLoader(ClassLoader serviceClassLoader) { requireNonNull(serviceClassLoader, "serviceClassLoader"); - delegate.setServiceClassLoader(serviceClassLoader); + return setComponentLoader(ComponentLoader.forClassLoader(serviceClassLoader)); + } + + /** Set the {@link ComponentLoader} used to load the sender API. */ + public OtlpHttpSpanExporterBuilder setComponentLoader(ComponentLoader componentLoader) { + requireNonNull(componentLoader, "componentLoader"); + delegate.setComponentLoader(componentLoader); return this; } diff --git a/exporters/otlp/all/src/main/java/io/opentelemetry/exporter/otlp/internal/OtlpConfigUtil.java b/exporters/otlp/all/src/main/java/io/opentelemetry/exporter/otlp/internal/OtlpConfigUtil.java index 8bfcb4b396..427d79e7ac 100644 --- a/exporters/otlp/all/src/main/java/io/opentelemetry/exporter/otlp/internal/OtlpConfigUtil.java +++ b/exporters/otlp/all/src/main/java/io/opentelemetry/exporter/otlp/internal/OtlpConfigUtil.java @@ -5,6 +5,7 @@ package io.opentelemetry.exporter.otlp.internal; +import io.opentelemetry.common.ComponentLoader; import io.opentelemetry.exporter.internal.ExporterBuilderUtil; import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; import io.opentelemetry.sdk.autoconfigure.spi.ConfigurationException; @@ -53,6 +54,7 @@ public final class OtlpConfigUtil { public static void configureOtlpExporterBuilder( String dataType, ConfigProperties config, + Consumer setComponentLoader, Consumer setEndpoint, BiConsumer addHeader, Consumer setCompression, @@ -61,6 +63,8 @@ public final class OtlpConfigUtil { BiConsumer setClientTls, Consumer setRetryPolicy, Consumer setMemoryMode) { + setComponentLoader.accept(config.getComponentLoader()); + String protocol = getOtlpProtocol(dataType, config); boolean isHttpProtobuf = protocol.equals(PROTOCOL_HTTP_PROTOBUF); URL endpoint = diff --git a/exporters/otlp/all/src/main/java/io/opentelemetry/exporter/otlp/internal/OtlpDeclarativeConfigUtil.java b/exporters/otlp/all/src/main/java/io/opentelemetry/exporter/otlp/internal/OtlpDeclarativeConfigUtil.java index dfcd6740bf..b2f174ae3d 100644 --- a/exporters/otlp/all/src/main/java/io/opentelemetry/exporter/otlp/internal/OtlpDeclarativeConfigUtil.java +++ b/exporters/otlp/all/src/main/java/io/opentelemetry/exporter/otlp/internal/OtlpDeclarativeConfigUtil.java @@ -11,6 +11,7 @@ import static io.opentelemetry.exporter.otlp.internal.OtlpConfigUtil.readFileByt import static io.opentelemetry.exporter.otlp.internal.OtlpConfigUtil.validateEndpoint; import io.opentelemetry.api.incubator.config.DeclarativeConfigProperties; +import io.opentelemetry.common.ComponentLoader; import io.opentelemetry.exporter.internal.IncubatingExporterBuilderUtil; import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; import io.opentelemetry.sdk.autoconfigure.spi.ConfigurationException; @@ -47,6 +48,7 @@ public final class OtlpDeclarativeConfigUtil { public static void configureOtlpExporterBuilder( String dataType, DeclarativeConfigProperties config, + Consumer setComponentLoader, Consumer setEndpoint, BiConsumer addHeader, Consumer setCompression, @@ -56,6 +58,8 @@ public final class OtlpDeclarativeConfigUtil { Consumer setRetryPolicy, Consumer setMemoryMode, boolean isHttpProtobuf) { + setComponentLoader.accept(config.getComponentLoader()); + URL endpoint = validateEndpoint(config.getString("endpoint"), isHttpProtobuf); if (endpoint != null) { setEndpoint.accept(endpoint.toString()); diff --git a/exporters/otlp/all/src/main/java/io/opentelemetry/exporter/otlp/internal/OtlpGrpcLogRecordExporterComponentProvider.java b/exporters/otlp/all/src/main/java/io/opentelemetry/exporter/otlp/internal/OtlpGrpcLogRecordExporterComponentProvider.java index 08b6b65d30..f8faab7a00 100644 --- a/exporters/otlp/all/src/main/java/io/opentelemetry/exporter/otlp/internal/OtlpGrpcLogRecordExporterComponentProvider.java +++ b/exporters/otlp/all/src/main/java/io/opentelemetry/exporter/otlp/internal/OtlpGrpcLogRecordExporterComponentProvider.java @@ -39,6 +39,7 @@ public class OtlpGrpcLogRecordExporterComponentProvider OtlpDeclarativeConfigUtil.configureOtlpExporterBuilder( DATA_TYPE_LOGS, config, + builder::setComponentLoader, builder::setEndpoint, builder::addHeader, builder::setCompression, diff --git a/exporters/otlp/all/src/main/java/io/opentelemetry/exporter/otlp/internal/OtlpGrpcMetricExporterComponentProvider.java b/exporters/otlp/all/src/main/java/io/opentelemetry/exporter/otlp/internal/OtlpGrpcMetricExporterComponentProvider.java index f55ab3795c..25f8341fb0 100644 --- a/exporters/otlp/all/src/main/java/io/opentelemetry/exporter/otlp/internal/OtlpGrpcMetricExporterComponentProvider.java +++ b/exporters/otlp/all/src/main/java/io/opentelemetry/exporter/otlp/internal/OtlpGrpcMetricExporterComponentProvider.java @@ -39,6 +39,7 @@ public class OtlpGrpcMetricExporterComponentProvider implements ComponentProvide OtlpDeclarativeConfigUtil.configureOtlpExporterBuilder( DATA_TYPE_METRICS, config, + builder::setComponentLoader, builder::setEndpoint, builder::addHeader, builder::setCompression, diff --git a/exporters/otlp/all/src/main/java/io/opentelemetry/exporter/otlp/internal/OtlpGrpcSpanExporterComponentProvider.java b/exporters/otlp/all/src/main/java/io/opentelemetry/exporter/otlp/internal/OtlpGrpcSpanExporterComponentProvider.java index c84afce9e6..84e230ca51 100644 --- a/exporters/otlp/all/src/main/java/io/opentelemetry/exporter/otlp/internal/OtlpGrpcSpanExporterComponentProvider.java +++ b/exporters/otlp/all/src/main/java/io/opentelemetry/exporter/otlp/internal/OtlpGrpcSpanExporterComponentProvider.java @@ -38,6 +38,7 @@ public class OtlpGrpcSpanExporterComponentProvider implements ComponentProvider< OtlpDeclarativeConfigUtil.configureOtlpExporterBuilder( DATA_TYPE_TRACES, config, + builder::setComponentLoader, builder::setEndpoint, builder::addHeader, builder::setCompression, diff --git a/exporters/otlp/all/src/main/java/io/opentelemetry/exporter/otlp/internal/OtlpHttpLogRecordExporterComponentProvider.java b/exporters/otlp/all/src/main/java/io/opentelemetry/exporter/otlp/internal/OtlpHttpLogRecordExporterComponentProvider.java index eb9143b181..e6a513e898 100644 --- a/exporters/otlp/all/src/main/java/io/opentelemetry/exporter/otlp/internal/OtlpHttpLogRecordExporterComponentProvider.java +++ b/exporters/otlp/all/src/main/java/io/opentelemetry/exporter/otlp/internal/OtlpHttpLogRecordExporterComponentProvider.java @@ -39,6 +39,7 @@ public class OtlpHttpLogRecordExporterComponentProvider OtlpDeclarativeConfigUtil.configureOtlpExporterBuilder( DATA_TYPE_LOGS, config, + builder::setComponentLoader, builder::setEndpoint, builder::addHeader, builder::setCompression, diff --git a/exporters/otlp/all/src/main/java/io/opentelemetry/exporter/otlp/internal/OtlpHttpMetricExporterComponentProvider.java b/exporters/otlp/all/src/main/java/io/opentelemetry/exporter/otlp/internal/OtlpHttpMetricExporterComponentProvider.java index f22059d9a7..d6cc1bf62d 100644 --- a/exporters/otlp/all/src/main/java/io/opentelemetry/exporter/otlp/internal/OtlpHttpMetricExporterComponentProvider.java +++ b/exporters/otlp/all/src/main/java/io/opentelemetry/exporter/otlp/internal/OtlpHttpMetricExporterComponentProvider.java @@ -39,6 +39,7 @@ public class OtlpHttpMetricExporterComponentProvider implements ComponentProvide OtlpDeclarativeConfigUtil.configureOtlpExporterBuilder( DATA_TYPE_METRICS, config, + builder::setComponentLoader, builder::setEndpoint, builder::addHeader, builder::setCompression, diff --git a/exporters/otlp/all/src/main/java/io/opentelemetry/exporter/otlp/internal/OtlpHttpSpanExporterComponentProvider.java b/exporters/otlp/all/src/main/java/io/opentelemetry/exporter/otlp/internal/OtlpHttpSpanExporterComponentProvider.java index 2c9b876979..752e9698dc 100644 --- a/exporters/otlp/all/src/main/java/io/opentelemetry/exporter/otlp/internal/OtlpHttpSpanExporterComponentProvider.java +++ b/exporters/otlp/all/src/main/java/io/opentelemetry/exporter/otlp/internal/OtlpHttpSpanExporterComponentProvider.java @@ -38,6 +38,7 @@ public class OtlpHttpSpanExporterComponentProvider implements ComponentProvider< OtlpDeclarativeConfigUtil.configureOtlpExporterBuilder( DATA_TYPE_TRACES, config, + builder::setComponentLoader, builder::setEndpoint, builder::addHeader, builder::setCompression, diff --git a/exporters/otlp/all/src/main/java/io/opentelemetry/exporter/otlp/internal/OtlpLogRecordExporterProvider.java b/exporters/otlp/all/src/main/java/io/opentelemetry/exporter/otlp/internal/OtlpLogRecordExporterProvider.java index 67ed44c754..1262bc7250 100644 --- a/exporters/otlp/all/src/main/java/io/opentelemetry/exporter/otlp/internal/OtlpLogRecordExporterProvider.java +++ b/exporters/otlp/all/src/main/java/io/opentelemetry/exporter/otlp/internal/OtlpLogRecordExporterProvider.java @@ -45,6 +45,7 @@ public class OtlpLogRecordExporterProvider OtlpConfigUtil.configureOtlpExporterBuilder( DATA_TYPE_LOGS, config, + builder::setComponentLoader, builder::setEndpoint, builder::addHeader, builder::setCompression, @@ -62,6 +63,7 @@ public class OtlpLogRecordExporterProvider OtlpConfigUtil.configureOtlpExporterBuilder( DATA_TYPE_LOGS, config, + builder::setComponentLoader, builder::setEndpoint, builder::addHeader, builder::setCompression, diff --git a/exporters/otlp/all/src/main/java/io/opentelemetry/exporter/otlp/internal/OtlpMetricExporterProvider.java b/exporters/otlp/all/src/main/java/io/opentelemetry/exporter/otlp/internal/OtlpMetricExporterProvider.java index a60f57a250..ebfaaacf29 100644 --- a/exporters/otlp/all/src/main/java/io/opentelemetry/exporter/otlp/internal/OtlpMetricExporterProvider.java +++ b/exporters/otlp/all/src/main/java/io/opentelemetry/exporter/otlp/internal/OtlpMetricExporterProvider.java @@ -38,6 +38,7 @@ public class OtlpMetricExporterProvider implements ConfigurableMetricExporterPro OtlpConfigUtil.configureOtlpExporterBuilder( DATA_TYPE_METRICS, config, + builder::setComponentLoader, builder::setEndpoint, builder::addHeader, builder::setCompression, @@ -58,6 +59,7 @@ public class OtlpMetricExporterProvider implements ConfigurableMetricExporterPro OtlpConfigUtil.configureOtlpExporterBuilder( DATA_TYPE_METRICS, config, + builder::setComponentLoader, builder::setEndpoint, builder::addHeader, builder::setCompression, diff --git a/exporters/otlp/all/src/main/java/io/opentelemetry/exporter/otlp/internal/OtlpSpanExporterProvider.java b/exporters/otlp/all/src/main/java/io/opentelemetry/exporter/otlp/internal/OtlpSpanExporterProvider.java index e6fad237d8..1561c057b2 100644 --- a/exporters/otlp/all/src/main/java/io/opentelemetry/exporter/otlp/internal/OtlpSpanExporterProvider.java +++ b/exporters/otlp/all/src/main/java/io/opentelemetry/exporter/otlp/internal/OtlpSpanExporterProvider.java @@ -44,6 +44,7 @@ public class OtlpSpanExporterProvider OtlpConfigUtil.configureOtlpExporterBuilder( DATA_TYPE_TRACES, config, + builder::setComponentLoader, builder::setEndpoint, builder::addHeader, builder::setCompression, @@ -61,6 +62,7 @@ public class OtlpSpanExporterProvider OtlpConfigUtil.configureOtlpExporterBuilder( DATA_TYPE_TRACES, config, + builder::setComponentLoader, builder::setEndpoint, builder::addHeader, builder::setCompression, diff --git a/exporters/otlp/all/src/main/java/io/opentelemetry/exporter/otlp/logs/OtlpGrpcLogRecordExporterBuilder.java b/exporters/otlp/all/src/main/java/io/opentelemetry/exporter/otlp/logs/OtlpGrpcLogRecordExporterBuilder.java index dbec072655..467aa0cd6a 100644 --- a/exporters/otlp/all/src/main/java/io/opentelemetry/exporter/otlp/logs/OtlpGrpcLogRecordExporterBuilder.java +++ b/exporters/otlp/all/src/main/java/io/opentelemetry/exporter/otlp/logs/OtlpGrpcLogRecordExporterBuilder.java @@ -11,9 +11,8 @@ import static java.util.Objects.requireNonNull; import io.grpc.ManagedChannel; import io.opentelemetry.api.GlobalOpenTelemetry; import io.opentelemetry.api.metrics.MeterProvider; +import io.opentelemetry.common.ComponentLoader; import io.opentelemetry.exporter.internal.compression.Compressor; -import io.opentelemetry.exporter.internal.compression.CompressorProvider; -import io.opentelemetry.exporter.internal.compression.CompressorUtil; import io.opentelemetry.exporter.internal.grpc.GrpcExporterBuilder; import io.opentelemetry.exporter.internal.marshal.Marshaler; import io.opentelemetry.exporter.otlp.internal.OtlpUserAgent; @@ -145,13 +144,12 @@ public final class OtlpGrpcLogRecordExporterBuilder { /** * Sets the method used to compress payloads. If unset, compression is disabled. Compression - * method "gzip" and "none" are supported out of the box. Support for additional compression - * methods is available by implementing {@link Compressor} and {@link CompressorProvider}. + * method "gzip" and "none" are supported out of the box. Additional compression methods can be + * supported by providing custom {@link Compressor} implementations via the service loader. */ public OtlpGrpcLogRecordExporterBuilder setCompression(String compressionMethod) { requireNonNull(compressionMethod, "compressionMethod"); - Compressor compressor = CompressorUtil.validateAndResolveCompressor(compressionMethod); - delegate.setCompression(compressor); + delegate.setCompression(compressionMethod); return this; } @@ -274,13 +272,20 @@ public final class OtlpGrpcLogRecordExporterBuilder { } /** - * Set the {@link ClassLoader} used to load the sender API. + * Set the {@link ClassLoader} used to load the sender API. Variant of {@link + * #setComponentLoader(ComponentLoader)}. * * @since 1.48.0 */ public OtlpGrpcLogRecordExporterBuilder setServiceClassLoader(ClassLoader serviceClassLoader) { requireNonNull(serviceClassLoader, "serviceClassLoader"); - delegate.setServiceClassLoader(serviceClassLoader); + return setComponentLoader(ComponentLoader.forClassLoader(serviceClassLoader)); + } + + /** Set the {@link ComponentLoader} used to load the sender API. */ + public OtlpGrpcLogRecordExporterBuilder setComponentLoader(ComponentLoader componentLoader) { + requireNonNull(componentLoader, "componentLoader"); + delegate.setComponentLoader(componentLoader); return this; } diff --git a/exporters/otlp/all/src/main/java/io/opentelemetry/exporter/otlp/metrics/OtlpGrpcMetricExporterBuilder.java b/exporters/otlp/all/src/main/java/io/opentelemetry/exporter/otlp/metrics/OtlpGrpcMetricExporterBuilder.java index 8a56a8188a..ef3403c735 100644 --- a/exporters/otlp/all/src/main/java/io/opentelemetry/exporter/otlp/metrics/OtlpGrpcMetricExporterBuilder.java +++ b/exporters/otlp/all/src/main/java/io/opentelemetry/exporter/otlp/metrics/OtlpGrpcMetricExporterBuilder.java @@ -11,9 +11,8 @@ import static java.util.Objects.requireNonNull; import io.grpc.ManagedChannel; import io.opentelemetry.api.GlobalOpenTelemetry; import io.opentelemetry.api.metrics.MeterProvider; +import io.opentelemetry.common.ComponentLoader; import io.opentelemetry.exporter.internal.compression.Compressor; -import io.opentelemetry.exporter.internal.compression.CompressorProvider; -import io.opentelemetry.exporter.internal.compression.CompressorUtil; import io.opentelemetry.exporter.internal.grpc.GrpcExporterBuilder; import io.opentelemetry.exporter.internal.marshal.Marshaler; import io.opentelemetry.exporter.otlp.internal.OtlpUserAgent; @@ -164,13 +163,12 @@ public final class OtlpGrpcMetricExporterBuilder { /** * Sets the method used to compress payloads. If unset, compression is disabled. Compression - * method "gzip" and "none" are supported out of the box. Support for additional compression - * methods is available by implementing {@link Compressor} and {@link CompressorProvider}. + * method "gzip" and "none" are supported out of the box. Additional compression methods can be + * supported by providing custom {@link Compressor} implementations via the service loader. */ public OtlpGrpcMetricExporterBuilder setCompression(String compressionMethod) { requireNonNull(compressionMethod, "compressionMethod"); - Compressor compressor = CompressorUtil.validateAndResolveCompressor(compressionMethod); - delegate.setCompression(compressor); + delegate.setCompression(compressionMethod); return this; } @@ -332,13 +330,20 @@ public final class OtlpGrpcMetricExporterBuilder { } /** - * Set the {@link ClassLoader} used to load the sender API. + * Set the {@link ClassLoader} used to load the sender API. Variant of {@link + * #setComponentLoader(ComponentLoader)}. * * @since 1.48.0 */ public OtlpGrpcMetricExporterBuilder setServiceClassLoader(ClassLoader serviceClassLoader) { requireNonNull(serviceClassLoader, "serviceClassLoader"); - delegate.setServiceClassLoader(serviceClassLoader); + return setComponentLoader(ComponentLoader.forClassLoader(serviceClassLoader)); + } + + /** Set the {@link ComponentLoader} used to load the sender API. */ + public OtlpGrpcMetricExporterBuilder setComponentLoader(ComponentLoader componentLoader) { + requireNonNull(componentLoader, "componentLoader"); + delegate.setComponentLoader(componentLoader); return this; } diff --git a/exporters/otlp/all/src/main/java/io/opentelemetry/exporter/otlp/trace/OtlpGrpcSpanExporterBuilder.java b/exporters/otlp/all/src/main/java/io/opentelemetry/exporter/otlp/trace/OtlpGrpcSpanExporterBuilder.java index 8b0c8c1f9f..fa7523b5b9 100644 --- a/exporters/otlp/all/src/main/java/io/opentelemetry/exporter/otlp/trace/OtlpGrpcSpanExporterBuilder.java +++ b/exporters/otlp/all/src/main/java/io/opentelemetry/exporter/otlp/trace/OtlpGrpcSpanExporterBuilder.java @@ -11,9 +11,8 @@ import static java.util.Objects.requireNonNull; import io.grpc.ManagedChannel; import io.opentelemetry.api.GlobalOpenTelemetry; import io.opentelemetry.api.metrics.MeterProvider; +import io.opentelemetry.common.ComponentLoader; import io.opentelemetry.exporter.internal.compression.Compressor; -import io.opentelemetry.exporter.internal.compression.CompressorProvider; -import io.opentelemetry.exporter.internal.compression.CompressorUtil; import io.opentelemetry.exporter.internal.grpc.GrpcExporterBuilder; import io.opentelemetry.exporter.internal.marshal.Marshaler; import io.opentelemetry.exporter.otlp.internal.OtlpUserAgent; @@ -141,13 +140,12 @@ public final class OtlpGrpcSpanExporterBuilder { /** * Sets the method used to compress payloads. If unset, compression is disabled. Compression - * method "gzip" and "none" are supported out of the box. Support for additional compression - * methods is available by implementing {@link Compressor} and {@link CompressorProvider}. + * method "gzip" and "none" are supported out of the box. Additional compression methods can be + * supported by providing custom {@link Compressor} implementations via the service loader. */ public OtlpGrpcSpanExporterBuilder setCompression(String compressionMethod) { requireNonNull(compressionMethod, "compressionMethod"); - Compressor compressor = CompressorUtil.validateAndResolveCompressor(compressionMethod); - delegate.setCompression(compressor); + delegate.setCompression(compressionMethod); return this; } @@ -271,13 +269,20 @@ public final class OtlpGrpcSpanExporterBuilder { } /** - * Set the {@link ClassLoader} used to load the sender API. + * Set the {@link ClassLoader} used to load the sender API. Variant of {@link + * #setComponentLoader(ComponentLoader)}. * * @since 1.48.0 */ public OtlpGrpcSpanExporterBuilder setServiceClassLoader(ClassLoader serviceClassLoader) { requireNonNull(serviceClassLoader, "serviceClassLoader"); - delegate.setServiceClassLoader(serviceClassLoader); + return setComponentLoader(ComponentLoader.forClassLoader(serviceClassLoader)); + } + + /** Set the {@link ComponentLoader} used to load the sender API. */ + public OtlpGrpcSpanExporterBuilder setComponentLoader(ComponentLoader componentLoader) { + requireNonNull(componentLoader, "componentLoader"); + delegate.setComponentLoader(componentLoader); return this; } diff --git a/exporters/otlp/all/src/test/java/io/opentelemetry/exporter/otlp/internal/OtlpConfigUtilTest.java b/exporters/otlp/all/src/test/java/io/opentelemetry/exporter/otlp/internal/OtlpConfigUtilTest.java index 5716d604ec..fb1061ae3f 100644 --- a/exporters/otlp/all/src/test/java/io/opentelemetry/exporter/otlp/internal/OtlpConfigUtilTest.java +++ b/exporters/otlp/all/src/test/java/io/opentelemetry/exporter/otlp/internal/OtlpConfigUtilTest.java @@ -371,6 +371,7 @@ class OtlpConfigUtilTest { OtlpConfigUtil.configureOtlpExporterBuilder( dataType, DefaultConfigProperties.createFromMap(properties), + value -> {}, endpoint::set, (value1, value2) -> {}, value -> {}, diff --git a/exporters/otlp/all/src/test/java/io/opentelemetry/exporter/otlp/internal/OtlpLogRecordExporterProviderTest.java b/exporters/otlp/all/src/test/java/io/opentelemetry/exporter/otlp/internal/OtlpLogRecordExporterProviderTest.java index 47ed7859cd..9fe5bad768 100644 --- a/exporters/otlp/all/src/test/java/io/opentelemetry/exporter/otlp/internal/OtlpLogRecordExporterProviderTest.java +++ b/exporters/otlp/all/src/test/java/io/opentelemetry/exporter/otlp/internal/OtlpLogRecordExporterProviderTest.java @@ -122,6 +122,7 @@ class OtlpLogRecordExporterProviderTest { provider.createExporter(DefaultConfigProperties.createFromMap(Collections.emptyMap()))) { assertThat(exporter).isInstanceOf(OtlpGrpcLogRecordExporter.class); verify(grpcBuilder, times(1)).build(); + verify(grpcBuilder).setComponentLoader(any()); verify(grpcBuilder, never()).setEndpoint(any()); verify(grpcBuilder, never()).addHeader(any(), any()); verify(grpcBuilder, never()).setCompression(any()); @@ -154,6 +155,7 @@ class OtlpLogRecordExporterProviderTest { provider.createExporter(DefaultConfigProperties.createFromMap(config))) { assertThat(exporter).isInstanceOf(OtlpGrpcLogRecordExporter.class); verify(grpcBuilder, times(1)).build(); + verify(grpcBuilder).setComponentLoader(any()); verify(grpcBuilder).setEndpoint("https://localhost:443/"); verify(grpcBuilder).addHeader("header-key", "header-value"); verify(grpcBuilder).setCompression("gzip"); @@ -189,6 +191,7 @@ class OtlpLogRecordExporterProviderTest { provider.createExporter(DefaultConfigProperties.createFromMap(config))) { assertThat(exporter).isInstanceOf(OtlpGrpcLogRecordExporter.class); verify(grpcBuilder, times(1)).build(); + verify(grpcBuilder).setComponentLoader(any()); verify(grpcBuilder).setEndpoint("https://localhost:443/"); verify(grpcBuilder).addHeader("header-key", "header-value"); verify(grpcBuilder).setCompression("gzip"); diff --git a/exporters/otlp/all/src/test/java/io/opentelemetry/exporter/otlp/internal/OtlpMetricExporterProviderTest.java b/exporters/otlp/all/src/test/java/io/opentelemetry/exporter/otlp/internal/OtlpMetricExporterProviderTest.java index 3d0a409832..e028c6a957 100644 --- a/exporters/otlp/all/src/test/java/io/opentelemetry/exporter/otlp/internal/OtlpMetricExporterProviderTest.java +++ b/exporters/otlp/all/src/test/java/io/opentelemetry/exporter/otlp/internal/OtlpMetricExporterProviderTest.java @@ -121,6 +121,7 @@ class OtlpMetricExporterProviderTest { provider.createExporter(DefaultConfigProperties.createFromMap(Collections.emptyMap()))) { assertThat(exporter).isInstanceOf(OtlpGrpcMetricExporter.class); verify(grpcBuilder, times(1)).build(); + verify(grpcBuilder).setComponentLoader(any()); verify(grpcBuilder, never()).setEndpoint(any()); verify(grpcBuilder, never()).addHeader(any(), any()); verify(grpcBuilder, never()).setCompression(any()); @@ -149,6 +150,7 @@ class OtlpMetricExporterProviderTest { provider.createExporter(DefaultConfigProperties.createFromMap(config))) { assertThat(exporter).isInstanceOf(OtlpGrpcMetricExporter.class); verify(grpcBuilder, times(1)).build(); + verify(grpcBuilder).setComponentLoader(any()); verify(grpcBuilder).setEndpoint("https://localhost:443/"); verify(grpcBuilder).addHeader("header-key", "header-value"); verify(grpcBuilder).setCompression("gzip"); @@ -184,6 +186,7 @@ class OtlpMetricExporterProviderTest { provider.createExporter(DefaultConfigProperties.createFromMap(config))) { assertThat(exporter).isInstanceOf(OtlpGrpcMetricExporter.class); verify(grpcBuilder, times(1)).build(); + verify(grpcBuilder).setComponentLoader(any()); verify(grpcBuilder).setEndpoint("https://localhost:443/"); verify(grpcBuilder).addHeader("header-key", "header-value"); verify(grpcBuilder).setCompression("gzip"); @@ -205,6 +208,7 @@ class OtlpMetricExporterProviderTest { "otel.exporter.otlp.metrics.protocol", "http/protobuf")))) { assertThat(exporter).isInstanceOf(OtlpHttpMetricExporter.class); verify(httpBuilder, times(1)).build(); + verify(httpBuilder).setComponentLoader(any()); verify(httpBuilder, never()).setEndpoint(any()); verify(httpBuilder, never()).addHeader(any(), any()); verify(httpBuilder, never()).setCompression(any()); @@ -234,6 +238,7 @@ class OtlpMetricExporterProviderTest { provider.createExporter(DefaultConfigProperties.createFromMap(config))) { assertThat(exporter).isInstanceOf(OtlpHttpMetricExporter.class); verify(httpBuilder, times(1)).build(); + verify(httpBuilder).setComponentLoader(any()); verify(httpBuilder).setEndpoint("https://localhost:443/v1/metrics"); verify(httpBuilder).addHeader("header-key", "header-value"); verify(httpBuilder).setCompression("gzip"); @@ -271,6 +276,7 @@ class OtlpMetricExporterProviderTest { provider.createExporter(DefaultConfigProperties.createFromMap(config))) { assertThat(exporter).isInstanceOf(OtlpHttpMetricExporter.class); verify(httpBuilder, times(1)).build(); + verify(httpBuilder).setComponentLoader(any()); verify(httpBuilder).setEndpoint("https://localhost:443/v1/metrics"); verify(httpBuilder).addHeader("header-key", "header-value"); verify(httpBuilder).setCompression("gzip"); diff --git a/exporters/otlp/all/src/test/java/io/opentelemetry/exporter/otlp/internal/OtlpSpanExporterProviderTest.java b/exporters/otlp/all/src/test/java/io/opentelemetry/exporter/otlp/internal/OtlpSpanExporterProviderTest.java index 46d2e8ea21..1454d209da 100644 --- a/exporters/otlp/all/src/test/java/io/opentelemetry/exporter/otlp/internal/OtlpSpanExporterProviderTest.java +++ b/exporters/otlp/all/src/test/java/io/opentelemetry/exporter/otlp/internal/OtlpSpanExporterProviderTest.java @@ -123,6 +123,7 @@ class OtlpSpanExporterProviderTest { provider.createExporter(DefaultConfigProperties.createFromMap(Collections.emptyMap()))) { assertThat(exporter).isInstanceOf(OtlpGrpcSpanExporter.class); verify(grpcBuilder, times(1)).build(); + verify(grpcBuilder).setComponentLoader(any()); verify(grpcBuilder, never()).setEndpoint(any()); verify(grpcBuilder, never()).addHeader(any(), any()); verify(grpcBuilder, never()).setCompression(any()); @@ -155,6 +156,7 @@ class OtlpSpanExporterProviderTest { provider.createExporter(DefaultConfigProperties.createFromMap(config))) { assertThat(exporter).isInstanceOf(OtlpGrpcSpanExporter.class); verify(grpcBuilder, times(1)).build(); + verify(grpcBuilder).setComponentLoader(any()); verify(grpcBuilder).setEndpoint("https://localhost:443/"); verify(grpcBuilder).addHeader("header-key", "header-value"); verify(grpcBuilder).setCompression("gzip"); @@ -190,6 +192,7 @@ class OtlpSpanExporterProviderTest { provider.createExporter(DefaultConfigProperties.createFromMap(config))) { assertThat(exporter).isInstanceOf(OtlpGrpcSpanExporter.class); verify(grpcBuilder, times(1)).build(); + verify(grpcBuilder).setComponentLoader(any()); verify(grpcBuilder).setEndpoint("https://localhost:443/"); verify(grpcBuilder).addHeader("header-key", "header-value"); verify(grpcBuilder).setCompression("gzip"); @@ -210,6 +213,7 @@ class OtlpSpanExporterProviderTest { Collections.singletonMap("otel.exporter.otlp.traces.protocol", "http/protobuf")))) { assertThat(exporter).isInstanceOf(OtlpHttpSpanExporter.class); verify(httpBuilder, times(1)).build(); + verify(httpBuilder).setComponentLoader(any()); verify(httpBuilder, never()).setEndpoint(any()); verify(httpBuilder, never()).addHeader(any(), any()); verify(httpBuilder, never()).setCompression(any()); @@ -240,6 +244,7 @@ class OtlpSpanExporterProviderTest { provider.createExporter(DefaultConfigProperties.createFromMap(config))) { assertThat(exporter).isInstanceOf(OtlpHttpSpanExporter.class); verify(httpBuilder, times(1)).build(); + verify(httpBuilder).setComponentLoader(any()); verify(httpBuilder).setEndpoint("https://localhost:443/v1/traces"); verify(httpBuilder).addHeader("header-key1", "header value1"); verify(httpBuilder).addHeader("header-key2", "header value2"); @@ -279,6 +284,7 @@ class OtlpSpanExporterProviderTest { provider.createExporter(DefaultConfigProperties.createFromMap(config))) { assertThat(exporter).isInstanceOf(OtlpHttpSpanExporter.class); verify(httpBuilder, times(1)).build(); + verify(httpBuilder).setComponentLoader(any()); verify(httpBuilder).setEndpoint("https://localhost:443/v1/traces"); verify(httpBuilder).addHeader("header-key", "header-value"); verify(httpBuilder).setCompression("gzip"); diff --git a/exporters/otlp/profiles/src/main/java/io/opentelemetry/exporter/otlp/profiles/OtlpGrpcProfilesExporterBuilder.java b/exporters/otlp/profiles/src/main/java/io/opentelemetry/exporter/otlp/profiles/OtlpGrpcProfilesExporterBuilder.java index 91d34b00e6..18e19ca51b 100644 --- a/exporters/otlp/profiles/src/main/java/io/opentelemetry/exporter/otlp/profiles/OtlpGrpcProfilesExporterBuilder.java +++ b/exporters/otlp/profiles/src/main/java/io/opentelemetry/exporter/otlp/profiles/OtlpGrpcProfilesExporterBuilder.java @@ -10,9 +10,8 @@ import static java.util.Objects.requireNonNull; import io.grpc.ManagedChannel; import io.opentelemetry.api.metrics.MeterProvider; +import io.opentelemetry.common.ComponentLoader; import io.opentelemetry.exporter.internal.compression.Compressor; -import io.opentelemetry.exporter.internal.compression.CompressorProvider; -import io.opentelemetry.exporter.internal.compression.CompressorUtil; import io.opentelemetry.exporter.internal.grpc.GrpcExporterBuilder; import io.opentelemetry.exporter.internal.marshal.Marshaler; import io.opentelemetry.exporter.otlp.internal.OtlpUserAgent; @@ -134,13 +133,12 @@ public final class OtlpGrpcProfilesExporterBuilder { /** * Sets the method used to compress payloads. If unset, compression is disabled. Compression - * method "gzip" and "none" are supported out of the box. Support for additional compression - * methods is available by implementing {@link Compressor} and {@link CompressorProvider}. + * method "gzip" and "none" are supported out of the box. Additional compression methods can be + * supported by providing custom {@link Compressor} implementations via the service loader. */ public OtlpGrpcProfilesExporterBuilder setCompression(String compressionMethod) { requireNonNull(compressionMethod, "compressionMethod"); - Compressor compressor = CompressorUtil.validateAndResolveCompressor(compressionMethod); - delegate.setCompression(compressor); + delegate.setCompression(compressionMethod); return this; } @@ -204,10 +202,19 @@ public final class OtlpGrpcProfilesExporterBuilder { return this; } - /** Set the {@link ClassLoader} used to load the sender API. */ + /** + * Set the {@link ClassLoader} used to load the sender API. Variant of {@link + * #setComponentLoader(ComponentLoader)}. + */ public OtlpGrpcProfilesExporterBuilder setServiceClassLoader(ClassLoader serviceClassLoader) { requireNonNull(serviceClassLoader, "serviceClassLoader"); - delegate.setServiceClassLoader(serviceClassLoader); + return setComponentLoader(ComponentLoader.forClassLoader(serviceClassLoader)); + } + + /** Set the {@link ComponentLoader} used to load the sender API. */ + public OtlpGrpcProfilesExporterBuilder setComponentLoader(ComponentLoader componentLoader) { + requireNonNull(componentLoader, "componentLoader"); + delegate.setComponentLoader(componentLoader); return this; } diff --git a/exporters/prometheus/src/main/java/io/opentelemetry/exporter/prometheus/Otel2PrometheusConverter.java b/exporters/prometheus/src/main/java/io/opentelemetry/exporter/prometheus/Otel2PrometheusConverter.java index 664ad1bd4c..afa04a8139 100644 --- a/exporters/prometheus/src/main/java/io/opentelemetry/exporter/prometheus/Otel2PrometheusConverter.java +++ b/exporters/prometheus/src/main/java/io/opentelemetry/exporter/prometheus/Otel2PrometheusConverter.java @@ -77,6 +77,8 @@ final class Otel2PrometheusConverter { private static final ThrottlingLogger THROTTLING_LOGGER = new ThrottlingLogger(LOGGER); private static final String OTEL_SCOPE_NAME = "otel_scope_name"; private static final String OTEL_SCOPE_VERSION = "otel_scope_version"; + private static final String OTEL_SCOPE_SCHEMA_URL = "otel_scope_schema_url"; + private static final String OTEL_SCOPE_ATTRIBUTE_PREFIX = "otel_scope_"; private static final long NANOS_PER_MILLISECOND = TimeUnit.MILLISECONDS.toNanos(1); static final int MAX_CACHE_SIZE = 10; @@ -488,6 +490,16 @@ final class Otel2PrometheusConverter { if (scope.getVersion() != null) { labelNameToValue.putIfAbsent(OTEL_SCOPE_VERSION, scope.getVersion()); } + String schemaUrl = scope.getSchemaUrl(); + if (schemaUrl != null) { + labelNameToValue.putIfAbsent(OTEL_SCOPE_SCHEMA_URL, schemaUrl); + } + scope + .getAttributes() + .forEach( + (key, value) -> + labelNameToValue.putIfAbsent( + OTEL_SCOPE_ATTRIBUTE_PREFIX + key.getKey(), value.toString())); } if (resource != null) { diff --git a/exporters/prometheus/src/main/java/io/opentelemetry/exporter/prometheus/PrometheusHttpServer.java b/exporters/prometheus/src/main/java/io/opentelemetry/exporter/prometheus/PrometheusHttpServer.java index 7cd7c2bdb6..e7b28e42ac 100644 --- a/exporters/prometheus/src/main/java/io/opentelemetry/exporter/prometheus/PrometheusHttpServer.java +++ b/exporters/prometheus/src/main/java/io/opentelemetry/exporter/prometheus/PrometheusHttpServer.java @@ -26,6 +26,7 @@ import io.prometheus.metrics.model.registry.PrometheusRegistry; import java.io.IOException; import java.io.UncheckedIOException; import java.net.InetSocketAddress; +import java.util.StringJoiner; import java.util.concurrent.ExecutorService; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ThreadPoolExecutor; @@ -39,13 +40,17 @@ import javax.annotation.Nullable; */ public final class PrometheusHttpServer implements MetricReader { + private final String host; + private final int port; + private final boolean otelScopeEnabled; + @Nullable private final Predicate allowedResourceAttributesFilter; + private final MemoryMode memoryMode; + private final DefaultAggregationSelector defaultAggregationSelector; + private final PrometheusHttpServerBuilder builder; private final HTTPServer httpServer; private final PrometheusMetricReader prometheusMetricReader; private final PrometheusRegistry prometheusRegistry; - private final String host; - private final MemoryMode memoryMode; - private final DefaultAggregationSelector defaultAggregationSelector; /** * Returns a new {@link PrometheusHttpServer} which can be registered to an {@link @@ -73,11 +78,15 @@ public final class PrometheusHttpServer implements MetricReader { @Nullable HttpHandler defaultHandler, DefaultAggregationSelector defaultAggregationSelector, @Nullable Authenticator authenticator) { + this.host = host; + this.port = port; + this.otelScopeEnabled = otelScopeEnabled; + this.allowedResourceAttributesFilter = allowedResourceAttributesFilter; + this.memoryMode = memoryMode; + this.defaultAggregationSelector = defaultAggregationSelector; this.builder = builder; this.prometheusMetricReader = new PrometheusMetricReader(otelScopeEnabled, allowedResourceAttributesFilter); - this.host = host; - this.memoryMode = memoryMode; this.prometheusRegistry = prometheusRegistry; prometheusRegistry.register(prometheusMetricReader); // When memory mode is REUSABLE_DATA, concurrent reads lead to data corruption. To prevent this, @@ -106,7 +115,6 @@ public final class PrometheusHttpServer implements MetricReader { } catch (IOException e) { throw new UncheckedIOException("Could not create Prometheus HTTP server", e); } - this.defaultAggregationSelector = defaultAggregationSelector; } @Override @@ -160,7 +168,16 @@ public final class PrometheusHttpServer implements MetricReader { @Override public String toString() { - return "PrometheusHttpServer{address=" + getAddress() + "}"; + StringJoiner joiner = new StringJoiner(",", "PrometheusHttpServer{", "}"); + joiner.add("host=" + host); + joiner.add("port=" + port); + joiner.add("otelScopeEnabled=" + otelScopeEnabled); + joiner.add("allowedResourceAttributesFilter=" + allowedResourceAttributesFilter); + joiner.add("memoryMode=" + memoryMode); + joiner.add( + "defaultAggregationSelector=" + + DefaultAggregationSelector.asString(defaultAggregationSelector)); + return joiner.toString(); } /** diff --git a/exporters/prometheus/src/main/java/io/opentelemetry/exporter/prometheus/internal/PrometheusComponentProvider.java b/exporters/prometheus/src/main/java/io/opentelemetry/exporter/prometheus/internal/PrometheusComponentProvider.java index 04b8094608..25d069db7a 100644 --- a/exporters/prometheus/src/main/java/io/opentelemetry/exporter/prometheus/internal/PrometheusComponentProvider.java +++ b/exporters/prometheus/src/main/java/io/opentelemetry/exporter/prometheus/internal/PrometheusComponentProvider.java @@ -9,7 +9,9 @@ import io.opentelemetry.api.incubator.config.DeclarativeConfigProperties; import io.opentelemetry.exporter.prometheus.PrometheusHttpServer; import io.opentelemetry.exporter.prometheus.PrometheusHttpServerBuilder; import io.opentelemetry.sdk.autoconfigure.spi.internal.ComponentProvider; +import io.opentelemetry.sdk.internal.IncludeExcludePredicate; import io.opentelemetry.sdk.metrics.export.MetricReader; +import java.util.List; /** * Declarative configuration SPI implementation for {@link PrometheusHttpServer}. @@ -37,11 +39,28 @@ public class PrometheusComponentProvider implements ComponentProvider included = withResourceConstantLabels.getScalarList("included", String.class); + List excluded = withResourceConstantLabels.getScalarList("excluded", String.class); + if (included != null || excluded != null) { + prometheusBuilder.setAllowedResourceAttributesFilter( + IncludeExcludePredicate.createPatternMatching(included, excluded)); + } + } + return prometheusBuilder.build(); } } diff --git a/exporters/prometheus/src/test/java/io/opentelemetry/exporter/prometheus/Otel2PrometheusConverterTest.java b/exporters/prometheus/src/test/java/io/opentelemetry/exporter/prometheus/Otel2PrometheusConverterTest.java index 6395d60c75..4e718c7862 100644 --- a/exporters/prometheus/src/test/java/io/opentelemetry/exporter/prometheus/Otel2PrometheusConverterTest.java +++ b/exporters/prometheus/src/test/java/io/opentelemetry/exporter/prometheus/Otel2PrometheusConverterTest.java @@ -38,7 +38,9 @@ import io.opentelemetry.sdk.metrics.internal.data.ImmutableSummaryData; import io.opentelemetry.sdk.metrics.internal.data.ImmutableSummaryPointData; import io.opentelemetry.sdk.resources.Resource; import io.prometheus.metrics.expositionformats.ExpositionFormats; +import io.prometheus.metrics.model.snapshots.CounterSnapshot; import io.prometheus.metrics.model.snapshots.Labels; +import io.prometheus.metrics.model.snapshots.MetricSnapshot; import io.prometheus.metrics.model.snapshots.MetricSnapshots; import java.io.ByteArrayOutputStream; import java.io.IOException; @@ -47,9 +49,9 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; +import java.util.Optional; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Predicate; -import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -63,7 +65,9 @@ class Otel2PrometheusConverterTest { private static final Pattern PATTERN = Pattern.compile( - "# HELP (?.*)\n# TYPE (?.*)\n(?.*)\\{otel_scope_name=\"scope\"}(.|\\n)*"); + "(.|\\n)*# HELP (?.*)\n# TYPE (?.*)\n(?.*)\\{" + + "otel_scope_foo=\"bar\",otel_scope_name=\"scope\"," + + "otel_scope_schema_url=\"schemaUrl\",otel_scope_version=\"version\"}(.|\\n)*"); private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); private final Otel2PrometheusConverter converter = @@ -79,16 +83,17 @@ class Otel2PrometheusConverterTest { ExpositionFormats.init().getPrometheusTextFormatWriter().write(out, snapshots); String expositionFormat = new String(out.toByteArray(), StandardCharsets.UTF_8); - // Uncomment to debug exposition format output - // System.out.println(expositionFormat); - - Matcher matcher = PATTERN.matcher(expositionFormat); - assertThat(matcher.matches()).isTrue(); - assertThat(matcher.group("help")).isEqualTo(expectedHelp); - assertThat(matcher.group("type")).isEqualTo(expectedType); - // Note: Summaries and histograms produce output which matches METRIC_NAME_PATTERN multiple - // times. The pattern ends up matching against the first. - assertThat(matcher.group("metricName")).isEqualTo(expectedMetricName); + assertThat(expositionFormat) + .matchesSatisfying( + PATTERN, + matcher -> { + assertThat(matcher.group("help")).isEqualTo(expectedHelp); + assertThat(matcher.group("type")).isEqualTo(expectedType); + // Note: Summaries and histograms produce output which matches METRIC_NAME_PATTERN + // multiple + // times. The pattern ends up matching against the first. + assertThat(matcher.group("metricName")).isEqualTo(expectedMetricName); + }); } private static Stream metricMetadataArgs() { @@ -239,7 +244,7 @@ class Otel2PrometheusConverterTest { : "my_metric_units", // "cluster" attribute is added (due to reg expr specified) and only it - "cluster=\"mycluster\",foo1=\"bar1\",foo2=\"bar2\",otel_scope_name=\"scope\"")); + "cluster=\"mycluster\",foo1=\"bar1\",foo2=\"bar2\",otel_scope_foo=\"bar\",otel_scope_name=\"scope\",otel_scope_schema_url=\"schemaUrl\",otel_scope_version=\"version\"")); } // Resource attributes which also exists in the metric labels are not added twice @@ -258,7 +263,7 @@ class Otel2PrometheusConverterTest { // "cluster" attribute is present only once and the value is taken // from the metric attributes and not the resource attributes - "cluster=\"mycluster2\",foo2=\"bar2\",otel_scope_name=\"scope\"")); + "cluster=\"mycluster2\",foo2=\"bar2\",otel_scope_foo=\"bar\",otel_scope_name=\"scope\",otel_scope_schema_url=\"schemaUrl\",otel_scope_version=\"version\"")); // Empty attributes arguments.add( @@ -273,7 +278,7 @@ class Otel2PrometheusConverterTest { stringKey("host"), "localhost", stringKey("cluster"), "mycluster"))), /* allowedResourceAttributesFilter= */ Predicates.startsWith("clu"), "my_metric_units", - "cluster=\"mycluster\",otel_scope_name=\"scope\"")); + "cluster=\"mycluster\",otel_scope_foo=\"bar\",otel_scope_name=\"scope\",otel_scope_schema_url=\"schemaUrl\",otel_scope_version=\"version\"")); return arguments.stream(); } @@ -314,7 +319,11 @@ class Otel2PrometheusConverterTest { MetricSnapshots snapshots = converter.convert(Collections.singletonList(metricData)); - Labels labels = snapshots.get(0).getDataPoints().get(0).getLabels(); + Optional metricSnapshot = + snapshots.stream().filter(snapshot -> snapshot instanceof CounterSnapshot).findFirst(); + assertThat(metricSnapshot).isPresent(); + + Labels labels = metricSnapshot.get().getDataPoints().get(0).getLabels(); attributes.forEach( (key, value) -> { String labelValue = labels.get(key.getKey()); @@ -368,11 +377,17 @@ class Otel2PrometheusConverterTest { Attributes attributesToUse = attributes == null ? Attributes.empty() : attributes; Resource resourceToUse = resource == null ? Resource.getDefault() : resource; + InstrumentationScopeInfo scope = + InstrumentationScopeInfo.builder("scope") + .setVersion("version") + .setSchemaUrl("schemaUrl") + .setAttributes(Attributes.of(stringKey("foo"), "bar")) + .build(); switch (metricDataType) { case SUMMARY: return ImmutableMetricData.createDoubleSummary( resourceToUse, - InstrumentationScopeInfo.create("scope"), + scope, metricName, "description", metricUnit, @@ -383,7 +398,7 @@ class Otel2PrometheusConverterTest { case LONG_SUM: return ImmutableMetricData.createLongSum( resourceToUse, - InstrumentationScopeInfo.create("scope"), + scope, metricName, "description", metricUnit, @@ -395,7 +410,7 @@ class Otel2PrometheusConverterTest { case DOUBLE_SUM: return ImmutableMetricData.createDoubleSum( resourceToUse, - InstrumentationScopeInfo.create("scope"), + scope, metricName, "description", metricUnit, @@ -407,7 +422,7 @@ class Otel2PrometheusConverterTest { case LONG_GAUGE: return ImmutableMetricData.createLongGauge( resourceToUse, - InstrumentationScopeInfo.create("scope"), + scope, metricName, "description", metricUnit, @@ -417,7 +432,7 @@ class Otel2PrometheusConverterTest { case DOUBLE_GAUGE: return ImmutableMetricData.createDoubleGauge( resourceToUse, - InstrumentationScopeInfo.create("scope"), + scope, metricName, "description", metricUnit, @@ -427,7 +442,7 @@ class Otel2PrometheusConverterTest { case HISTOGRAM: return ImmutableMetricData.createDoubleHistogram( resourceToUse, - InstrumentationScopeInfo.create("scope"), + scope, metricName, "description", metricUnit, @@ -448,7 +463,7 @@ class Otel2PrometheusConverterTest { case EXPONENTIAL_HISTOGRAM: return ImmutableMetricData.createExponentialHistogram( resourceToUse, - InstrumentationScopeInfo.create("scope"), + scope, metricName, "description", metricUnit, diff --git a/exporters/prometheus/src/test/java/io/opentelemetry/exporter/prometheus/PrometheusHttpServerTest.java b/exporters/prometheus/src/test/java/io/opentelemetry/exporter/prometheus/PrometheusHttpServerTest.java index 81b85fb948..470ae2aa31 100644 --- a/exporters/prometheus/src/test/java/io/opentelemetry/exporter/prometheus/PrometheusHttpServerTest.java +++ b/exporters/prometheus/src/test/java/io/opentelemetry/exporter/prometheus/PrometheusHttpServerTest.java @@ -409,7 +409,15 @@ class PrometheusHttpServerTest { @Test void stringRepresentation() { assertThat(prometheusServer.toString()) - .isEqualTo("PrometheusHttpServer{address=" + prometheusServer.getAddress() + "}"); + .isEqualTo( + "PrometheusHttpServer{" + + "host=localhost," + + "port=0," + + "otelScopeEnabled=true," + + "allowedResourceAttributesFilter=null," + + "memoryMode=REUSABLE_DATA," + + "defaultAggregationSelector=DefaultAggregationSelector{COUNTER=default, UP_DOWN_COUNTER=default, HISTOGRAM=default, OBSERVABLE_COUNTER=default, OBSERVABLE_UP_DOWN_COUNTER=default, OBSERVABLE_GAUGE=default, GAUGE=default}" + + "}"); } @Test diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 3735f265b9..78cb6e16a4 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,7 +1,7 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionSha256Sum=7197a12f450794931532469d4ff21a59ea2c1cd59a3ec3f89c035c3c420a6999 -distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.2-bin.zip +distributionSha256Sum=bd71102213493060956ec229d946beee57158dbd89d0e62b91bca0fa2c5f3531 +distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.3-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/integration-tests/graal-incubating/src/test/java/io/opentelemetry/integrationtests/graal/IncubatingApiTests.java b/integration-tests/graal-incubating/src/test/java/io/opentelemetry/integrationtests/graal/IncubatingApiTests.java index fd3d0162da..bb2eceefd0 100644 --- a/integration-tests/graal-incubating/src/test/java/io/opentelemetry/integrationtests/graal/IncubatingApiTests.java +++ b/integration-tests/graal-incubating/src/test/java/io/opentelemetry/integrationtests/graal/IncubatingApiTests.java @@ -19,6 +19,7 @@ import io.opentelemetry.api.incubator.metrics.ExtendedLongHistogram; import io.opentelemetry.api.incubator.metrics.ExtendedLongUpDownCounter; import io.opentelemetry.api.incubator.trace.ExtendedTracer; import io.opentelemetry.api.logs.LoggerProvider; +import io.opentelemetry.api.logs.Severity; import io.opentelemetry.api.metrics.Meter; import io.opentelemetry.api.metrics.MeterProvider; import io.opentelemetry.api.trace.TracerProvider; @@ -50,8 +51,8 @@ class IncubatingApiTests { .build(); ExtendedLogger logger = (ExtendedLogger) loggerProvider.get("logger"); - logger.isEnabled(); - logger.logRecordBuilder().setBody("message").emit(); + logger.isEnabled(Severity.INFO); + logger.logRecordBuilder().setSeverity(Severity.INFO).setBody("message").emit(); } @Test diff --git a/integration-tests/tracecontext/docker/Dockerfile b/integration-tests/tracecontext/docker/Dockerfile index 02f577a93c..d7ba3fa57f 100644 --- a/integration-tests/tracecontext/docker/Dockerfile +++ b/integration-tests/tracecontext/docker/Dockerfile @@ -1,4 +1,4 @@ -FROM python:3.13.5@sha256:5f69d22a88dd4cc4ee1576def19aef48c8faa1b566054c44291183831cbad13b AS build +FROM python:3.13.5@sha256:a6af772cf98267c48c145928cbeb35bd8e89b610acd70f93e3e8ac3e96c92af8 AS build # Main branch SHA as of April-1-2021 ARG TRACECONTEXT_GIT_TAG="dcd3ad9b7d6ac36f70ff3739874b73c11b0302a1" @@ -11,7 +11,7 @@ RUN unzip trace-context.zip RUN rm trace-context.zip RUN mv trace-context-${TRACECONTEXT_GIT_TAG}/test /tracecontext-testsuite -FROM python:3.13.5-slim@sha256:f2fdaec50160418e0c2867ba3e254755edd067171725886d5d303fd7057bbf81 +FROM python:3.13.5-slim@sha256:6544e0e002b40ae0f59bc3618b07c1e48064c4faed3a15ae2fbd2e8f663e8283 RUN pip install aiohttp diff --git a/sdk-extensions/autoconfigure-spi/src/main/java/io/opentelemetry/sdk/autoconfigure/spi/ConfigProperties.java b/sdk-extensions/autoconfigure-spi/src/main/java/io/opentelemetry/sdk/autoconfigure/spi/ConfigProperties.java index 9480b66ac8..0485597edc 100644 --- a/sdk-extensions/autoconfigure-spi/src/main/java/io/opentelemetry/sdk/autoconfigure/spi/ConfigProperties.java +++ b/sdk-extensions/autoconfigure-spi/src/main/java/io/opentelemetry/sdk/autoconfigure/spi/ConfigProperties.java @@ -7,6 +7,7 @@ package io.opentelemetry.sdk.autoconfigure.spi; import static io.opentelemetry.api.internal.ConfigUtil.defaultIfNull; +import io.opentelemetry.common.ComponentLoader; import java.time.Duration; import java.util.List; import java.util.Map; @@ -204,4 +205,9 @@ public interface ConfigProperties { Map value = getMap(name); return value.isEmpty() ? defaultValue : value; } + + /** Return a {@link ComponentLoader} that should be used to load SPIs. */ + default ComponentLoader getComponentLoader() { + return ComponentLoader.forClassLoader(ConfigProperties.class.getClassLoader()); + } } diff --git a/sdk-extensions/autoconfigure-spi/src/main/java/io/opentelemetry/sdk/autoconfigure/spi/internal/DefaultConfigProperties.java b/sdk-extensions/autoconfigure-spi/src/main/java/io/opentelemetry/sdk/autoconfigure/spi/internal/DefaultConfigProperties.java index b5496df7c4..f8818c015b 100644 --- a/sdk-extensions/autoconfigure-spi/src/main/java/io/opentelemetry/sdk/autoconfigure/spi/internal/DefaultConfigProperties.java +++ b/sdk-extensions/autoconfigure-spi/src/main/java/io/opentelemetry/sdk/autoconfigure/spi/internal/DefaultConfigProperties.java @@ -10,6 +10,7 @@ import static java.util.stream.Collectors.joining; import io.opentelemetry.api.internal.ConfigUtil; import io.opentelemetry.api.internal.StringUtils; +import io.opentelemetry.common.ComponentLoader; import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; import io.opentelemetry.sdk.autoconfigure.spi.ConfigurationException; import java.time.Duration; @@ -38,6 +39,7 @@ import javax.annotation.Nullable; public final class DefaultConfigProperties implements ConfigProperties { private final Map config; + private final ComponentLoader componentLoader; /** * Creates a {@link DefaultConfigProperties} by merging system properties, environment variables, @@ -46,9 +48,10 @@ public final class DefaultConfigProperties implements ConfigProperties { *

Environment variables take priority over {@code defaultProperties}. System properties take * priority over environment variables. */ - public static DefaultConfigProperties create(Map defaultProperties) { + public static DefaultConfigProperties create( + Map defaultProperties, ComponentLoader componentLoader) { return new DefaultConfigProperties( - ConfigUtil.safeSystemProperties(), System.getenv(), defaultProperties); + ConfigUtil.safeSystemProperties(), System.getenv(), defaultProperties, componentLoader); } /** @@ -56,13 +59,18 @@ public final class DefaultConfigProperties implements ConfigProperties { * properties and environment variables. */ public static DefaultConfigProperties createFromMap(Map properties) { - return new DefaultConfigProperties(properties, Collections.emptyMap(), Collections.emptyMap()); + return new DefaultConfigProperties( + properties, + Collections.emptyMap(), + Collections.emptyMap(), + ComponentLoader.forClassLoader(DefaultConfigProperties.class.getClassLoader())); } private DefaultConfigProperties( Map systemProperties, Map environmentVariables, - Map defaultProperties) { + Map defaultProperties, + ComponentLoader componentLoader) { Map config = new HashMap<>(); defaultProperties.forEach( (name, value) -> config.put(ConfigUtil.normalizePropertyKey(name), value)); @@ -73,6 +81,7 @@ public final class DefaultConfigProperties implements ConfigProperties { config.put(ConfigUtil.normalizePropertyKey(key.toString()), value.toString())); this.config = config; + this.componentLoader = componentLoader; } private DefaultConfigProperties( @@ -82,6 +91,7 @@ public final class DefaultConfigProperties implements ConfigProperties { overrides.forEach((name, value) -> config.put(ConfigUtil.normalizePropertyKey(name), value)); this.config = config; + this.componentLoader = previousProperties.componentLoader; } @Override @@ -233,6 +243,11 @@ public final class DefaultConfigProperties implements ConfigProperties { Map.Entry::getKey, Map.Entry::getValue, (first, next) -> next, LinkedHashMap::new)); } + @Override + public ComponentLoader getComponentLoader() { + return componentLoader; + } + /** * Return a new {@link DefaultConfigProperties} by overriding the {@code previousProperties} with * the {@code overrides}. diff --git a/sdk-extensions/autoconfigure-spi/src/test/java/io/opentelemetry/sdk/autoconfigure/spi/internal/ConfigPropertiesTest.java b/sdk-extensions/autoconfigure-spi/src/test/java/io/opentelemetry/sdk/autoconfigure/spi/internal/ConfigPropertiesTest.java index 78cc6806c0..4a119e600c 100644 --- a/sdk-extensions/autoconfigure-spi/src/test/java/io/opentelemetry/sdk/autoconfigure/spi/internal/ConfigPropertiesTest.java +++ b/sdk-extensions/autoconfigure-spi/src/test/java/io/opentelemetry/sdk/autoconfigure/spi/internal/ConfigPropertiesTest.java @@ -11,6 +11,7 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.assertj.core.api.Assertions.entry; +import io.opentelemetry.common.ComponentLoader; import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; import io.opentelemetry.sdk.autoconfigure.spi.ConfigurationException; import java.time.Duration; @@ -22,6 +23,9 @@ import org.junit.jupiter.api.Test; class ConfigPropertiesTest { + private static final ComponentLoader COMPONENT_LOADER = + ComponentLoader.forClassLoader(ConfigPropertiesTest.class.getClassLoader()); + @Test void allValid() { Map properties = makeTestProps(); @@ -246,7 +250,7 @@ class ConfigPropertiesTest { expectedMap.put("bear", "growl"); Map map = makeTestProps(); - ConfigProperties properties = DefaultConfigProperties.create(map); + ConfigProperties properties = DefaultConfigProperties.create(map, COMPONENT_LOADER); assertThat(properties.getBoolean("test.boolean", false)).isTrue(); assertThat(properties.getString("test.string", "nah")).isEqualTo("str"); assertThat(properties.getDouble("test.double", 65.535)).isEqualTo(5.4); @@ -260,7 +264,7 @@ class ConfigPropertiesTest { @Test void defaultMethodsFallBack() { - ConfigProperties properties = DefaultConfigProperties.create(emptyMap()); + ConfigProperties properties = DefaultConfigProperties.create(emptyMap(), COMPONENT_LOADER); assertThat(properties.getBoolean("foo", true)).isTrue(); assertThat(properties.getString("foo", "bar")).isEqualTo("bar"); assertThat(properties.getDouble("foo", 65.535)).isEqualTo(65.535); @@ -271,7 +275,7 @@ class ConfigPropertiesTest { @Test void defaultCollectionTypes() { - ConfigProperties properties = DefaultConfigProperties.create(emptyMap()); + ConfigProperties properties = DefaultConfigProperties.create(emptyMap(), COMPONENT_LOADER); assertThat(properties.getList("foo", Arrays.asList("1", "2", "3"))) .containsExactly("1", "2", "3"); assertThat(properties.getList("foo")).isEmpty(); diff --git a/sdk-extensions/autoconfigure/src/main/java/io/opentelemetry/sdk/autoconfigure/AutoConfiguredOpenTelemetrySdkBuilder.java b/sdk-extensions/autoconfigure/src/main/java/io/opentelemetry/sdk/autoconfigure/AutoConfiguredOpenTelemetrySdkBuilder.java index 124231fa32..1b0e43c1f1 100644 --- a/sdk-extensions/autoconfigure/src/main/java/io/opentelemetry/sdk/autoconfigure/AutoConfiguredOpenTelemetrySdkBuilder.java +++ b/sdk-extensions/autoconfigure/src/main/java/io/opentelemetry/sdk/autoconfigure/AutoConfiguredOpenTelemetrySdkBuilder.java @@ -8,11 +8,11 @@ package io.opentelemetry.sdk.autoconfigure; import static java.util.Objects.requireNonNull; import io.opentelemetry.api.GlobalOpenTelemetry; +import io.opentelemetry.common.ComponentLoader; import io.opentelemetry.context.propagation.ContextPropagators; import io.opentelemetry.context.propagation.TextMapPropagator; import io.opentelemetry.sdk.OpenTelemetrySdk; import io.opentelemetry.sdk.OpenTelemetrySdkBuilder; -import io.opentelemetry.sdk.autoconfigure.internal.ComponentLoader; import io.opentelemetry.sdk.autoconfigure.internal.SpiHelper; import io.opentelemetry.sdk.autoconfigure.spi.AutoConfigurationCustomizer; import io.opentelemetry.sdk.autoconfigure.spi.AutoConfigurationCustomizerProvider; @@ -117,7 +117,7 @@ public final class AutoConfiguredOpenTelemetrySdkBuilder implements AutoConfigur Function.identity(); private ComponentLoader componentLoader = - SpiHelper.serviceComponentLoader(AutoConfiguredOpenTelemetrySdk.class.getClassLoader()); + ComponentLoader.forClassLoader(AutoConfiguredOpenTelemetrySdk.class.getClassLoader()); private boolean registerShutdownHook = true; @@ -410,12 +410,12 @@ public final class AutoConfiguredOpenTelemetrySdkBuilder implements AutoConfigur public AutoConfiguredOpenTelemetrySdkBuilder setServiceClassLoader( ClassLoader serviceClassLoader) { requireNonNull(serviceClassLoader, "serviceClassLoader"); - this.componentLoader = SpiHelper.serviceComponentLoader(serviceClassLoader); + this.componentLoader = ComponentLoader.forClassLoader(serviceClassLoader); return this; } /** Sets the {@link ComponentLoader} to be used to load SPI implementations. */ - AutoConfiguredOpenTelemetrySdkBuilder setComponentLoader(ComponentLoader componentLoader) { + public AutoConfiguredOpenTelemetrySdkBuilder setComponentLoader(ComponentLoader componentLoader) { requireNonNull(componentLoader, "componentLoader"); this.componentLoader = componentLoader; return this; @@ -628,7 +628,8 @@ public final class AutoConfiguredOpenTelemetrySdkBuilder implements AutoConfigur } private ConfigProperties computeConfigProperties() { - DefaultConfigProperties properties = DefaultConfigProperties.create(propertiesSupplier.get()); + DefaultConfigProperties properties = + DefaultConfigProperties.create(propertiesSupplier.get(), componentLoader); for (Function> customizer : propertiesCustomizers) { Map overrides = customizer.apply(properties); properties = properties.withOverrides(overrides); diff --git a/sdk-extensions/autoconfigure/src/main/java/io/opentelemetry/sdk/autoconfigure/IncubatingUtil.java b/sdk-extensions/autoconfigure/src/main/java/io/opentelemetry/sdk/autoconfigure/IncubatingUtil.java index a1280e241d..df2686ece5 100644 --- a/sdk-extensions/autoconfigure/src/main/java/io/opentelemetry/sdk/autoconfigure/IncubatingUtil.java +++ b/sdk-extensions/autoconfigure/src/main/java/io/opentelemetry/sdk/autoconfigure/IncubatingUtil.java @@ -8,8 +8,8 @@ package io.opentelemetry.sdk.autoconfigure; import io.opentelemetry.api.incubator.config.ConfigProvider; import io.opentelemetry.api.incubator.config.DeclarativeConfigException; import io.opentelemetry.api.incubator.config.GlobalConfigProvider; +import io.opentelemetry.common.ComponentLoader; import io.opentelemetry.sdk.OpenTelemetrySdk; -import io.opentelemetry.sdk.autoconfigure.internal.ComponentLoader; import io.opentelemetry.sdk.autoconfigure.spi.ConfigurationException; import io.opentelemetry.sdk.resources.Resource; import java.io.FileInputStream; @@ -51,8 +51,9 @@ final class IncubatingUtil { Class sdkConfigProvider = Class.forName("io.opentelemetry.sdk.extension.incubator.fileconfig.SdkConfigProvider"); Method createFileConfigProvider = - sdkConfigProvider.getMethod("create", openTelemetryConfiguration); - ConfigProvider configProvider = (ConfigProvider) createFileConfigProvider.invoke(null, model); + sdkConfigProvider.getMethod("create", openTelemetryConfiguration, ComponentLoader.class); + ConfigProvider configProvider = + (ConfigProvider) createFileConfigProvider.invoke(null, model, componentLoader); // Note: can't access file configuration resource without reflection so setting a dummy // resource return AutoConfiguredOpenTelemetrySdk.create( diff --git a/sdk-extensions/autoconfigure/src/main/java/io/opentelemetry/sdk/autoconfigure/ResourceConfiguration.java b/sdk-extensions/autoconfigure/src/main/java/io/opentelemetry/sdk/autoconfigure/ResourceConfiguration.java index 8e00ee19ab..6f15adfc03 100644 --- a/sdk-extensions/autoconfigure/src/main/java/io/opentelemetry/sdk/autoconfigure/ResourceConfiguration.java +++ b/sdk-extensions/autoconfigure/src/main/java/io/opentelemetry/sdk/autoconfigure/ResourceConfiguration.java @@ -8,6 +8,7 @@ package io.opentelemetry.sdk.autoconfigure; import io.opentelemetry.api.common.AttributeKey; import io.opentelemetry.api.common.Attributes; import io.opentelemetry.api.common.AttributesBuilder; +import io.opentelemetry.common.ComponentLoader; import io.opentelemetry.sdk.autoconfigure.internal.SpiHelper; import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; import io.opentelemetry.sdk.autoconfigure.spi.ConfigurationException; @@ -59,7 +60,10 @@ public final class ResourceConfiguration { * @return the resource. */ public static Resource createEnvironmentResource() { - return createEnvironmentResource(DefaultConfigProperties.create(Collections.emptyMap())); + return createEnvironmentResource( + DefaultConfigProperties.create( + Collections.emptyMap(), + ComponentLoader.forClassLoader(ResourceConfiguration.class.getClassLoader()))); } /** diff --git a/sdk-extensions/autoconfigure/src/main/java/io/opentelemetry/sdk/autoconfigure/internal/AutoConfigureUtil.java b/sdk-extensions/autoconfigure/src/main/java/io/opentelemetry/sdk/autoconfigure/internal/AutoConfigureUtil.java index 52f0236e8d..8bf42005ba 100644 --- a/sdk-extensions/autoconfigure/src/main/java/io/opentelemetry/sdk/autoconfigure/internal/AutoConfigureUtil.java +++ b/sdk-extensions/autoconfigure/src/main/java/io/opentelemetry/sdk/autoconfigure/internal/AutoConfigureUtil.java @@ -59,22 +59,6 @@ public final class AutoConfigureUtil { } } - /** Sets the {@link ComponentLoader} to be used in the auto-configuration process. */ - public static AutoConfiguredOpenTelemetrySdkBuilder setComponentLoader( - AutoConfiguredOpenTelemetrySdkBuilder builder, ComponentLoader componentLoader) { - try { - Method method = - AutoConfiguredOpenTelemetrySdkBuilder.class.getDeclaredMethod( - "setComponentLoader", ComponentLoader.class); - method.setAccessible(true); - method.invoke(builder, componentLoader); - return builder; - } catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException e) { - throw new IllegalStateException( - "Error calling setComponentLoader on AutoConfiguredOpenTelemetrySdkBuilder", e); - } - } - /** Sets the {@link ConfigProperties} customizer to be used in the auto-configuration process. */ public static AutoConfiguredOpenTelemetrySdkBuilder setConfigPropertiesCustomizer( AutoConfiguredOpenTelemetrySdkBuilder builder, diff --git a/sdk-extensions/autoconfigure/src/main/java/io/opentelemetry/sdk/autoconfigure/internal/ComponentLoader.java b/sdk-extensions/autoconfigure/src/main/java/io/opentelemetry/sdk/autoconfigure/internal/ComponentLoader.java index 49bfc4e52f..b7f29db27e 100644 --- a/sdk-extensions/autoconfigure/src/main/java/io/opentelemetry/sdk/autoconfigure/internal/ComponentLoader.java +++ b/sdk-extensions/autoconfigure/src/main/java/io/opentelemetry/sdk/autoconfigure/internal/ComponentLoader.java @@ -10,14 +10,9 @@ package io.opentelemetry.sdk.autoconfigure.internal; * *

This class is internal and is hence not for public use. Its APIs are unstable and can change * at any time. + * + * @deprecated Use {@link io.opentelemetry.common.ComponentLoader} instead */ -public interface ComponentLoader { - /** - * Load implementations of an SPI. - * - * @param spiClass the SPI class - * @param the SPI type - * @return iterable of SPI implementations - */ - Iterable load(Class spiClass); -} +@Deprecated +// TODO(jack-berg): delete after 1.54.0 release +public interface ComponentLoader extends io.opentelemetry.common.ComponentLoader {} diff --git a/sdk-extensions/autoconfigure/src/main/java/io/opentelemetry/sdk/autoconfigure/internal/SpiHelper.java b/sdk-extensions/autoconfigure/src/main/java/io/opentelemetry/sdk/autoconfigure/internal/SpiHelper.java index db4ca4ff64..d1d00aed57 100644 --- a/sdk-extensions/autoconfigure/src/main/java/io/opentelemetry/sdk/autoconfigure/internal/SpiHelper.java +++ b/sdk-extensions/autoconfigure/src/main/java/io/opentelemetry/sdk/autoconfigure/internal/SpiHelper.java @@ -5,6 +5,7 @@ package io.opentelemetry.sdk.autoconfigure.internal; +import io.opentelemetry.common.ComponentLoader; import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; import io.opentelemetry.sdk.autoconfigure.spi.Ordered; import io.opentelemetry.sdk.autoconfigure.spi.internal.AutoConfigureListener; @@ -15,7 +16,6 @@ import java.util.HashMap; import java.util.IdentityHashMap; import java.util.List; import java.util.Map; -import java.util.ServiceLoader; import java.util.Set; import java.util.function.BiFunction; import java.util.function.Function; @@ -37,7 +37,7 @@ public final class SpiHelper { /** Create a {@link SpiHelper} which loads SPIs using the {@code classLoader}. */ public static SpiHelper create(ClassLoader classLoader) { - return new SpiHelper(serviceComponentLoader(classLoader)); + return new SpiHelper(ComponentLoader.forClassLoader(classLoader)); } /** Create a {@link SpiHelper} which loads SPIs using the {@code componentLoader}. */ @@ -45,11 +45,6 @@ public final class SpiHelper { return new SpiHelper(componentLoader); } - /** Create a {@link ComponentLoader} which loads using the {@code classLoader}. */ - public static ComponentLoader serviceComponentLoader(ClassLoader classLoader) { - return new ServiceLoaderComponentLoader(classLoader); - } - /** Return the backing underlying {@link ComponentLoader}. */ public ComponentLoader getComponentLoader() { return componentLoader; @@ -125,17 +120,4 @@ public final class SpiHelper { public Set getListeners() { return Collections.unmodifiableSet(listeners); } - - private static class ServiceLoaderComponentLoader implements ComponentLoader { - private final ClassLoader classLoader; - - private ServiceLoaderComponentLoader(ClassLoader classLoader) { - this.classLoader = classLoader; - } - - @Override - public Iterable load(Class spiClass) { - return ServiceLoader.load(spiClass, classLoader); - } - } } diff --git a/sdk-extensions/autoconfigure/src/test/java/io/opentelemetry/sdk/autoconfigure/AutoConfiguredOpenTelemetrySdkTest.java b/sdk-extensions/autoconfigure/src/test/java/io/opentelemetry/sdk/autoconfigure/AutoConfiguredOpenTelemetrySdkTest.java index 45d2630832..3283cd8af6 100644 --- a/sdk-extensions/autoconfigure/src/test/java/io/opentelemetry/sdk/autoconfigure/AutoConfiguredOpenTelemetrySdkTest.java +++ b/sdk-extensions/autoconfigure/src/test/java/io/opentelemetry/sdk/autoconfigure/AutoConfiguredOpenTelemetrySdkTest.java @@ -28,6 +28,7 @@ import io.opentelemetry.api.trace.Span; import io.opentelemetry.api.trace.SpanId; import io.opentelemetry.api.trace.TraceId; import io.opentelemetry.api.trace.propagation.W3CTraceContextPropagator; +import io.opentelemetry.common.ComponentLoader; import io.opentelemetry.context.Context; import io.opentelemetry.context.ContextKey; import io.opentelemetry.context.propagation.TextMapGetter; @@ -35,7 +36,6 @@ import io.opentelemetry.context.propagation.TextMapPropagator; import io.opentelemetry.internal.testing.slf4j.SuppressLogger; import io.opentelemetry.sdk.OpenTelemetrySdk; import io.opentelemetry.sdk.autoconfigure.internal.AutoConfigureUtil; -import io.opentelemetry.sdk.autoconfigure.internal.ComponentLoader; import io.opentelemetry.sdk.autoconfigure.internal.SpiHelper; import io.opentelemetry.sdk.autoconfigure.spi.AutoConfigurationCustomizerProvider; import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; @@ -65,6 +65,7 @@ import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Properties; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CountDownLatch; @@ -255,22 +256,28 @@ class AutoConfiguredOpenTelemetrySdkTest { SpiHelper spiHelper = SpiHelper.create(AutoConfiguredOpenTelemetrySdkBuilder.class.getClassLoader()); - AutoConfigureUtil.setComponentLoader( - builder, - new ComponentLoader() { - @SuppressWarnings("unchecked") - @Override - public Iterable load(Class spiClass) { - if (spiClass.equals(AutoConfigurationCustomizerProvider.class)) { - return Collections.singletonList((T) customizerProvider); - } - return spiHelper.load(spiClass); - } - }) - .build(); + ComponentLoader componentLoader = + new ComponentLoader() { + @SuppressWarnings("unchecked") + @Override + public Iterable load(Class spiClass) { + if (spiClass.equals(AutoConfigurationCustomizerProvider.class)) { + return Collections.singletonList((T) customizerProvider); + } + return spiHelper.load(spiClass); + } + }; + AutoConfiguredOpenTelemetrySdk autoConfiguredOpenTelemetrySdk = + builder.setComponentLoader(componentLoader).build(); + + assertThat( + Objects.requireNonNull(autoConfiguredOpenTelemetrySdk.getConfig()).getComponentLoader()) + .isSameAs(componentLoader); verify(customizerProvider).customize(any()); verifyNoMoreInteractions(customizerProvider); + + autoConfiguredOpenTelemetrySdk.getOpenTelemetrySdk().shutdown().join(10, TimeUnit.SECONDS); } @Test diff --git a/sdk-extensions/autoconfigure/src/test/java/io/opentelemetry/sdk/autoconfigure/ResourceConfigurationTest.java b/sdk-extensions/autoconfigure/src/test/java/io/opentelemetry/sdk/autoconfigure/ResourceConfigurationTest.java index e2c94a68aa..1678fb2957 100644 --- a/sdk-extensions/autoconfigure/src/test/java/io/opentelemetry/sdk/autoconfigure/ResourceConfigurationTest.java +++ b/sdk-extensions/autoconfigure/src/test/java/io/opentelemetry/sdk/autoconfigure/ResourceConfigurationTest.java @@ -12,6 +12,7 @@ import static java.util.Collections.singletonMap; import com.google.common.collect.ImmutableMap; import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.common.ComponentLoader; import io.opentelemetry.sdk.autoconfigure.internal.SpiHelper; import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; import io.opentelemetry.sdk.autoconfigure.spi.internal.DefaultConfigProperties; @@ -25,6 +26,9 @@ import org.mockito.junit.jupiter.MockitoExtension; @ExtendWith(MockitoExtension.class) class ResourceConfigurationTest { + private static final ComponentLoader componentLoader = + ComponentLoader.forClassLoader(ResourceConfigurationTest.class.getClassLoader()); + @Test void customConfigResourceWithDisabledKeys() { Map props = new HashMap<>(); @@ -35,7 +39,7 @@ class ResourceConfigurationTest { assertThat( ResourceConfiguration.configureResource( - DefaultConfigProperties.create(props), + DefaultConfigProperties.create(props, componentLoader), SpiHelper.create(ResourceConfigurationTest.class.getClassLoader()), (r, c) -> r)) .isEqualTo( diff --git a/sdk-extensions/autoconfigure/src/test/java/io/opentelemetry/sdk/autoconfigure/internal/SpiHelperTest.java b/sdk-extensions/autoconfigure/src/test/java/io/opentelemetry/sdk/autoconfigure/internal/SpiHelperTest.java index ad7a96704e..b1497079a9 100644 --- a/sdk-extensions/autoconfigure/src/test/java/io/opentelemetry/sdk/autoconfigure/internal/SpiHelperTest.java +++ b/sdk-extensions/autoconfigure/src/test/java/io/opentelemetry/sdk/autoconfigure/internal/SpiHelperTest.java @@ -15,6 +15,7 @@ import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import io.opentelemetry.common.ComponentLoader; import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; import io.opentelemetry.sdk.autoconfigure.spi.ResourceProvider; import io.opentelemetry.sdk.autoconfigure.spi.internal.DefaultConfigProperties; diff --git a/sdk-extensions/autoconfigure/src/testFullConfig/java/io/opentelemetry/sdk/autoconfigure/ResourceConfigurationTest.java b/sdk-extensions/autoconfigure/src/testFullConfig/java/io/opentelemetry/sdk/autoconfigure/ResourceConfigurationTest.java index ae2342aeff..8683dd6de3 100644 --- a/sdk-extensions/autoconfigure/src/testFullConfig/java/io/opentelemetry/sdk/autoconfigure/ResourceConfigurationTest.java +++ b/sdk-extensions/autoconfigure/src/testFullConfig/java/io/opentelemetry/sdk/autoconfigure/ResourceConfigurationTest.java @@ -9,6 +9,7 @@ import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.asser import io.opentelemetry.api.common.AttributeKey; import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.common.ComponentLoader; import io.opentelemetry.internal.testing.slf4j.SuppressLogger; import io.opentelemetry.sdk.autoconfigure.internal.SpiHelper; import io.opentelemetry.sdk.autoconfigure.spi.internal.DefaultConfigProperties; @@ -29,14 +30,15 @@ import org.junit.jupiter.params.provider.MethodSource; @SuppressLogger(ResourceConfiguration.class) class ResourceConfigurationTest { - private final SpiHelper spiHelper = - SpiHelper.create(ResourceConfigurationTest.class.getClassLoader()); + private final ComponentLoader componentLoader = + ComponentLoader.forClassLoader(ResourceConfigurationTest.class.getClassLoader()); + private final SpiHelper spiHelper = SpiHelper.create(componentLoader); @Test void configureResource_EmptyClassLoader() { Attributes attributes = ResourceConfiguration.configureResource( - DefaultConfigProperties.create(Collections.emptyMap()), + DefaultConfigProperties.create(Collections.emptyMap(), componentLoader), SpiHelper.create(new URLClassLoader(new URL[0], null)), (r, c) -> r) .getAttributes(); @@ -66,7 +68,7 @@ class ResourceConfigurationTest { } Attributes attributes = ResourceConfiguration.configureResource( - DefaultConfigProperties.create(config), spiHelper, (r, c) -> r) + DefaultConfigProperties.create(config, componentLoader), spiHelper, (r, c) -> r) .getAttributes(); attributeAssertion.accept(assertThat(attributes)); diff --git a/sdk-extensions/autoconfigure/src/testIncubating/java/io/opentelemetry/sdk/autoconfigure/DeclarativeConfigurationTest.java b/sdk-extensions/autoconfigure/src/testIncubating/java/io/opentelemetry/sdk/autoconfigure/DeclarativeConfigurationTest.java index f92c1027cc..a29a9f581d 100644 --- a/sdk-extensions/autoconfigure/src/testIncubating/java/io/opentelemetry/sdk/autoconfigure/DeclarativeConfigurationTest.java +++ b/sdk-extensions/autoconfigure/src/testIncubating/java/io/opentelemetry/sdk/autoconfigure/DeclarativeConfigurationTest.java @@ -7,6 +7,7 @@ package io.opentelemetry.sdk.autoconfigure; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat; import static java.util.Collections.singletonMap; +import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatCode; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.mockito.ArgumentMatchers.any; @@ -23,6 +24,7 @@ import io.opentelemetry.api.incubator.config.ConfigProvider; import io.opentelemetry.api.incubator.config.DeclarativeConfigProperties; import io.opentelemetry.api.incubator.config.GlobalConfigProvider; import io.opentelemetry.api.incubator.config.InstrumentationConfigUtil; +import io.opentelemetry.common.ComponentLoader; import io.opentelemetry.exporter.logging.LoggingSpanExporter; import io.opentelemetry.internal.testing.CleanupExtension; import io.opentelemetry.internal.testing.slf4j.SuppressLogger; @@ -40,7 +42,7 @@ import java.nio.file.Files; import java.nio.file.Path; import java.util.Arrays; import java.util.Collections; -import org.assertj.core.api.Assertions; +import java.util.Optional; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; @@ -134,17 +136,39 @@ class DeclarativeConfigurationTest { builder.setConfig(config).build(); cleanup.addCloseable(autoConfiguredOpenTelemetrySdk.getOpenTelemetrySdk()); - Assertions.assertThat(autoConfiguredOpenTelemetrySdk.getOpenTelemetrySdk().toString()) + assertThat(autoConfiguredOpenTelemetrySdk.getOpenTelemetrySdk().toString()) .isEqualTo(expectedSdk.toString()); // AutoConfiguredOpenTelemetrySdk#getResource() is set to a dummy value when configuring from // file - Assertions.assertThat(autoConfiguredOpenTelemetrySdk.getResource()) - .isEqualTo(Resource.getDefault()); + assertThat(autoConfiguredOpenTelemetrySdk.getResource()).isEqualTo(Resource.getDefault()); verify(builder, times(1)).shutdownHook(autoConfiguredOpenTelemetrySdk.getOpenTelemetrySdk()); - Assertions.assertThat(Runtime.getRuntime().removeShutdownHook(thread)).isTrue(); + assertThat(Runtime.getRuntime().removeShutdownHook(thread)).isTrue(); logCapturer.assertContains("Autoconfiguring from configuration file: " + configFilePath); } + @Test + void configFile_setComponentLoader() { + ComponentLoader componentLoader = + ComponentLoader.forClassLoader(DeclarativeConfigurationTest.class.getClassLoader()); + ConfigProperties config = + DefaultConfigProperties.createFromMap( + Collections.singletonMap("otel.experimental.config.file", configFilePath.toString())); + + AutoConfiguredOpenTelemetrySdk autoConfiguredOpenTelemetrySdk = + AutoConfiguredOpenTelemetrySdk.builder() + .setConfig(config) + .setComponentLoader(componentLoader) + .build(); + cleanup.addCloseable(autoConfiguredOpenTelemetrySdk.getOpenTelemetrySdk()); + + assertThat( + Optional.ofNullable(AutoConfigureUtil.getConfigProvider(autoConfiguredOpenTelemetrySdk)) + .map(ConfigProvider::getInstrumentationConfig) + .map(DeclarativeConfigProperties::getComponentLoader) + .orElse(null)) + .isSameAs(componentLoader); + } + @Test void configFile_NoShutdownHook() { ConfigProperties config = @@ -171,9 +195,7 @@ class DeclarativeConfigurationTest { OpenTelemetrySdk openTelemetrySdk = autoConfiguredOpenTelemetrySdk.getOpenTelemetrySdk(); cleanup.addCloseable(openTelemetrySdk); - Assertions.assertThat(GlobalOpenTelemetry.get()) - .extracting("delegate") - .isNotSameAs(openTelemetrySdk); + assertThat(GlobalOpenTelemetry.get()).extracting("delegate").isNotSameAs(openTelemetrySdk); assertThat(GlobalConfigProvider.get()) .isNotSameAs(autoConfiguredOpenTelemetrySdk.getConfigProvider()); } @@ -189,9 +211,7 @@ class DeclarativeConfigurationTest { OpenTelemetrySdk openTelemetrySdk = autoConfiguredOpenTelemetrySdk.getOpenTelemetrySdk(); cleanup.addCloseable(openTelemetrySdk); - Assertions.assertThat(GlobalOpenTelemetry.get()) - .extracting("delegate") - .isSameAs(openTelemetrySdk); + assertThat(GlobalOpenTelemetry.get()).extracting("delegate").isSameAs(openTelemetrySdk); assertThat(GlobalConfigProvider.get()) .isSameAs(autoConfiguredOpenTelemetrySdk.getConfigProvider()); } diff --git a/sdk-extensions/incubator/src/main/java/io/opentelemetry/sdk/extension/incubator/fileconfig/DeclarativeConfiguration.java b/sdk-extensions/incubator/src/main/java/io/opentelemetry/sdk/extension/incubator/fileconfig/DeclarativeConfiguration.java index b672f8e679..494138bdaa 100644 --- a/sdk-extensions/incubator/src/main/java/io/opentelemetry/sdk/extension/incubator/fileconfig/DeclarativeConfiguration.java +++ b/sdk-extensions/incubator/src/main/java/io/opentelemetry/sdk/extension/incubator/fileconfig/DeclarativeConfiguration.java @@ -11,8 +11,8 @@ import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; import io.opentelemetry.api.incubator.config.DeclarativeConfigException; import io.opentelemetry.api.incubator.config.DeclarativeConfigProperties; +import io.opentelemetry.common.ComponentLoader; import io.opentelemetry.sdk.OpenTelemetrySdk; -import io.opentelemetry.sdk.autoconfigure.internal.ComponentLoader; import io.opentelemetry.sdk.autoconfigure.internal.SpiHelper; import io.opentelemetry.sdk.autoconfigure.spi.internal.ComponentProvider; import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.OpenTelemetryConfigurationModel; @@ -22,8 +22,8 @@ import java.io.Closeable; import java.io.IOException; import java.io.InputStream; import java.util.Collections; -import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.logging.Logger; import java.util.regex.MatchResult; import java.util.regex.Matcher; @@ -31,14 +31,16 @@ import java.util.regex.Pattern; import javax.annotation.Nullable; import org.snakeyaml.engine.v2.api.Load; import org.snakeyaml.engine.v2.api.LoadSettings; +import org.snakeyaml.engine.v2.api.YamlUnicodeReader; import org.snakeyaml.engine.v2.common.ScalarStyle; -import org.snakeyaml.engine.v2.constructor.StandardConstructor; -import org.snakeyaml.engine.v2.exceptions.ConstructorException; -import org.snakeyaml.engine.v2.exceptions.YamlEngineException; +import org.snakeyaml.engine.v2.composer.Composer; import org.snakeyaml.engine.v2.nodes.MappingNode; import org.snakeyaml.engine.v2.nodes.Node; -import org.snakeyaml.engine.v2.nodes.NodeTuple; import org.snakeyaml.engine.v2.nodes.ScalarNode; +import org.snakeyaml.engine.v2.nodes.Tag; +import org.snakeyaml.engine.v2.parser.ParserImpl; +import org.snakeyaml.engine.v2.resolver.ScalarResolver; +import org.snakeyaml.engine.v2.scanner.StreamReader; import org.snakeyaml.engine.v2.schema.CoreSchema; /** @@ -55,9 +57,10 @@ public final class DeclarativeConfiguration { private static final Pattern ENV_VARIABLE_REFERENCE = Pattern.compile("\\$\\{([a-zA-Z_][a-zA-Z0-9_]*)(:-([^\n}]*))?}"); private static final ComponentLoader DEFAULT_COMPONENT_LOADER = - SpiHelper.serviceComponentLoader(DeclarativeConfiguration.class.getClassLoader()); + ComponentLoader.forClassLoader(DeclarativeConfigProperties.class.getClassLoader()); - private static final ObjectMapper MAPPER; + // Visible for testing + static final ObjectMapper MAPPER; static { MAPPER = @@ -126,8 +129,9 @@ public final class DeclarativeConfiguration { /** * Parse the {@code configuration} YAML and return the {@link OpenTelemetryConfigurationModel}. * - *

Before parsing, environment variable substitution is performed as described in {@link - * EnvSubstitutionConstructor}. + *

During parsing, environment variable substitution is performed as defined in the + * OpenTelemetry Configuration Data Model specification. * * @throws DeclarativeConfigException if unable to parse */ @@ -149,7 +153,7 @@ public final class DeclarativeConfiguration { // Visible for testing static Object loadYaml(InputStream inputStream, Map environmentVariables) { LoadSettings settings = LoadSettings.builder().setSchema(new CoreSchema()).build(); - Load yaml = new Load(settings, new EnvSubstitutionConstructor(settings, environmentVariables)); + Load yaml = new EnvLoad(settings, environmentVariables); return yaml.loadFromInputStream(inputStream); } @@ -159,8 +163,7 @@ public final class DeclarativeConfiguration { * @param model the configuration model * @return a generic {@link DeclarativeConfigProperties} representation of the model */ - public static DeclarativeConfigProperties toConfigProperties( - OpenTelemetryConfigurationModel model) { + public static DeclarativeConfigProperties toConfigProperties(Object model) { return toConfigProperties(model, DEFAULT_COMPONENT_LOADER); } @@ -198,7 +201,9 @@ public final class DeclarativeConfiguration { public static Sampler createSampler(DeclarativeConfigProperties genericSamplerModel) { YamlDeclarativeConfigProperties yamlDeclarativeConfigProperties = requireYamlDeclarativeConfigProperties(genericSamplerModel); - SamplerModel samplerModel = convertToModel(yamlDeclarativeConfigProperties, SamplerModel.class); + SamplerModel samplerModel = + MAPPER.convertValue( + DeclarativeConfigProperties.toMap(yamlDeclarativeConfigProperties), SamplerModel.class); return createAndMaybeCleanup( SamplerFactory.getInstance(), SpiHelper.create(yamlDeclarativeConfigProperties.getComponentLoader()), @@ -214,11 +219,6 @@ public final class DeclarativeConfiguration { return (YamlDeclarativeConfigProperties) declarativeConfigProperties; } - static T convertToModel( - YamlDeclarativeConfigProperties yamlDeclarativeConfigProperties, Class modelType) { - return MAPPER.convertValue(yamlDeclarativeConfigProperties.toMap(), modelType); - } - static R createAndMaybeCleanup(Factory factory, SpiHelper spiHelper, M model) { DeclarativeConfigContext context = new DeclarativeConfigContext(spiHelper); try { @@ -241,89 +241,110 @@ public final class DeclarativeConfiguration { } } - /** - * {@link StandardConstructor} which substitutes environment variables. - * - *

Environment variables follow the syntax {@code ${VARIABLE}}, where {@code VARIABLE} is an - * environment variable matching the regular expression {@code [a-zA-Z_]+[a-zA-Z0-9_]*}. - * - *

Environment variable substitution only takes place on scalar values of maps. References to - * environment variables in keys or sets are ignored. - * - *

If a referenced environment variable is not defined, it is replaced with {@code ""}. - */ - private static final class EnvSubstitutionConstructor extends StandardConstructor { + private static final class EnvLoad extends Load { - // Load is not thread safe but this instance is always used on the same thread - private final Load load; + private final LoadSettings settings; private final Map environmentVariables; - private EnvSubstitutionConstructor( - LoadSettings loadSettings, Map environmentVariables) { - super(loadSettings); - load = new Load(loadSettings); + public EnvLoad(LoadSettings settings, Map environmentVariables) { + super(settings); + this.settings = settings; this.environmentVariables = environmentVariables; } - /** - * Implementation is same as {@link - * org.snakeyaml.engine.v2.constructor.BaseConstructor#constructMapping(MappingNode)} except we - * override the resolution of values with our custom {@link #constructValueObject(Node)}, which - * performs environment variable substitution. - */ @Override - @SuppressWarnings({"ReturnValueIgnored", "CatchingUnchecked"}) - protected Map constructMapping(MappingNode node) { - Map mapping = settings.getDefaultMap().apply(node.getValue().size()); - List nodeValue = node.getValue(); - for (NodeTuple tuple : nodeValue) { - Node keyNode = tuple.getKeyNode(); - Object key = constructObject(keyNode); - if (key != null) { - try { - key.hashCode(); // check circular dependencies - } catch (Exception e) { - throw new ConstructorException( - "while constructing a mapping", - node.getStartMark(), - "found unacceptable key " + key, - tuple.getKeyNode().getStartMark(), - e); - } - } - Node valueNode = tuple.getValueNode(); - Object value = constructValueObject(valueNode); - if (keyNode.isRecursive()) { - if (settings.getAllowRecursiveKeys()) { - postponeMapFilling(mapping, key, value); - } else { - throw new YamlEngineException( - "Recursive key for mapping is detected but it is not configured to be allowed."); - } - } else { - mapping.put(key, value); - } - } - - return mapping; + public Object loadFromInputStream(InputStream yamlStream) { + Objects.requireNonNull(yamlStream, "InputStream cannot be null"); + return loadOne( + new EnvComposer( + settings, + new ParserImpl( + settings, new StreamReader(settings, new YamlUnicodeReader(yamlStream))), + environmentVariables)); } + } + + /** + * A YAML Composer that performs environment variable substitution according to the + * OpenTelemetry Configuration Data Model specification. + * + *

This composer supports: + * + *

+ * + *

Environment variable substitution only applies to scalar values. Mapping keys are not + * candidates for substitution. Referenced environment variables that are undefined, null, or + * empty are replaced with empty values unless a default value is provided. + * + *

The {@code $} character serves as an escape sequence where {@code $$} in the input is + * translated to a single {@code $} in the output. This prevents environment variable substitution + * for the escaped content. + */ + private static final class EnvComposer extends Composer { + + private final Load load; + private final Map environmentVariables; + private final ScalarResolver scalarResolver; private static final String ESCAPE_SEQUENCE = "$$"; private static final int ESCAPE_SEQUENCE_LENGTH = ESCAPE_SEQUENCE.length(); private static final char ESCAPE_SEQUENCE_REPLACEMENT = '$'; - private Object constructValueObject(Node node) { - Object value = constructObject(node); - if (!(node instanceof ScalarNode)) { - return value; + public EnvComposer( + LoadSettings settings, ParserImpl parser, Map environmentVariables) { + super(settings, parser); + this.load = new Load(settings); + this.environmentVariables = environmentVariables; + this.scalarResolver = settings.getSchema().getScalarResolver(); + } + + @Override + protected Node composeValueNode(MappingNode node) { + Node itemValue = super.composeValueNode(node); + if (!(itemValue instanceof ScalarNode)) { + // Only apply environment variable substitution to ScalarNodes + return itemValue; } - if (!(value instanceof String)) { - return value; + ScalarNode scalarNode = (ScalarNode) itemValue; + String envSubstitution = envSubstitution(scalarNode.getValue()); + + // If the environment variable substitution does not change the value, do not modify the node + if (envSubstitution.equals(scalarNode.getValue())) { + return itemValue; } - String val = (String) value; - ScalarStyle scalarStyle = ((ScalarNode) node).getScalarStyle(); + Object envSubstitutionObj = load.loadFromString(envSubstitution); + Tag tag = itemValue.getTag(); + ScalarStyle scalarStyle = scalarNode.getScalarStyle(); + Tag resolvedTag = + envSubstitutionObj == null + ? Tag.NULL + : scalarResolver.resolve(envSubstitutionObj.toString(), true); + + // Only non-quoted substituted scalars can have their tag changed + if (!itemValue.getTag().equals(resolvedTag) + && scalarStyle != ScalarStyle.SINGLE_QUOTED + && scalarStyle != ScalarStyle.DOUBLE_QUOTED) { + tag = resolvedTag; + } + + boolean resolved = true; + return new ScalarNode( + tag, + resolved, + envSubstitution, + scalarStyle, + itemValue.getStartMark(), + itemValue.getEndMark()); + } + + private String envSubstitution(String val) { // Iterate through val left to right, search for escape sequence "$$" // For the substring of val between the last escape sequence and the next found, perform // environment variable substitution @@ -346,13 +367,7 @@ public final class DeclarativeConfiguration { } } - // If the value was double quoted, retain the double quotes so we don't change a value - // intended to be a string to a different type after environment variable substitution - if (scalarStyle == ScalarStyle.DOUBLE_QUOTED) { - newVal.insert(0, "\""); - newVal.append("\""); - } - return load.loadFromString(newVal.toString()); + return newVal.toString(); } private StringBuilder envVarSubstitution( diff --git a/sdk-extensions/incubator/src/main/java/io/opentelemetry/sdk/extension/incubator/fileconfig/ResourceFactory.java b/sdk-extensions/incubator/src/main/java/io/opentelemetry/sdk/extension/incubator/fileconfig/ResourceFactory.java index 1ac18ecb95..4fdbb9bd73 100644 --- a/sdk-extensions/incubator/src/main/java/io/opentelemetry/sdk/extension/incubator/fileconfig/ResourceFactory.java +++ b/sdk-extensions/incubator/src/main/java/io/opentelemetry/sdk/extension/incubator/fileconfig/ResourceFactory.java @@ -5,8 +5,6 @@ package io.opentelemetry.sdk.extension.incubator.fileconfig; -import static io.opentelemetry.sdk.internal.GlobUtil.createGlobPatternPredicate; - import io.opentelemetry.api.common.Attributes; import io.opentelemetry.sdk.autoconfigure.ResourceConfiguration; import io.opentelemetry.sdk.autoconfigure.spi.internal.DefaultConfigProperties; @@ -15,6 +13,7 @@ import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.Experi import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.ExperimentalResourceDetectorModel; import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.IncludeExcludeModel; import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.ResourceModel; +import io.opentelemetry.sdk.internal.IncludeExcludePredicate; import io.opentelemetry.sdk.resources.Resource; import io.opentelemetry.sdk.resources.ResourceBuilder; import java.util.Collections; @@ -90,35 +89,6 @@ final class ResourceFactory implements Factory { if (included == null && excluded == null) { return ResourceFactory::matchAll; } - if (included == null) { - return excludedPredicate(excluded); - } - if (excluded == null) { - return includedPredicate(included); - } - return includedPredicate(included).and(excludedPredicate(excluded)); - } - - /** - * Returns a predicate which matches strings matching any of the {@code included} glob patterns. - */ - private static Predicate includedPredicate(List included) { - Predicate result = attributeKey -> false; - for (String include : included) { - result = result.or(createGlobPatternPredicate(include)); - } - return result; - } - - /** - * Returns a predicate which matches strings NOT matching any of the {@code excluded} glob - * patterns. - */ - private static Predicate excludedPredicate(List excluded) { - Predicate result = attributeKey -> true; - for (String exclude : excluded) { - result = result.and(createGlobPatternPredicate(exclude).negate()); - } - return result; + return IncludeExcludePredicate.createPatternMatching(included, excluded); } } diff --git a/sdk-extensions/incubator/src/main/java/io/opentelemetry/sdk/extension/incubator/fileconfig/SdkConfigProvider.java b/sdk-extensions/incubator/src/main/java/io/opentelemetry/sdk/extension/incubator/fileconfig/SdkConfigProvider.java index d23c379961..9bc5192a78 100644 --- a/sdk-extensions/incubator/src/main/java/io/opentelemetry/sdk/extension/incubator/fileconfig/SdkConfigProvider.java +++ b/sdk-extensions/incubator/src/main/java/io/opentelemetry/sdk/extension/incubator/fileconfig/SdkConfigProvider.java @@ -7,6 +7,7 @@ package io.opentelemetry.sdk.extension.incubator.fileconfig; import io.opentelemetry.api.incubator.config.ConfigProvider; import io.opentelemetry.api.incubator.config.DeclarativeConfigProperties; +import io.opentelemetry.common.ComponentLoader; import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.OpenTelemetryConfigurationModel; import javax.annotation.Nullable; @@ -15,9 +16,10 @@ public final class SdkConfigProvider implements ConfigProvider { @Nullable private final DeclarativeConfigProperties instrumentationConfig; - private SdkConfigProvider(OpenTelemetryConfigurationModel model) { + private SdkConfigProvider( + OpenTelemetryConfigurationModel model, ComponentLoader componentLoader) { DeclarativeConfigProperties configProperties = - DeclarativeConfiguration.toConfigProperties(model); + DeclarativeConfiguration.toConfigProperties(model, componentLoader); this.instrumentationConfig = configProperties.getStructured("instrumentation/development"); } @@ -28,7 +30,19 @@ public final class SdkConfigProvider implements ConfigProvider { * @return the {@link SdkConfigProvider} */ public static SdkConfigProvider create(OpenTelemetryConfigurationModel model) { - return new SdkConfigProvider(model); + return create(model, ComponentLoader.forClassLoader(SdkConfigProvider.class.getClassLoader())); + } + + /** + * Create a {@link SdkConfigProvider} from the {@code model}. + * + * @param model the configuration model + * @param componentLoader the component loader used to load SPIs + * @return the {@link SdkConfigProvider} + */ + public static SdkConfigProvider create( + OpenTelemetryConfigurationModel model, ComponentLoader componentLoader) { + return new SdkConfigProvider(model, componentLoader); } @Nullable diff --git a/sdk-extensions/incubator/src/main/java/io/opentelemetry/sdk/extension/incubator/fileconfig/ServiceResourceDetector.java b/sdk-extensions/incubator/src/main/java/io/opentelemetry/sdk/extension/incubator/fileconfig/ServiceResourceDetector.java index a8077032ec..dcaeeee271 100644 --- a/sdk-extensions/incubator/src/main/java/io/opentelemetry/sdk/extension/incubator/fileconfig/ServiceResourceDetector.java +++ b/sdk-extensions/incubator/src/main/java/io/opentelemetry/sdk/extension/incubator/fileconfig/ServiceResourceDetector.java @@ -38,7 +38,8 @@ public class ServiceResourceDetector implements ComponentProvider { public Resource create(DeclarativeConfigProperties config) { ResourceBuilder builder = Resource.builder(); - ConfigProperties properties = DefaultConfigProperties.create(Collections.emptyMap()); + ConfigProperties properties = + DefaultConfigProperties.create(Collections.emptyMap(), config.getComponentLoader()); String serviceName = properties.getString("otel.service.name"); if (serviceName != null) { builder.put(SERVICE_NAME, serviceName).build(); diff --git a/sdk-extensions/incubator/src/main/java/io/opentelemetry/sdk/extension/incubator/fileconfig/ViewFactory.java b/sdk-extensions/incubator/src/main/java/io/opentelemetry/sdk/extension/incubator/fileconfig/ViewFactory.java index a399ac724f..beeaa54f7d 100644 --- a/sdk-extensions/incubator/src/main/java/io/opentelemetry/sdk/extension/incubator/fileconfig/ViewFactory.java +++ b/sdk-extensions/incubator/src/main/java/io/opentelemetry/sdk/extension/incubator/fileconfig/ViewFactory.java @@ -7,12 +7,10 @@ package io.opentelemetry.sdk.extension.incubator.fileconfig; import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.IncludeExcludeModel; import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.ViewStreamModel; +import io.opentelemetry.sdk.internal.IncludeExcludePredicate; import io.opentelemetry.sdk.metrics.View; import io.opentelemetry.sdk.metrics.ViewBuilder; -import java.util.HashSet; import java.util.List; -import java.util.Set; -import javax.annotation.Nullable; final class ViewFactory implements Factory { @@ -35,7 +33,11 @@ final class ViewFactory implements Factory { } IncludeExcludeModel attributeKeys = model.getAttributeKeys(); if (attributeKeys != null) { - addAttributeKeyFilter(builder, attributeKeys.getIncluded(), attributeKeys.getExcluded()); + List included = attributeKeys.getIncluded(); + List excluded = attributeKeys.getExcluded(); + if (included != null || excluded != null) { + builder.setAttributeFilter(IncludeExcludePredicate.createExactMatching(included, excluded)); + } } if (model.getAggregation() != null) { builder.setAggregation( @@ -46,25 +48,4 @@ final class ViewFactory implements Factory { } return builder.build(); } - - private static void addAttributeKeyFilter( - ViewBuilder builder, @Nullable List included, @Nullable List excluded) { - if (included == null && excluded == null) { - return; - } - if (included == null) { - Set excludedKeys = new HashSet<>(excluded); - // TODO: set predicate with useful toString implementation - builder.setAttributeFilter(attributeKey -> !excludedKeys.contains(attributeKey)); - return; - } - if (excluded == null) { - Set includedKeys = new HashSet<>(included); - builder.setAttributeFilter(includedKeys); - return; - } - Set includedKeys = new HashSet<>(included); - excluded.forEach(includedKeys::remove); - builder.setAttributeFilter(includedKeys); - } } diff --git a/sdk-extensions/incubator/src/main/java/io/opentelemetry/sdk/extension/incubator/fileconfig/YamlDeclarativeConfigProperties.java b/sdk-extensions/incubator/src/main/java/io/opentelemetry/sdk/extension/incubator/fileconfig/YamlDeclarativeConfigProperties.java index 8a4a70704f..4be956af59 100644 --- a/sdk-extensions/incubator/src/main/java/io/opentelemetry/sdk/extension/incubator/fileconfig/YamlDeclarativeConfigProperties.java +++ b/sdk-extensions/incubator/src/main/java/io/opentelemetry/sdk/extension/incubator/fileconfig/YamlDeclarativeConfigProperties.java @@ -10,11 +10,9 @@ import static java.util.stream.Collectors.toList; import io.opentelemetry.api.incubator.config.DeclarativeConfigException; import io.opentelemetry.api.incubator.config.DeclarativeConfigProperties; -import io.opentelemetry.sdk.autoconfigure.internal.ComponentLoader; -import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.OpenTelemetryConfigurationModel; +import io.opentelemetry.common.ComponentLoader; import java.util.Arrays; import java.util.Collections; -import java.util.HashMap; import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.List; @@ -68,10 +66,10 @@ public final class YamlDeclarativeConfigProperties implements DeclarativeConfigP * com.fasterxml.jackson.databind.ObjectMapper}), and have values which are scalars, lists of * scalars, lists of maps, and maps. * - * @see DeclarativeConfiguration#toConfigProperties(OpenTelemetryConfigurationModel) + * @see DeclarativeConfiguration#toConfigProperties(Object) */ @SuppressWarnings("unchecked") - static YamlDeclarativeConfigProperties create( + public static YamlDeclarativeConfigProperties create( Map properties, ComponentLoader componentLoader) { Map simpleEntries = new LinkedHashMap<>(); Map> listEntries = new LinkedHashMap<>(); @@ -202,9 +200,13 @@ public final class YamlDeclarativeConfigProperties implements DeclarativeConfigP } Object value = simpleEntries.get(name); if (value instanceof List) { - return (List) - ((List) value) - .stream() + List objectList = ((List) value); + if (objectList.isEmpty()) { + return Collections.emptyList(); + } + List result = + (List) + objectList.stream() .map( entry -> { if (scalarType == String.class) { @@ -220,6 +222,10 @@ public final class YamlDeclarativeConfigProperties implements DeclarativeConfigP }) .filter(Objects::nonNull) .collect(toList()); + if (result.isEmpty()) { + return null; + } + return result; } return null; } @@ -296,18 +302,8 @@ public final class YamlDeclarativeConfigProperties implements DeclarativeConfigP return joiner.toString(); } - /** Return a map representation of the data. */ - public Map toMap() { - Map result = new HashMap<>(simpleEntries); - listEntries.forEach( - (key, value) -> - result.put( - key, value.stream().map(YamlDeclarativeConfigProperties::toMap).collect(toList()))); - mapEntries.forEach((key, value) -> result.put(key, value.toMap())); - return Collections.unmodifiableMap(result); - } - /** Return the {@link ComponentLoader}. */ + @Override public ComponentLoader getComponentLoader() { return componentLoader; } diff --git a/sdk-extensions/incubator/src/test/java/io/opentelemetry/sdk/extension/incubator/fileconfig/DeclarativeConfigurationCreateTest.java b/sdk-extensions/incubator/src/test/java/io/opentelemetry/sdk/extension/incubator/fileconfig/DeclarativeConfigurationCreateTest.java index 62ee078286..b72c0bc8eb 100644 --- a/sdk-extensions/incubator/src/test/java/io/opentelemetry/sdk/extension/incubator/fileconfig/DeclarativeConfigurationCreateTest.java +++ b/sdk-extensions/incubator/src/test/java/io/opentelemetry/sdk/extension/incubator/fileconfig/DeclarativeConfigurationCreateTest.java @@ -13,9 +13,9 @@ import static org.assertj.core.api.Assertions.assertThatThrownBy; import com.linecorp.armeria.testing.junit5.server.SelfSignedCertificateExtension; import io.github.netmikey.logunit.api.LogCapturer; import io.opentelemetry.api.incubator.config.DeclarativeConfigException; +import io.opentelemetry.common.ComponentLoader; import io.opentelemetry.internal.testing.CleanupExtension; import io.opentelemetry.sdk.OpenTelemetrySdk; -import io.opentelemetry.sdk.autoconfigure.internal.SpiHelper; import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.OpenTelemetryConfigurationModel; import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.SpanProcessorModel; import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.TracerProviderModel; @@ -161,7 +161,7 @@ class DeclarativeConfigurationCreateTest { DeclarativeConfiguration.create( model, // customizer is TestDeclarativeConfigurationCustomizerProvider - SpiHelper.serviceComponentLoader( + ComponentLoader.forClassLoader( DeclarativeConfigurationCreateTest.class.getClassLoader())); assertThat(sdk.toString()) .contains( diff --git a/sdk-extensions/incubator/src/test/java/io/opentelemetry/sdk/extension/incubator/fileconfig/DeclarativeConfigurationParseTest.java b/sdk-extensions/incubator/src/test/java/io/opentelemetry/sdk/extension/incubator/fileconfig/DeclarativeConfigurationParseTest.java index 0147cf8b68..1828462732 100644 --- a/sdk-extensions/incubator/src/test/java/io/opentelemetry/sdk/extension/incubator/fileconfig/DeclarativeConfigurationParseTest.java +++ b/sdk-extensions/incubator/src/test/java/io/opentelemetry/sdk/extension/incubator/fileconfig/DeclarativeConfigurationParseTest.java @@ -92,6 +92,7 @@ import java.util.Collections; import java.util.HashMap; import java.util.Map; import javax.annotation.Nullable; +import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; @@ -855,6 +856,27 @@ class DeclarativeConfigurationParseTest { .withTraceIdRatioBased(new TraceIdRatioBasedSamplerModel())))); } + @Test + void parse_quotedInput() { + String yaml = + "resource:\n" + + " attributes:\n" + + " - name: single_quote\n" + + " value: '\"single\"'\n" + + " - name: double_quote\n" + + " value: \"\\\"double\\\"\""; + + OpenTelemetryConfigurationModel model = + DeclarativeConfiguration.parse( + new ByteArrayInputStream(yaml.getBytes(StandardCharsets.UTF_8))); + + Assertions.assertNotNull(model.getResource()); + assertThat(model.getResource().getAttributes()) + .containsExactly( + new AttributeNameValueModel().withName("single_quote").withValue("\"single\""), + new AttributeNameValueModel().withName("double_quote").withValue("\"double\"")); + } + @ParameterizedTest @MethodSource("coreSchemaValuesArgs") void coreSchemaValues(String rawYaml, Object expectedYamlResult) { @@ -928,7 +950,7 @@ class DeclarativeConfigurationParseTest { // Undefined / empty environment variable Arguments.of("key1: ${EMPTY_STR}\n", mapOf(entry("key1", null))), Arguments.of("key1: ${STR_3}\n", mapOf(entry("key1", null))), - Arguments.of("key1: ${STR_1} ${STR_3}\n", mapOf(entry("key1", "value1"))), + Arguments.of("key1: ${STR_1} ${STR_3}\n", mapOf(entry("key1", "value1 "))), // Environment variable keys must match pattern: [a-zA-Z_]+[a-zA-Z0-9_]* Arguments.of("key1: ${VAR&}\n", mapOf(entry("key1", "${VAR&}"))), // Environment variable substitution only takes place in scalar values of maps @@ -938,13 +960,23 @@ class DeclarativeConfigurationParseTest { mapOf(entry("key1", mapOf(entry("${STR_1}", "value1"))))), Arguments.of( "key1:\n - ${STR_1}\n", mapOf(entry("key1", Collections.singletonList("${STR_1}")))), - // Quoted environment variables + // Double-quoted environment variables Arguments.of("key1: \"${HEX}\"\n", mapOf(entry("key1", "0xdeadbeef"))), Arguments.of("key1: \"${STR_1}\"\n", mapOf(entry("key1", "value1"))), Arguments.of("key1: \"${EMPTY_STR}\"\n", mapOf(entry("key1", ""))), Arguments.of("key1: \"${BOOL}\"\n", mapOf(entry("key1", "true"))), Arguments.of("key1: \"${INT}\"\n", mapOf(entry("key1", "1"))), Arguments.of("key1: \"${FLOAT}\"\n", mapOf(entry("key1", "1.1"))), + Arguments.of( + "key1: \"${HEX} ${BOOL} ${INT}\"\n", mapOf(entry("key1", "0xdeadbeef true 1"))), + // Single-quoted environment variables + Arguments.of("key1: '${HEX}'\n", mapOf(entry("key1", "0xdeadbeef"))), + Arguments.of("key1: '${STR_1}'\n", mapOf(entry("key1", "value1"))), + Arguments.of("key1: '${EMPTY_STR}'\n", mapOf(entry("key1", ""))), + Arguments.of("key1: '${BOOL}'\n", mapOf(entry("key1", "true"))), + Arguments.of("key1: '${INT}'\n", mapOf(entry("key1", "1"))), + Arguments.of("key1: '${FLOAT}'\n", mapOf(entry("key1", "1.1"))), + Arguments.of("key1: '${HEX} ${BOOL} ${INT}'\n", mapOf(entry("key1", "0xdeadbeef true 1"))), // Escaped Arguments.of("key1: ${FOO}\n", mapOf(entry("key1", "BAR"))), Arguments.of("key1: $${FOO}\n", mapOf(entry("key1", "${FOO}"))), diff --git a/sdk-extensions/incubator/src/test/java/io/opentelemetry/sdk/extension/incubator/fileconfig/MetricReaderFactoryTest.java b/sdk-extensions/incubator/src/test/java/io/opentelemetry/sdk/extension/incubator/fileconfig/MetricReaderFactoryTest.java index 6674822023..b0e546ea70 100644 --- a/sdk-extensions/incubator/src/test/java/io/opentelemetry/sdk/extension/incubator/fileconfig/MetricReaderFactoryTest.java +++ b/sdk-extensions/incubator/src/test/java/io/opentelemetry/sdk/extension/incubator/fileconfig/MetricReaderFactoryTest.java @@ -6,6 +6,7 @@ package io.opentelemetry.sdk.extension.incubator.fileconfig; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat; +import static java.util.Collections.singletonList; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; @@ -20,12 +21,14 @@ import io.opentelemetry.internal.testing.CleanupExtension; import io.opentelemetry.sdk.autoconfigure.internal.SpiHelper; import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.CardinalityLimitsModel; import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.ExperimentalPrometheusMetricExporterModel; +import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.IncludeExcludeModel; import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.MetricReaderModel; import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.OtlpHttpMetricExporterModel; import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.PeriodicMetricReaderModel; import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.PullMetricExporterModel; import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.PullMetricReaderModel; import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.PushMetricExporterModel; +import io.opentelemetry.sdk.internal.IncludeExcludePredicate; import io.opentelemetry.sdk.metrics.InstrumentType; import io.opentelemetry.sdk.metrics.export.MetricReader; import java.io.Closeable; @@ -92,7 +95,7 @@ class MetricReaderFactoryTest { @Test void create_PeriodicConfigured() { List closeables = new ArrayList<>(); - io.opentelemetry.sdk.metrics.export.MetricReader expectedReader = + MetricReader expectedReader = io.opentelemetry.sdk.metrics.export.PeriodicMetricReader.builder( OtlpHttpMetricExporter.getDefault()) .setInterval(Duration.ofMillis(1)) @@ -143,8 +146,7 @@ class MetricReaderFactoryTest { new ExperimentalPrometheusMetricExporterModel() .withPort(port)))), context); - io.opentelemetry.sdk.metrics.export.MetricReader reader = - readerAndCardinalityLimits.getMetricReader(); + MetricReader reader = readerAndCardinalityLimits.getMetricReader(); cleanup.addCloseable(reader); cleanup.addCloseables(closeables); @@ -160,7 +162,14 @@ class MetricReaderFactoryTest { List closeables = new ArrayList<>(); PrometheusHttpServer expectedReader = - PrometheusHttpServer.builder().setHost("localhost").setPort(port).build(); + PrometheusHttpServer.builder() + .setHost("localhost") + .setPort(port) + .setOtelScopeEnabled(false) + .setAllowedResourceAttributesFilter( + IncludeExcludePredicate.createPatternMatching( + singletonList("foo"), singletonList("bar"))) + .build(); // Close the reader to avoid port conflict with the new instance created by MetricReaderFactory expectedReader.close(); @@ -170,16 +179,22 @@ class MetricReaderFactoryTest { new MetricReaderModel() .withPull( new PullMetricReaderModel() + .withCardinalityLimits(new CardinalityLimitsModel().withDefault(100)) .withExporter( new PullMetricExporterModel() .withPrometheusDevelopment( new ExperimentalPrometheusMetricExporterModel() .withHost("localhost") - .withPort(port))) - .withCardinalityLimits(new CardinalityLimitsModel().withDefault(100))), + .withPort(port) + .withWithResourceConstantLabels( + new IncludeExcludeModel() + .withIncluded(singletonList("foo")) + .withExcluded(singletonList("bar"))) + .withWithoutScopeInfo(true) + .withWithoutTypeSuffix(true) + .withWithoutUnits(true)))), context); - io.opentelemetry.sdk.metrics.export.MetricReader reader = - readerAndCardinalityLimits.getMetricReader(); + MetricReader reader = readerAndCardinalityLimits.getMetricReader(); cleanup.addCloseable(reader); cleanup.addCloseables(closeables); diff --git a/sdk-extensions/incubator/src/test/java/io/opentelemetry/sdk/extension/incubator/fileconfig/ViewFactoryTest.java b/sdk-extensions/incubator/src/test/java/io/opentelemetry/sdk/extension/incubator/fileconfig/ViewFactoryTest.java index 825bc5e1f4..c39ae051e3 100644 --- a/sdk-extensions/incubator/src/test/java/io/opentelemetry/sdk/extension/incubator/fileconfig/ViewFactoryTest.java +++ b/sdk-extensions/incubator/src/test/java/io/opentelemetry/sdk/extension/incubator/fileconfig/ViewFactoryTest.java @@ -12,9 +12,10 @@ import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.Aggreg import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.ExplicitBucketHistogramAggregationModel; import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.IncludeExcludeModel; import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.ViewStreamModel; +import io.opentelemetry.sdk.internal.IncludeExcludePredicate; import io.opentelemetry.sdk.metrics.View; import java.util.Arrays; -import java.util.HashSet; +import java.util.Collections; import org.junit.jupiter.api.Test; class ViewFactoryTest { @@ -38,7 +39,9 @@ class ViewFactoryTest { View.builder() .setName("name") .setDescription("description") - .setAttributeFilter(new HashSet<>(Arrays.asList("foo", "bar"))) + .setAttributeFilter( + IncludeExcludePredicate.createExactMatching( + Arrays.asList("foo", "bar"), Collections.singletonList("baz"))) .setAggregation( io.opentelemetry.sdk.metrics.Aggregation.explicitBucketHistogram( Arrays.asList(1.0, 2.0))) @@ -51,7 +54,9 @@ class ViewFactoryTest { .withName("name") .withDescription("description") .withAttributeKeys( - new IncludeExcludeModel().withIncluded(Arrays.asList("foo", "bar"))) + new IncludeExcludeModel() + .withIncluded(Arrays.asList("foo", "bar")) + .withExcluded(Collections.singletonList("baz"))) .withAggregation( new AggregationModel() .withExplicitBucketHistogram( diff --git a/sdk/common/build.gradle.kts b/sdk/common/build.gradle.kts index 8f6f309535..c092ec2144 100644 --- a/sdk/common/build.gradle.kts +++ b/sdk/common/build.gradle.kts @@ -4,6 +4,7 @@ plugins { id("otel.java-conventions") id("otel.publish-conventions") id("otel.animalsniffer-conventions") + id("otel.jmh-conventions") } apply() diff --git a/sdk/common/src/jmh/java/io/opentelemetry/sdk/internal/StacktraceRenderBenchmark.java b/sdk/common/src/jmh/java/io/opentelemetry/sdk/internal/StacktraceRenderBenchmark.java new file mode 100644 index 0000000000..04121e9ae8 --- /dev/null +++ b/sdk/common/src/jmh/java/io/opentelemetry/sdk/internal/StacktraceRenderBenchmark.java @@ -0,0 +1,107 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.sdk.internal; + +import java.io.PrintStream; +import java.io.PrintWriter; +import java.io.StringWriter; +import java.util.concurrent.TimeUnit; +import java.util.function.BiFunction; +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.Param; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.Threads; +import org.openjdk.jmh.annotations.Warmup; + +/** + * This benchmark compares the performance of {@link StackTraceRenderer}, the custom length limit + * aware exception render, to the built-in JDK stacktrace renderer {@link + * Throwable#printStackTrace(PrintStream)}. + */ +@BenchmarkMode({Mode.AverageTime}) +@OutputTimeUnit(TimeUnit.NANOSECONDS) +@Warmup(iterations = 5, time = 100, timeUnit = TimeUnit.MILLISECONDS) +@Measurement(iterations = 5, time = 100, timeUnit = TimeUnit.MILLISECONDS) +@Fork(1) +@SuppressWarnings("StaticAssignmentOfThrowable") +public class StacktraceRenderBenchmark { + + private static final Exception simple = new Exception("error"); + private static final Exception complex = + new Exception("error", new Exception("cause1", new Exception("cause2"))); + + static { + complex.addSuppressed(new Exception("suppressed1")); + complex.addSuppressed(new Exception("suppressed2", new Exception("cause"))); + } + + @State(Scope.Benchmark) + public static class BenchmarkState { + + @Param Renderer renderer; + @Param ExceptionParam exceptionParam; + + @Param({"10", "1000", "100000"}) + int lengthLimit; + } + + @SuppressWarnings("ImmutableEnumChecker") + public enum Renderer { + JDK( + (throwable, limit) -> { + StringWriter stringWriter = new StringWriter(); + try (PrintWriter printWriter = new PrintWriter(stringWriter)) { + throwable.printStackTrace(printWriter); + } + String stacktrace = stringWriter.toString(); + return stacktrace.substring(0, Math.min(stacktrace.length(), limit)); + }), + CUSTOM((throwable, limit) -> new StackTraceRenderer(throwable, limit).render()); + + private final BiFunction renderer; + + Renderer(BiFunction renderer) { + this.renderer = renderer; + } + + BiFunction renderer() { + return renderer; + } + } + + @SuppressWarnings("ImmutableEnumChecker") + public enum ExceptionParam { + SIMPLE(simple), + COMPLEX(complex); + + private final Throwable throwable; + + ExceptionParam(Throwable throwable) { + this.throwable = throwable; + } + + Throwable throwable() { + return throwable; + } + } + + @Benchmark + @Threads(1) + @SuppressWarnings("ReturnValueIgnored") + public void render(BenchmarkState benchmarkState) { + BiFunction renderer = benchmarkState.renderer.renderer(); + Throwable throwable = benchmarkState.exceptionParam.throwable(); + int limit = benchmarkState.lengthLimit; + + renderer.apply(throwable, limit); + } +} diff --git a/sdk/common/src/main/java/io/opentelemetry/sdk/internal/DefaultExceptionAttributeResolver.java b/sdk/common/src/main/java/io/opentelemetry/sdk/internal/DefaultExceptionAttributeResolver.java index 44061c1644..f3b75731c7 100644 --- a/sdk/common/src/main/java/io/opentelemetry/sdk/internal/DefaultExceptionAttributeResolver.java +++ b/sdk/common/src/main/java/io/opentelemetry/sdk/internal/DefaultExceptionAttributeResolver.java @@ -9,19 +9,24 @@ import java.io.PrintWriter; import java.io.StringWriter; /** - * This class is internal and experimental. Its APIs are unstable and can change at any time. Its + * The default {@link ExceptionAttributeResolver}, populating standard {@code exception.*} as + * defined by the semantic conventions. + * + *

This class is internal and experimental. Its APIs are unstable and can change at any time. Its * APIs (or a version of them) may be promoted to the public stable API in the future, but no * guarantees are made. + * + * @see ExceptionAttributeResolver#getDefault() + * @see ExceptionAttributeResolver#getDefault(boolean) () */ -public final class DefaultExceptionAttributeResolver implements ExceptionAttributeResolver { +final class DefaultExceptionAttributeResolver implements ExceptionAttributeResolver { - private static final DefaultExceptionAttributeResolver INSTANCE = - new DefaultExceptionAttributeResolver(); + static final String ENABLE_JVM_STACKTRACE_PROPERTY = "otel.experimental.sdk.jvm_stacktrace"; - private DefaultExceptionAttributeResolver() {} + private final boolean jvmStacktraceEnabled; - public static ExceptionAttributeResolver getInstance() { - return INSTANCE; + DefaultExceptionAttributeResolver(boolean jvmStacktraceEnabled) { + this.jvmStacktraceEnabled = jvmStacktraceEnabled; } @Override @@ -37,14 +42,23 @@ public final class DefaultExceptionAttributeResolver implements ExceptionAttribu attributeSetter.setAttribute(ExceptionAttributeResolver.EXCEPTION_MESSAGE, exceptionMessage); } + String exceptionStacktrace = + jvmStacktraceEnabled + ? jvmStacktrace(throwable) + : limitsAwareStacktrace(throwable, maxAttributeLength); + attributeSetter.setAttribute( + ExceptionAttributeResolver.EXCEPTION_STACKTRACE, exceptionStacktrace); + } + + private static String jvmStacktrace(Throwable throwable) { StringWriter stringWriter = new StringWriter(); try (PrintWriter printWriter = new PrintWriter(stringWriter)) { throwable.printStackTrace(printWriter); } - String exceptionStacktrace = stringWriter.toString(); - if (exceptionStacktrace != null) { - attributeSetter.setAttribute( - ExceptionAttributeResolver.EXCEPTION_STACKTRACE, exceptionStacktrace); - } + return stringWriter.toString(); + } + + private static String limitsAwareStacktrace(Throwable throwable, int maxAttributeLength) { + return new StackTraceRenderer(throwable, maxAttributeLength).render(); } } diff --git a/sdk/common/src/main/java/io/opentelemetry/sdk/internal/ExceptionAttributeResolver.java b/sdk/common/src/main/java/io/opentelemetry/sdk/internal/ExceptionAttributeResolver.java index 4b9d7a8dc9..9e6378e880 100644 --- a/sdk/common/src/main/java/io/opentelemetry/sdk/internal/ExceptionAttributeResolver.java +++ b/sdk/common/src/main/java/io/opentelemetry/sdk/internal/ExceptionAttributeResolver.java @@ -5,7 +5,10 @@ package io.opentelemetry.sdk.internal; +import static io.opentelemetry.sdk.internal.DefaultExceptionAttributeResolver.ENABLE_JVM_STACKTRACE_PROPERTY; + import io.opentelemetry.api.common.AttributeKey; +import io.opentelemetry.api.internal.ConfigUtil; import javax.annotation.Nullable; /** @@ -24,6 +27,26 @@ public interface ExceptionAttributeResolver { void setExceptionAttributes( AttributeSetter attributeSetter, Throwable throwable, int maxAttributeLength); + /** + * Return the default exception attribute resolver, setting {@code jvmStacktraceEnabled} based on + * {@link DefaultExceptionAttributeResolver#ENABLE_JVM_STACKTRACE_PROPERTY}. + */ + static ExceptionAttributeResolver getDefault() { + return getDefault( + Boolean.parseBoolean(ConfigUtil.getString(ENABLE_JVM_STACKTRACE_PROPERTY, "false"))); + } + + /** + * Return the default exception attribute resolver. + * + * @param jvmStacktraceEnabled if true, resolve stacktrace using the stacktrace renderer built + * into the JVM. This built in JVM renderer is not attribute limits aware, and may utilize + * more CPU / memory than is needed. Most users will prefer to set this to {@code false}. + */ + static ExceptionAttributeResolver getDefault(boolean jvmStacktraceEnabled) { + return new DefaultExceptionAttributeResolver(jvmStacktraceEnabled); + } + /** * This class is internal and experimental. Its APIs are unstable and can change at any time. Its * APIs (or a version of them) may be promoted to the public stable API in the future, but no diff --git a/sdk/common/src/main/java/io/opentelemetry/sdk/internal/IncludeExcludePredicate.java b/sdk/common/src/main/java/io/opentelemetry/sdk/internal/IncludeExcludePredicate.java new file mode 100644 index 0000000000..fe1db4ca88 --- /dev/null +++ b/sdk/common/src/main/java/io/opentelemetry/sdk/internal/IncludeExcludePredicate.java @@ -0,0 +1,122 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.sdk.internal; + +import static io.opentelemetry.sdk.internal.GlobUtil.createGlobPatternPredicate; +import static java.util.stream.Collectors.joining; + +import java.util.Collection; +import java.util.LinkedHashSet; +import java.util.Set; +import java.util.StringJoiner; +import java.util.function.Predicate; +import javax.annotation.Nullable; + +/** + * A predicate that evaluates if a string matches a configurable set of {@code included} and {@code + * excluded} string. + * + *

Supports optional glob pattern matching. See {@link GlobUtil}. + * + *

String equality is evaluated using {@link String#equalsIgnoreCase(String)}. + * + *

This class is internal and is hence not for public use. Its APIs are unstable and can change + * at any time. + */ +public final class IncludeExcludePredicate implements Predicate { + + private final boolean globMatchingEnabled; + @Nullable private final Set included; + @Nullable private final Set excluded; + private final Predicate predicate; + + private IncludeExcludePredicate( + @Nullable Collection included, + @Nullable Collection excluded, + boolean globMatchingEnabled) { + this.globMatchingEnabled = globMatchingEnabled; + this.included = included == null ? null : new LinkedHashSet<>(included); + this.excluded = excluded == null ? null : new LinkedHashSet<>(excluded); + if (this.included != null && this.excluded != null) { + this.predicate = + includedPredicate(this.included, globMatchingEnabled) + .and(excludedPredicate(this.excluded, globMatchingEnabled)); + } else if (this.included == null && this.excluded != null) { + this.predicate = excludedPredicate(this.excluded, globMatchingEnabled); + } else if (this.excluded == null && this.included != null) { + this.predicate = includedPredicate(this.included, globMatchingEnabled); + } else { + throw new IllegalArgumentException( + "At least one of includedPatterns or excludedPatterns must not be null"); + } + } + + /** + * Create a (case-insensitive) exact matching include exclude predicate. + * + * @throws IllegalArgumentException if {@code included} AND {@code excluded} are null. + */ + public static Predicate createExactMatching( + @Nullable Collection included, @Nullable Collection excluded) { + return new IncludeExcludePredicate(included, excluded, /* globMatchingEnabled= */ false); + } + + /** + * Create a pattern matching include exclude predicate. + * + *

See {@link GlobUtil} for pattern matching details. + * + * @throws IllegalArgumentException if {@code included} AND {@code excluded} are null. + */ + public static Predicate createPatternMatching( + @Nullable Collection included, @Nullable Collection excluded) { + return new IncludeExcludePredicate(included, excluded, /* globMatchingEnabled= */ true); + } + + @Override + public boolean test(String s) { + return predicate.test(s); + } + + @Override + public String toString() { + StringJoiner joiner = new StringJoiner(", ", "IncludeExcludePredicate{", "}"); + joiner.add("globMatchingEnabled=" + globMatchingEnabled); + if (included != null) { + joiner.add("included=" + included.stream().collect(joining(", ", "[", "]"))); + } + if (excluded != null) { + joiner.add("excluded=" + excluded.stream().collect(joining(", ", "[", "]"))); + } + return joiner.toString(); + } + + private static Predicate includedPredicate( + Set included, boolean globMatchingEnabled) { + Predicate result = attributeKey -> false; + for (String include : included) { + if (globMatchingEnabled) { + result = result.or(createGlobPatternPredicate(include)); + } else { + result = result.or(include::equalsIgnoreCase); + } + } + return result; + } + + private static Predicate excludedPredicate( + Set excluded, boolean globMatchingEnabled) { + Predicate result = attributeKey -> true; + for (String exclude : excluded) { + if (globMatchingEnabled) { + result = result.and(createGlobPatternPredicate(exclude).negate()); + } else { + result = result.and(s -> !exclude.equalsIgnoreCase(s)); + } + } + return result; + } +} diff --git a/sdk/common/src/main/java/io/opentelemetry/sdk/internal/StackTraceRenderer.java b/sdk/common/src/main/java/io/opentelemetry/sdk/internal/StackTraceRenderer.java new file mode 100644 index 0000000000..fe89e5481b --- /dev/null +++ b/sdk/common/src/main/java/io/opentelemetry/sdk/internal/StackTraceRenderer.java @@ -0,0 +1,154 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.sdk.internal; + +import java.io.PrintStream; +import java.util.Collections; +import java.util.IdentityHashMap; +import java.util.Set; + +/** + * An alternative to exception stacktrace renderer that replicates the behavior of {@link + * Throwable#printStackTrace(PrintStream)}, but which is aware of a maximum stacktrace length limit, + * and exits early when the length limit has been exceeded to avoid unnecessary computation. + * + *

Instances should only be used once. + */ +class StackTraceRenderer { + + private static final String CAUSED_BY = "Caused by: "; + private static final String SUPPRESSED = "Suppressed: "; + + private final Throwable throwable; + private final int lengthLimit; + private final StringBuilder builder = new StringBuilder(); + + StackTraceRenderer(Throwable throwable, int lengthLimit) { + this.throwable = throwable; + this.lengthLimit = lengthLimit; + } + + String render() { + if (builder.length() == 0) { + appendStackTrace(); + } + + return builder.substring(0, Math.min(builder.length(), lengthLimit)); + } + + private void appendStackTrace() { + builder.append(throwable).append(System.lineSeparator()); + if (isOverLimit()) { + return; + } + + StackTraceElement[] stackTraceElements = throwable.getStackTrace(); + for (StackTraceElement stackTraceElement : stackTraceElements) { + builder.append("\tat ").append(stackTraceElement).append(System.lineSeparator()); + if (isOverLimit()) { + return; + } + } + + Set seen = Collections.newSetFromMap(new IdentityHashMap<>()); + seen.add(throwable); + + for (Throwable suppressed : throwable.getSuppressed()) { + appendInnerStacktrace(stackTraceElements, suppressed, "\t", SUPPRESSED, seen); + } + + Throwable cause = throwable.getCause(); + if (cause != null) { + appendInnerStacktrace(stackTraceElements, cause, "", CAUSED_BY, seen); + } + } + + /** + * Append the {@code innerThrowable} to the {@link #builder}, returning {@code true} if the + * builder now exceeds the length limit. + */ + private boolean appendInnerStacktrace( + StackTraceElement[] parentElements, + Throwable innerThrowable, + String prefix, + String caption, + Set seen) { + if (seen.contains(innerThrowable)) { + builder + .append(prefix) + .append(caption) + .append("[CIRCULAR REFERENCE: ") + .append(innerThrowable) + .append("]") + .append(System.lineSeparator()); + return true; + } + seen.add(innerThrowable); + + // Iterating back to front, compute the lastSharedFrameIndex, which tracks the point at which + // this exception's stacktrace elements start repeating the parent's elements + StackTraceElement[] currentElements = innerThrowable.getStackTrace(); + int parentIndex = parentElements.length - 1; + int lastSharedFrameIndex = currentElements.length - 1; + while (true) { + if (parentIndex < 0 || lastSharedFrameIndex < 0) { + break; + } + if (!parentElements[parentIndex].equals(currentElements[lastSharedFrameIndex])) { + break; + } + parentIndex--; + lastSharedFrameIndex--; + } + + builder.append(prefix).append(caption).append(innerThrowable).append(System.lineSeparator()); + if (isOverLimit()) { + return true; + } + + for (int i = 0; i <= lastSharedFrameIndex; i++) { + StackTraceElement stackTraceElement = currentElements[i]; + builder + .append(prefix) + .append("\tat ") + .append(stackTraceElement) + .append(System.lineSeparator()); + if (isOverLimit()) { + return true; + } + } + + int duplicateFrames = currentElements.length - 1 - lastSharedFrameIndex; + if (duplicateFrames != 0) { + builder + .append(prefix) + .append("\t... ") + .append(duplicateFrames) + .append(" more") + .append(System.lineSeparator()); + if (isOverLimit()) { + return true; + } + } + + for (Throwable suppressed : innerThrowable.getSuppressed()) { + if (appendInnerStacktrace(currentElements, suppressed, prefix + "\t", SUPPRESSED, seen)) { + return true; + } + } + + Throwable cause = innerThrowable.getCause(); + if (cause != null) { + return appendInnerStacktrace(currentElements, cause, prefix, CAUSED_BY, seen); + } + + return false; + } + + private boolean isOverLimit() { + return builder.length() >= lengthLimit; + } +} diff --git a/sdk/common/src/test/java/io/opentelemetry/sdk/internal/DefaultExceptionAttributeResolverTest.java b/sdk/common/src/test/java/io/opentelemetry/sdk/internal/DefaultExceptionAttributeResolverTest.java new file mode 100644 index 0000000000..5c4446f0cb --- /dev/null +++ b/sdk/common/src/test/java/io/opentelemetry/sdk/internal/DefaultExceptionAttributeResolverTest.java @@ -0,0 +1,76 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.sdk.internal; + +import static org.assertj.core.api.Assertions.assertThat; + +import io.opentelemetry.api.common.AttributeKey; +import java.util.HashMap; +import java.util.Map; +import java.util.function.Predicate; +import java.util.stream.Stream; +import javax.annotation.Nullable; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +class DefaultExceptionAttributeResolverTest { + + @ParameterizedTest + @MethodSource("setExceptionAttributesArgs") + void setExceptionAttributes( + boolean jvmStacktraceEnabled, + Throwable throwable, + int maxAttributeLength, + String expectedMessage, + String expectedType, + Predicate validStacktrace) { + ExceptionAttributeResolver resolver = + ExceptionAttributeResolver.getDefault(jvmStacktraceEnabled); + Map attributes = new HashMap<>(); + + resolver.setExceptionAttributes( + new ExceptionAttributeResolver.AttributeSetter() { + @Override + public void setAttribute(AttributeKey key, @Nullable T value) { + attributes.put(key.toString(), value); + } + }, + throwable, + maxAttributeLength); + + assertThat(attributes.get(ExceptionAttributeResolver.EXCEPTION_MESSAGE.getKey())) + .isEqualTo(expectedMessage); + assertThat(attributes.get(ExceptionAttributeResolver.EXCEPTION_TYPE.getKey())) + .isEqualTo(expectedType); + assertThat((String) attributes.get(ExceptionAttributeResolver.EXCEPTION_STACKTRACE.getKey())) + .matches(validStacktrace); + } + + private static Stream setExceptionAttributesArgs() { + return Stream.of( + // When jvmStacktraceEnabled=true, limit is ignored + Arguments.of( + true, + new Exception("error"), + 10, + "error", + "java.lang.Exception", + predicate(stacktrace -> stacktrace.length() > 10)), + // When jvmStacktraceEnabled=false, limit is adhered + Arguments.of( + false, + new Exception("error"), + 10, + "error", + "java.lang.Exception", + predicate(stacktrace -> stacktrace.length() == 10))); + } + + private static Predicate predicate(Predicate predicate) { + return predicate; + } +} diff --git a/sdk/common/src/test/java/io/opentelemetry/sdk/internal/IncludeExcludePredicateTest.java b/sdk/common/src/test/java/io/opentelemetry/sdk/internal/IncludeExcludePredicateTest.java new file mode 100644 index 0000000000..98b582f007 --- /dev/null +++ b/sdk/common/src/test/java/io/opentelemetry/sdk/internal/IncludeExcludePredicateTest.java @@ -0,0 +1,115 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.sdk.internal; + +import static java.util.Arrays.asList; +import static java.util.Collections.singletonList; +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.function.Predicate; +import java.util.stream.Stream; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +class IncludeExcludePredicateTest { + + private static final Predicate EXACT_INCLUDE = + IncludeExcludePredicate.createExactMatching(singletonList("foo"), null); + private static final Predicate EXACT_EXCLUDE = + IncludeExcludePredicate.createExactMatching(null, singletonList("bar")); + private static final Predicate EXACT_INCLUDE_AND_EXCLUDE = + IncludeExcludePredicate.createExactMatching(singletonList("foo"), singletonList("bar")); + private static final Predicate EXACT_MULTI = + IncludeExcludePredicate.createExactMatching(asList("foo", "fooo"), asList("bar", "barr")); + + private static final Predicate PATTERN_INCLUDE = + IncludeExcludePredicate.createPatternMatching(singletonList("f?o"), null); + private static final Predicate PATTERN_EXCLUDE = + IncludeExcludePredicate.createPatternMatching(null, singletonList("b?r")); + private static final Predicate PATTERN_INCLUDE_AND_EXCLUDE = + IncludeExcludePredicate.createPatternMatching(singletonList("f?o"), singletonList("b?r")); + private static final Predicate PATTERN_MULTI = + IncludeExcludePredicate.createPatternMatching(asList("f?o", "f?oo"), asList("b?r", "b?rr")); + + @ParameterizedTest + @MethodSource("testArgs") + void test(Predicate predicate, String testCase, boolean expectedResult) { + assertThat(predicate.test(testCase)).isEqualTo(expectedResult); + } + + private static Stream testArgs() { + return Stream.of( + // exact matching + // include only + Arguments.of(EXACT_INCLUDE, "foo", true), + Arguments.of(EXACT_INCLUDE, "bar", false), + Arguments.of(EXACT_INCLUDE, "baz", false), + // exclude only + Arguments.of(EXACT_EXCLUDE, "foo", true), + Arguments.of(EXACT_EXCLUDE, "bar", false), + Arguments.of(EXACT_EXCLUDE, "baz", true), + // include and exclude + Arguments.of(EXACT_INCLUDE_AND_EXCLUDE, "foo", true), + Arguments.of(EXACT_INCLUDE_AND_EXCLUDE, "bar", false), + Arguments.of(EXACT_INCLUDE_AND_EXCLUDE, "baz", false), + // multi + Arguments.of(EXACT_MULTI, "foo", true), + Arguments.of(EXACT_MULTI, "fooo", true), + Arguments.of(EXACT_MULTI, "bar", false), + Arguments.of(EXACT_MULTI, "barr", false), + Arguments.of(EXACT_MULTI, "baz", false), + // pattern matching + // include only + Arguments.of(PATTERN_INCLUDE, "foo", true), + Arguments.of(PATTERN_INCLUDE, "bar", false), + Arguments.of(PATTERN_INCLUDE, "baz", false), + // exclude only + Arguments.of(PATTERN_EXCLUDE, "foo", true), + Arguments.of(PATTERN_EXCLUDE, "bar", false), + Arguments.of(PATTERN_EXCLUDE, "baz", true), + // include and exclude + Arguments.of(PATTERN_INCLUDE_AND_EXCLUDE, "foo", true), + Arguments.of(PATTERN_INCLUDE_AND_EXCLUDE, "bar", false), + Arguments.of(PATTERN_INCLUDE_AND_EXCLUDE, "baz", false), + // multi + Arguments.of(PATTERN_MULTI, "foo", true), + Arguments.of(PATTERN_MULTI, "fooo", true), + Arguments.of(PATTERN_MULTI, "bar", false), + Arguments.of(PATTERN_MULTI, "barr", false), + Arguments.of(PATTERN_MULTI, "baz", false)); + } + + @ParameterizedTest + @MethodSource("stringRepresentationArgs") + void stringRepresentation(Predicate predicate, String exepectedString) { + assertThat(predicate.toString()).isEqualTo(exepectedString); + } + + private static Stream stringRepresentationArgs() { + return Stream.of( + Arguments.of( + EXACT_INCLUDE, "IncludeExcludePredicate{globMatchingEnabled=false, included=[foo]}"), + Arguments.of( + EXACT_EXCLUDE, "IncludeExcludePredicate{globMatchingEnabled=false, excluded=[bar]}"), + Arguments.of( + EXACT_INCLUDE_AND_EXCLUDE, + "IncludeExcludePredicate{globMatchingEnabled=false, included=[foo], excluded=[bar]}"), + Arguments.of( + EXACT_MULTI, + "IncludeExcludePredicate{globMatchingEnabled=false, included=[foo, fooo], excluded=[bar, barr]}"), + Arguments.of( + PATTERN_INCLUDE, "IncludeExcludePredicate{globMatchingEnabled=true, included=[f?o]}"), + Arguments.of( + PATTERN_EXCLUDE, "IncludeExcludePredicate{globMatchingEnabled=true, excluded=[b?r]}"), + Arguments.of( + PATTERN_INCLUDE_AND_EXCLUDE, + "IncludeExcludePredicate{globMatchingEnabled=true, included=[f?o], excluded=[b?r]}"), + Arguments.of( + PATTERN_MULTI, + "IncludeExcludePredicate{globMatchingEnabled=true, included=[f?o, f?oo], excluded=[b?r, b?rr]}")); + } +} diff --git a/sdk/common/src/test/java/io/opentelemetry/sdk/internal/StackTraceRendererTest.java b/sdk/common/src/test/java/io/opentelemetry/sdk/internal/StackTraceRendererTest.java new file mode 100644 index 0000000000..a4a4605eae --- /dev/null +++ b/sdk/common/src/test/java/io/opentelemetry/sdk/internal/StackTraceRendererTest.java @@ -0,0 +1,101 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.sdk.internal; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.io.PrintWriter; +import java.io.StringWriter; +import java.util.Random; +import java.util.stream.Stream; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +class StackTraceRendererTest { + + private static final Random random = new Random(); + + @ParameterizedTest + @MethodSource("renderStacktraceArgs") + void renderStacktrace_randomLength(Throwable throwable) { + // Test equal stacktrace for random lengths to test edges + for (int i = 0; i < 100; i++) { + int length = random.nextInt(10_000); + assertThat(new StackTraceRenderer(throwable, length).render()) + .isEqualTo(jdkStackTrace(throwable, length)); + } + } + + @ParameterizedTest + @MethodSource("renderStacktraceArgs") + void renderStacktrace_fixedLengths(Throwable throwable) { + assertThat(new StackTraceRenderer(throwable, 10).render()) + .isEqualTo(jdkStackTrace(throwable, 10)); + assertThat(new StackTraceRenderer(throwable, 100).render()) + .isEqualTo(jdkStackTrace(throwable, 100)); + assertThat(new StackTraceRenderer(throwable, 1000).render()) + .isEqualTo(jdkStackTrace(throwable, 1000)); + assertThat(new StackTraceRenderer(throwable, Integer.MAX_VALUE).render()) + .isEqualTo(jdkStackTrace(throwable, Integer.MAX_VALUE)); + } + + private static Stream renderStacktraceArgs() { + Exception withCycle = new Exception("error1"); + Exception withCycleInner = new Exception("error2", withCycle); + withCycle.initCause(withCycleInner); + + Exception withSuppressed = new Exception("error"); + withSuppressed.addSuppressed(new Exception("suppressed")); + + Exception withMultipleSuppressed = new Exception("error"); + withMultipleSuppressed.addSuppressed(new Exception("suppressed1")); + withMultipleSuppressed.addSuppressed(new Exception("suppressed2")); + + Exception withNestedSuppressed = new Exception("error"); + withNestedSuppressed.addSuppressed(withSuppressed); + withNestedSuppressed.addSuppressed(withMultipleSuppressed); + + Exception withKitchenSink = new Exception("kitchenSink", withCycle); + withKitchenSink.addSuppressed(withMultipleSuppressed); + withKitchenSink.addSuppressed(withNestedSuppressed); + + return Stream.of( + // simple + Arguments.of(new Exception()), + Arguments.of(new Exception("error")), + // with cause + Arguments.of(new Exception(new Exception("cause"))), + Arguments.of(new Exception("error", new Exception("cause"))), + // with nested causes + Arguments.of( + new Exception( + "error", + new Exception( + "cause1", + new Exception( + "cause2", + new Exception( + "cause3", new Exception("cause4", new Exception("cause5"))))))), + // with cause with circular reference + Arguments.of(withCycle), + // with suppressed + Arguments.of(withSuppressed), + Arguments.of(withMultipleSuppressed), + Arguments.of(withNestedSuppressed), + // with cause, cycle, and suppressed! + Arguments.of(withKitchenSink)); + } + + private static String jdkStackTrace(Throwable exception, int limit) { + StringWriter stringWriter = new StringWriter(); + try (PrintWriter printWriter = new PrintWriter(stringWriter)) { + exception.printStackTrace(printWriter); + } + String stacktrace = stringWriter.toString(); + return stacktrace.substring(0, Math.min(stacktrace.length(), limit)); + } +} diff --git a/sdk/logs/src/main/java/io/opentelemetry/sdk/logs/ExtendedSdkLogger.java b/sdk/logs/src/main/java/io/opentelemetry/sdk/logs/ExtendedSdkLogger.java index cab91c25a5..5ceb6414f4 100644 --- a/sdk/logs/src/main/java/io/opentelemetry/sdk/logs/ExtendedSdkLogger.java +++ b/sdk/logs/src/main/java/io/opentelemetry/sdk/logs/ExtendedSdkLogger.java @@ -7,6 +7,8 @@ package io.opentelemetry.sdk.logs; import io.opentelemetry.api.incubator.logs.ExtendedLogRecordBuilder; import io.opentelemetry.api.incubator.logs.ExtendedLogger; +import io.opentelemetry.api.logs.Severity; +import io.opentelemetry.context.Context; import io.opentelemetry.sdk.common.InstrumentationScopeInfo; import io.opentelemetry.sdk.logs.internal.LoggerConfig; @@ -22,8 +24,8 @@ final class ExtendedSdkLogger extends SdkLogger implements ExtendedLogger { @Override @SuppressWarnings("RedundantOverride") - public boolean isEnabled() { - return super.isEnabled(); + public boolean isEnabled(Severity severity, Context context) { + return super.isEnabled(severity, context); } @Override diff --git a/sdk/logs/src/main/java/io/opentelemetry/sdk/logs/SdkLogger.java b/sdk/logs/src/main/java/io/opentelemetry/sdk/logs/SdkLogger.java index 2f8b950674..7539a5c3b1 100644 --- a/sdk/logs/src/main/java/io/opentelemetry/sdk/logs/SdkLogger.java +++ b/sdk/logs/src/main/java/io/opentelemetry/sdk/logs/SdkLogger.java @@ -8,6 +8,8 @@ package io.opentelemetry.sdk.logs; import io.opentelemetry.api.logs.LogRecordBuilder; import io.opentelemetry.api.logs.Logger; import io.opentelemetry.api.logs.LoggerProvider; +import io.opentelemetry.api.logs.Severity; +import io.opentelemetry.context.Context; import io.opentelemetry.sdk.common.InstrumentationScopeInfo; import io.opentelemetry.sdk.logs.internal.LoggerConfig; @@ -69,7 +71,8 @@ class SdkLogger implements Logger { return instrumentationScopeInfo; } - public boolean isEnabled() { + // Visible for testing + public boolean isEnabled(Severity severity, Context context) { return loggerEnabled; } diff --git a/sdk/logs/src/main/java/io/opentelemetry/sdk/logs/SdkLoggerProviderBuilder.java b/sdk/logs/src/main/java/io/opentelemetry/sdk/logs/SdkLoggerProviderBuilder.java index 25fdeaa445..41ce7d4498 100644 --- a/sdk/logs/src/main/java/io/opentelemetry/sdk/logs/SdkLoggerProviderBuilder.java +++ b/sdk/logs/src/main/java/io/opentelemetry/sdk/logs/SdkLoggerProviderBuilder.java @@ -12,7 +12,6 @@ import io.opentelemetry.api.logs.Logger; import io.opentelemetry.context.Context; import io.opentelemetry.sdk.common.Clock; import io.opentelemetry.sdk.common.InstrumentationScopeInfo; -import io.opentelemetry.sdk.internal.DefaultExceptionAttributeResolver; import io.opentelemetry.sdk.internal.ExceptionAttributeResolver; import io.opentelemetry.sdk.internal.ScopeConfigurator; import io.opentelemetry.sdk.internal.ScopeConfiguratorBuilder; @@ -40,7 +39,7 @@ public final class SdkLoggerProviderBuilder { private ScopeConfiguratorBuilder loggerConfiguratorBuilder = LoggerConfig.configuratorBuilder(); private ExceptionAttributeResolver exceptionAttributeResolver = - DefaultExceptionAttributeResolver.getInstance(); + ExceptionAttributeResolver.getDefault(); SdkLoggerProviderBuilder() {} diff --git a/sdk/logs/src/test/java/io/opentelemetry/sdk/logs/LoggerSharedStateTest.java b/sdk/logs/src/test/java/io/opentelemetry/sdk/logs/LoggerSharedStateTest.java index 31b46c6520..7aa4f02aa0 100644 --- a/sdk/logs/src/test/java/io/opentelemetry/sdk/logs/LoggerSharedStateTest.java +++ b/sdk/logs/src/test/java/io/opentelemetry/sdk/logs/LoggerSharedStateTest.java @@ -12,7 +12,7 @@ import static org.mockito.Mockito.when; import io.opentelemetry.sdk.common.Clock; import io.opentelemetry.sdk.common.CompletableResultCode; -import io.opentelemetry.sdk.internal.DefaultExceptionAttributeResolver; +import io.opentelemetry.sdk.internal.ExceptionAttributeResolver; import io.opentelemetry.sdk.resources.Resource; import org.junit.jupiter.api.Test; @@ -29,7 +29,7 @@ class LoggerSharedStateTest { LogLimits::getDefault, logRecordProcessor, Clock.getDefault(), - DefaultExceptionAttributeResolver.getInstance()); + ExceptionAttributeResolver.getDefault()); state.shutdown(); state.shutdown(); verify(logRecordProcessor, times(1)).shutdown(); diff --git a/sdk/logs/src/test/java/io/opentelemetry/sdk/logs/SdkLoggerProviderTest.java b/sdk/logs/src/test/java/io/opentelemetry/sdk/logs/SdkLoggerProviderTest.java index cb0f08a86b..c6f0b2885e 100644 --- a/sdk/logs/src/test/java/io/opentelemetry/sdk/logs/SdkLoggerProviderTest.java +++ b/sdk/logs/src/test/java/io/opentelemetry/sdk/logs/SdkLoggerProviderTest.java @@ -361,20 +361,22 @@ class SdkLoggerProviderTest { @Test void propagatesEnablementToLoggerDirectly() { SdkLogger logger = (SdkLogger) sdkLoggerProvider.get("test"); - boolean isEnabled = logger.isEnabled(); + boolean isEnabled = logger.isEnabled(Severity.UNDEFINED_SEVERITY_NUMBER, Context.current()); sdkLoggerProvider.setLoggerConfigurator(flipConfigurator(isEnabled)); - assertThat(logger.isEnabled()).isEqualTo(!isEnabled); + assertThat(logger.isEnabled(Severity.UNDEFINED_SEVERITY_NUMBER, Context.current())) + .isEqualTo(!isEnabled); } @Test void propagatesEnablementToLoggerByUtil() { SdkLogger logger = (SdkLogger) sdkLoggerProvider.get("test"); - boolean isEnabled = logger.isEnabled(); + boolean isEnabled = logger.isEnabled(Severity.UNDEFINED_SEVERITY_NUMBER, Context.current()); SdkLoggerProviderUtil.setLoggerConfigurator(sdkLoggerProvider, flipConfigurator(isEnabled)); - assertThat(logger.isEnabled()).isEqualTo(!isEnabled); + assertThat(logger.isEnabled(Severity.UNDEFINED_SEVERITY_NUMBER, Context.current())) + .isEqualTo(!isEnabled); } } diff --git a/sdk/logs/src/test/java/io/opentelemetry/sdk/logs/SdkLoggerTest.java b/sdk/logs/src/test/java/io/opentelemetry/sdk/logs/SdkLoggerTest.java index 01aa76feb1..1f1415adf2 100644 --- a/sdk/logs/src/test/java/io/opentelemetry/sdk/logs/SdkLoggerTest.java +++ b/sdk/logs/src/test/java/io/opentelemetry/sdk/logs/SdkLoggerTest.java @@ -21,6 +21,8 @@ import io.opentelemetry.api.common.Attributes; import io.opentelemetry.api.common.AttributesBuilder; import io.opentelemetry.api.internal.StringUtils; import io.opentelemetry.api.logs.LogRecordBuilder; +import io.opentelemetry.api.logs.Severity; +import io.opentelemetry.context.Context; import io.opentelemetry.sdk.common.Clock; import io.opentelemetry.sdk.common.CompletableResultCode; import io.opentelemetry.sdk.common.InstrumentationScopeInfo; @@ -142,8 +144,8 @@ class SdkLoggerTest { SdkLogger logger = (SdkLogger) loggerProvider.get("test"); logger.updateLoggerConfig(LoggerConfig.disabled()); - assertThat(logger.isEnabled()).isFalse(); + assertThat(logger.isEnabled(Severity.UNDEFINED_SEVERITY_NUMBER, Context.current())).isFalse(); logger.updateLoggerConfig(LoggerConfig.enabled()); - assertThat(logger.isEnabled()).isTrue(); + assertThat(logger.isEnabled(Severity.UNDEFINED_SEVERITY_NUMBER, Context.current())).isTrue(); } } diff --git a/sdk/logs/src/testIncubating/java/io/opentelemetry/sdk/logs/LoggerConfigTest.java b/sdk/logs/src/testIncubating/java/io/opentelemetry/sdk/logs/LoggerConfigTest.java index e58f97c0a1..699f9be4c4 100644 --- a/sdk/logs/src/testIncubating/java/io/opentelemetry/sdk/logs/LoggerConfigTest.java +++ b/sdk/logs/src/testIncubating/java/io/opentelemetry/sdk/logs/LoggerConfigTest.java @@ -14,6 +14,8 @@ import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.asser import io.opentelemetry.api.incubator.logs.ExtendedLogger; import io.opentelemetry.api.logs.Logger; +import io.opentelemetry.api.logs.Severity; +import io.opentelemetry.context.Context; import io.opentelemetry.sdk.common.InstrumentationScopeInfo; import io.opentelemetry.sdk.internal.ScopeConfigurator; import io.opentelemetry.sdk.logs.data.LogRecordData; @@ -62,9 +64,9 @@ class LoggerConfigTest { assertThat(logsByScope.get(InstrumentationScopeInfo.create("loggerC"))).hasSize(1); }); // loggerA and loggerC are enabled, loggerB is disabled. - assertThat(((ExtendedLogger) loggerA).isEnabled()).isTrue(); - assertThat(((ExtendedLogger) loggerB).isEnabled()).isFalse(); - assertThat(((ExtendedLogger) loggerC).isEnabled()).isTrue(); + assertThat(((ExtendedLogger) loggerA).isEnabled(Severity.INFO)).isTrue(); + assertThat(((ExtendedLogger) loggerB).isEnabled(Severity.INFO)).isFalse(); + assertThat(((ExtendedLogger) loggerC).isEnabled(Severity.INFO)).isTrue(); } @ParameterizedTest @@ -144,9 +146,9 @@ class LoggerConfigTest { ExtendedSdkLogger loggerC = (ExtendedSdkLogger) loggerProvider.get("loggerC"); // verify isEnabled() - assertThat(loggerA.isEnabled()).isTrue(); - assertThat(loggerB.isEnabled()).isFalse(); - assertThat(loggerC.isEnabled()).isTrue(); + assertThat(loggerA.isEnabled(Severity.UNDEFINED_SEVERITY_NUMBER, Context.current())).isTrue(); + assertThat(loggerB.isEnabled(Severity.UNDEFINED_SEVERITY_NUMBER, Context.current())).isFalse(); + assertThat(loggerC.isEnabled(Severity.UNDEFINED_SEVERITY_NUMBER, Context.current())).isTrue(); // verify logs are emitted as expected loggerA.logRecordBuilder().setBody("logA").emit(); @@ -162,9 +164,9 @@ class LoggerConfigTest { ScopeConfigurator.builder().setDefault(disabled()).build()); // verify isEnabled() - assertThat(loggerA.isEnabled()).isFalse(); - assertThat(loggerB.isEnabled()).isFalse(); - assertThat(loggerC.isEnabled()).isFalse(); + assertThat(loggerA.isEnabled(Severity.UNDEFINED_SEVERITY_NUMBER, Context.current())).isFalse(); + assertThat(loggerB.isEnabled(Severity.UNDEFINED_SEVERITY_NUMBER, Context.current())).isFalse(); + assertThat(loggerC.isEnabled(Severity.UNDEFINED_SEVERITY_NUMBER, Context.current())).isFalse(); // verify logs are emitted as expected loggerA.logRecordBuilder().setBody("logA").emit(); @@ -179,9 +181,9 @@ class LoggerConfigTest { .build()); // verify isEnabled() - assertThat(loggerA.isEnabled()).isTrue(); - assertThat(loggerB.isEnabled()).isFalse(); - assertThat(loggerC.isEnabled()).isTrue(); + assertThat(loggerA.isEnabled(Severity.UNDEFINED_SEVERITY_NUMBER, Context.current())).isTrue(); + assertThat(loggerB.isEnabled(Severity.UNDEFINED_SEVERITY_NUMBER, Context.current())).isFalse(); + assertThat(loggerC.isEnabled(Severity.UNDEFINED_SEVERITY_NUMBER, Context.current())).isTrue(); // verify logs are emitted as expected loggerA.logRecordBuilder().setBody("logA").emit(); diff --git a/sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/ViewBuilder.java b/sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/ViewBuilder.java index 87ce139f81..5d51b6769e 100644 --- a/sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/ViewBuilder.java +++ b/sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/ViewBuilder.java @@ -5,9 +5,8 @@ package io.opentelemetry.sdk.metrics; -import static io.opentelemetry.sdk.metrics.internal.view.AttributesProcessor.setIncludes; - import io.opentelemetry.sdk.common.export.MemoryMode; +import io.opentelemetry.sdk.internal.IncludeExcludePredicate; import io.opentelemetry.sdk.metrics.internal.SdkMeterProviderUtil; import io.opentelemetry.sdk.metrics.internal.aggregator.AggregatorFactory; import io.opentelemetry.sdk.metrics.internal.state.MetricStorage; @@ -75,7 +74,7 @@ public final class ViewBuilder { */ public ViewBuilder setAttributeFilter(Set keysToRetain) { Objects.requireNonNull(keysToRetain, "keysToRetain"); - return setAttributeFilter(setIncludes(keysToRetain)); + return setAttributeFilter(IncludeExcludePredicate.createExactMatching(keysToRetain, null)); } /** diff --git a/sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/internal/view/AttributesProcessor.java b/sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/internal/view/AttributesProcessor.java index 14ddeef581..6ba0cb8a99 100644 --- a/sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/internal/view/AttributesProcessor.java +++ b/sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/internal/view/AttributesProcessor.java @@ -16,7 +16,6 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.List; -import java.util.Set; import java.util.function.Predicate; import javax.annotation.concurrent.Immutable; @@ -98,30 +97,6 @@ public abstract class AttributesProcessor { return new AppendingAttributesProcessor(attributes); } - /** Creates a {@link Predicate} which tests if the {@code set} includes the input. */ - public static Predicate setIncludes(Set set) { - return new SetIncludesPredicate(set); - } - - /** Predicate which tests if the {@code set} includes the input. */ - private static class SetIncludesPredicate implements Predicate { - private final Set set; - - private SetIncludesPredicate(Set set) { - this.set = set; - } - - @Override - public boolean test(String s) { - return set.contains(s); - } - - @Override - public String toString() { - return "SetIncludesPredicate{set=" + set + "}"; - } - } - /** * Processor which filters attributes according to a {@link AttributeKey#getKey()} {@link * Predicate}. diff --git a/sdk/metrics/src/test/java/io/opentelemetry/sdk/metrics/ViewTest.java b/sdk/metrics/src/test/java/io/opentelemetry/sdk/metrics/ViewTest.java index 137bc81d58..7f13c91f0d 100644 --- a/sdk/metrics/src/test/java/io/opentelemetry/sdk/metrics/ViewTest.java +++ b/sdk/metrics/src/test/java/io/opentelemetry/sdk/metrics/ViewTest.java @@ -36,7 +36,7 @@ class ViewTest { + "name=name, " + "description=description, " + "aggregation=SumAggregation, " - + "attributesProcessor=AttributeKeyFilteringProcessor{nameFilter=SetIncludesPredicate{set=[key1, key2]}}, " + + "attributesProcessor=AttributeKeyFilteringProcessor{nameFilter=IncludeExcludePredicate{globMatchingEnabled=false, included=[key1, key2]}}, " + "cardinalityLimit=10" + "}"); } diff --git a/sdk/metrics/src/test/java/io/opentelemetry/sdk/metrics/internal/view/AttributesProcessorTest.java b/sdk/metrics/src/test/java/io/opentelemetry/sdk/metrics/internal/view/AttributesProcessorTest.java index b05e555c43..bf5d65235a 100644 --- a/sdk/metrics/src/test/java/io/opentelemetry/sdk/metrics/internal/view/AttributesProcessorTest.java +++ b/sdk/metrics/src/test/java/io/opentelemetry/sdk/metrics/internal/view/AttributesProcessorTest.java @@ -6,13 +6,13 @@ package io.opentelemetry.sdk.metrics.internal.view; import static io.opentelemetry.sdk.metrics.internal.view.AttributesProcessor.append; -import static io.opentelemetry.sdk.metrics.internal.view.AttributesProcessor.setIncludes; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat; +import static java.util.Collections.singleton; import io.opentelemetry.api.baggage.Baggage; import io.opentelemetry.api.common.Attributes; import io.opentelemetry.context.Context; -import java.util.Collections; +import io.opentelemetry.sdk.internal.IncludeExcludePredicate; import org.junit.jupiter.api.Test; /** Tests for the {@link AttributesProcessor} DSL-ish library. */ @@ -29,26 +29,15 @@ class AttributesProcessorTest { .containsEntry("test", "keep"); } - @Test - void filterKeyName_SetIncludes() { - AttributesProcessor processor = - AttributesProcessor.filterByKeyName(setIncludes(Collections.singleton("test"))); - - assertThat( - processor.process( - Attributes.builder().put("remove", "me").put("test", "keep").build(), - Context.root())) - .hasSize(1) - .containsEntry("test", "keep"); - } - @Test void filterKeyName_toString() { AttributesProcessor processor = - AttributesProcessor.filterByKeyName(setIncludes(Collections.singleton("test"))); + AttributesProcessor.filterByKeyName( + IncludeExcludePredicate.createExactMatching(singleton("test"), null)); assertThat(processor.toString()) - .isEqualTo("AttributeKeyFilteringProcessor{nameFilter=SetIncludesPredicate{set=[test]}}"); + .isEqualTo( + "AttributeKeyFilteringProcessor{nameFilter=IncludeExcludePredicate{globMatchingEnabled=false, included=[test]}}"); } @Test @@ -117,12 +106,13 @@ class AttributesProcessorTest { @Test void appendBaggage_toString() { AttributesProcessor processor = - AttributesProcessor.appendBaggageByKeyName(setIncludes(Collections.singleton("keep"))); + AttributesProcessor.appendBaggageByKeyName( + IncludeExcludePredicate.createExactMatching(singleton("keep"), null)); assertThat(processor.toString()) .isEqualTo( "BaggageAppendingAttributesProcessor{" - + "nameFilter=SetIncludesPredicate{set=[keep]}" + + "nameFilter=IncludeExcludePredicate{globMatchingEnabled=false, included=[keep]}" + "}"); } @@ -143,13 +133,14 @@ class AttributesProcessorTest { @Test void joinedAttributes_toString() { AttributesProcessor processor = - AttributesProcessor.appendBaggageByKeyName(setIncludes(Collections.singleton("keep"))) + AttributesProcessor.appendBaggageByKeyName( + IncludeExcludePredicate.createExactMatching(singleton("keep"), null)) .then(append(Attributes.builder().put("key", "value").build())); assertThat(processor.toString()) .isEqualTo( "JoinedAttributesProcessor{processors=[" - + "BaggageAppendingAttributesProcessor{nameFilter=SetIncludesPredicate{set=[keep]}}, " + + "BaggageAppendingAttributesProcessor{nameFilter=IncludeExcludePredicate{globMatchingEnabled=false, included=[keep]}}, " + "AppendingAttributesProcessor{additionalAttributes={key=\"value\"}}" + "]}"); } diff --git a/sdk/trace/src/main/java/io/opentelemetry/sdk/trace/SdkTracerProviderBuilder.java b/sdk/trace/src/main/java/io/opentelemetry/sdk/trace/SdkTracerProviderBuilder.java index 194aa67bc1..71f589d15b 100644 --- a/sdk/trace/src/main/java/io/opentelemetry/sdk/trace/SdkTracerProviderBuilder.java +++ b/sdk/trace/src/main/java/io/opentelemetry/sdk/trace/SdkTracerProviderBuilder.java @@ -10,7 +10,6 @@ import static java.util.Objects.requireNonNull; import io.opentelemetry.api.trace.Span; import io.opentelemetry.sdk.common.Clock; import io.opentelemetry.sdk.common.InstrumentationScopeInfo; -import io.opentelemetry.sdk.internal.DefaultExceptionAttributeResolver; import io.opentelemetry.sdk.internal.ExceptionAttributeResolver; import io.opentelemetry.sdk.internal.ScopeConfigurator; import io.opentelemetry.sdk.internal.ScopeConfiguratorBuilder; @@ -38,7 +37,7 @@ public final class SdkTracerProviderBuilder { private ScopeConfiguratorBuilder tracerConfiguratorBuilder = TracerConfig.configuratorBuilder(); private ExceptionAttributeResolver exceptionAttributeResolver = - DefaultExceptionAttributeResolver.getInstance(); + ExceptionAttributeResolver.getDefault(); /** * Assign a {@link Clock}. {@link Clock} will be used each time a {@link Span} is started, ended diff --git a/sdk/trace/src/test/java/io/opentelemetry/sdk/trace/SdkSpanTest.java b/sdk/trace/src/test/java/io/opentelemetry/sdk/trace/SdkSpanTest.java index fcfec65d47..d5c40fcccb 100644 --- a/sdk/trace/src/test/java/io/opentelemetry/sdk/trace/SdkSpanTest.java +++ b/sdk/trace/src/test/java/io/opentelemetry/sdk/trace/SdkSpanTest.java @@ -38,7 +38,6 @@ import io.opentelemetry.api.trace.TraceState; import io.opentelemetry.context.Context; import io.opentelemetry.sdk.common.InstrumentationScopeInfo; import io.opentelemetry.sdk.internal.AttributesMap; -import io.opentelemetry.sdk.internal.DefaultExceptionAttributeResolver; import io.opentelemetry.sdk.internal.ExceptionAttributeResolver; import io.opentelemetry.sdk.internal.InstrumentationScopeUtil; import io.opentelemetry.sdk.resources.Resource; @@ -946,7 +945,7 @@ class SdkSpanTest { parentSpanId, null, null, - DefaultExceptionAttributeResolver.getInstance()); + ExceptionAttributeResolver.getDefault()); try { Span span1 = createTestSpan(SpanKind.INTERNAL); Span span2 = createTestSpan(SpanKind.INTERNAL); @@ -1036,7 +1035,7 @@ class SdkSpanTest { Context.root(), SpanLimits.getDefault(), spanProcessor, - DefaultExceptionAttributeResolver.getInstance(), + ExceptionAttributeResolver.getDefault(), testClock, resource, null, @@ -1381,7 +1380,7 @@ class SdkSpanTest { Context.root(), spanLimits, spanProcessor, - DefaultExceptionAttributeResolver.getInstance(), + ExceptionAttributeResolver.getDefault(), testClock, resource, AttributesMap.create( @@ -1464,7 +1463,7 @@ class SdkSpanTest { null, attributesMap, Collections.singletonList(link), - DefaultExceptionAttributeResolver.getInstance()); + ExceptionAttributeResolver.getDefault()); } private SdkSpan createTestRootSpan() { @@ -1474,7 +1473,7 @@ class SdkSpanTest { SpanId.getInvalid(), null, Collections.singletonList(link), - DefaultExceptionAttributeResolver.getInstance()); + ExceptionAttributeResolver.getDefault()); } private SdkSpan createTestSpan(SpanKind kind) { @@ -1484,7 +1483,7 @@ class SdkSpanTest { parentSpanId, null, Collections.singletonList(link), - DefaultExceptionAttributeResolver.getInstance()); + ExceptionAttributeResolver.getDefault()); } private SdkSpan createTestSpan(SpanLimits config) { @@ -1494,7 +1493,7 @@ class SdkSpanTest { parentSpanId, null, Collections.singletonList(link), - DefaultExceptionAttributeResolver.getInstance()); + ExceptionAttributeResolver.getDefault()); } private SdkSpan createTestSpan( @@ -1608,7 +1607,7 @@ class SdkSpanTest { Context.root(), spanLimits, spanProcessor, - DefaultExceptionAttributeResolver.getInstance(), + ExceptionAttributeResolver.getDefault(), clock, resource, attributesWithCapacity, diff --git a/sdk/trace/src/test/java/io/opentelemetry/sdk/trace/SdkTracerProviderTest.java b/sdk/trace/src/test/java/io/opentelemetry/sdk/trace/SdkTracerProviderTest.java index 345f97d52c..9fb7724524 100644 --- a/sdk/trace/src/test/java/io/opentelemetry/sdk/trace/SdkTracerProviderTest.java +++ b/sdk/trace/src/test/java/io/opentelemetry/sdk/trace/SdkTracerProviderTest.java @@ -22,7 +22,6 @@ import io.opentelemetry.internal.testing.slf4j.SuppressLogger; import io.opentelemetry.sdk.common.Clock; import io.opentelemetry.sdk.common.CompletableResultCode; import io.opentelemetry.sdk.common.InstrumentationScopeInfo; -import io.opentelemetry.sdk.internal.DefaultExceptionAttributeResolver; import io.opentelemetry.sdk.internal.ExceptionAttributeResolver; import io.opentelemetry.sdk.internal.ScopeConfigurator; import io.opentelemetry.sdk.resources.Resource; @@ -298,7 +297,7 @@ class SdkTracerProviderTest { .setSpanLimits( SpanLimits.builder().setMaxAttributeValueLength(maxAttributeLength).build()); ExceptionAttributeResolver exceptionAttributeResolver = - spy(DefaultExceptionAttributeResolver.getInstance()); + spy(ExceptionAttributeResolver.getDefault()); SdkTracerProviderUtil.setExceptionAttributeResolver(builder, exceptionAttributeResolver); Exception exception = new Exception("error"); diff --git a/settings.gradle.kts b/settings.gradle.kts index 58659284e9..eb92b193f2 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -1,6 +1,6 @@ pluginManagement { plugins { - id("com.gradleup.shadow") version "8.3.7" + id("com.gradleup.shadow") version "8.3.8" id("com.gradle.develocity") version "4.0.2" id("de.undercouch.download") version "5.6.0" id("org.jsonschema2pojo") version "1.2.2" @@ -28,6 +28,7 @@ include(":api:incubator") include(":api:testing-internal") include(":bom") include(":bom-alpha") +include(":common") include(":context") include(":custom-checks") include(":dependencyManagement") diff --git a/version.gradle.kts b/version.gradle.kts index 543abf0333..f08f504448 100644 --- a/version.gradle.kts +++ b/version.gradle.kts @@ -1,7 +1,7 @@ val snapshot = true allprojects { - var ver = "1.52.0" + var ver = "1.53.0" val release = findProperty("otel.release") if (release != null) { ver += "-" + release