From acc64ee012cca3ea74877048265f0b5feddf0fe7 Mon Sep 17 00:00:00 2001 From: "otelbot[bot]" <197425009+otelbot[bot]@users.noreply.github.com> Date: Fri, 9 May 2025 17:58:04 -0500 Subject: [PATCH 01/42] Update version to 1.51.0 (#7338) Co-authored-by: otelbot <197425009+otelbot@users.noreply.github.com> Co-authored-by: Trask Stalnaker Co-authored-by: Jack Berg --- CHANGELOG.md | 2 ++ docs/apidiffs/current_vs_latest/opentelemetry-api.txt | 2 +- docs/apidiffs/current_vs_latest/opentelemetry-context.txt | 2 +- .../current_vs_latest/opentelemetry-exporter-common.txt | 2 +- .../current_vs_latest/opentelemetry-exporter-logging-otlp.txt | 2 +- .../current_vs_latest/opentelemetry-exporter-logging.txt | 2 +- .../current_vs_latest/opentelemetry-exporter-otlp-common.txt | 2 +- docs/apidiffs/current_vs_latest/opentelemetry-exporter-otlp.txt | 2 +- .../opentelemetry-exporter-sender-grpc-managed-channel.txt | 2 +- .../current_vs_latest/opentelemetry-exporter-sender-jdk.txt | 2 +- .../current_vs_latest/opentelemetry-exporter-sender-okhttp.txt | 2 +- .../current_vs_latest/opentelemetry-exporter-zipkin.txt | 2 +- .../current_vs_latest/opentelemetry-extension-kotlin.txt | 2 +- .../opentelemetry-extension-trace-propagators.txt | 2 +- .../current_vs_latest/opentelemetry-opentracing-shim.txt | 2 +- docs/apidiffs/current_vs_latest/opentelemetry-sdk-common.txt | 2 +- .../opentelemetry-sdk-extension-autoconfigure-spi.txt | 2 +- .../opentelemetry-sdk-extension-autoconfigure.txt | 2 +- .../opentelemetry-sdk-extension-jaeger-remote-sampler.txt | 2 +- docs/apidiffs/current_vs_latest/opentelemetry-sdk-logs.txt | 2 +- docs/apidiffs/current_vs_latest/opentelemetry-sdk-metrics.txt | 2 +- docs/apidiffs/current_vs_latest/opentelemetry-sdk-testing.txt | 2 +- docs/apidiffs/current_vs_latest/opentelemetry-sdk-trace.txt | 2 +- docs/apidiffs/current_vs_latest/opentelemetry-sdk.txt | 2 +- version.gradle.kts | 2 +- 25 files changed, 26 insertions(+), 24 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6a8e3e04b63..c912fd9da9b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## Version 1.50.0 (2025-05-09) + ### API * Clarify that AttributesBuilder.put allows nulls diff --git a/docs/apidiffs/current_vs_latest/opentelemetry-api.txt b/docs/apidiffs/current_vs_latest/opentelemetry-api.txt index d095106aa51..251bac6a43b 100644 --- a/docs/apidiffs/current_vs_latest/opentelemetry-api.txt +++ b/docs/apidiffs/current_vs_latest/opentelemetry-api.txt @@ -1,4 +1,4 @@ -Comparing source compatibility of opentelemetry-api-1.50.0-SNAPSHOT.jar against opentelemetry-api-1.49.0.jar +Comparing source compatibility of opentelemetry-api-1.51.0-SNAPSHOT.jar against opentelemetry-api-1.49.0.jar *** MODIFIED INTERFACE: PUBLIC ABSTRACT io.opentelemetry.api.logs.LogRecordBuilder (not serializable) === CLASS FILE FORMAT VERSION: 52.0 <- 52.0 +++ NEW METHOD: PUBLIC(+) io.opentelemetry.api.logs.LogRecordBuilder setEventName(java.lang.String) diff --git a/docs/apidiffs/current_vs_latest/opentelemetry-context.txt b/docs/apidiffs/current_vs_latest/opentelemetry-context.txt index 8546589e179..9efdc0f2021 100644 --- a/docs/apidiffs/current_vs_latest/opentelemetry-context.txt +++ b/docs/apidiffs/current_vs_latest/opentelemetry-context.txt @@ -1,4 +1,4 @@ -Comparing source compatibility of opentelemetry-context-1.50.0-SNAPSHOT.jar against opentelemetry-context-1.49.0.jar +Comparing source compatibility of opentelemetry-context-1.51.0-SNAPSHOT.jar against opentelemetry-context-1.49.0.jar *** MODIFIED INTERFACE: PUBLIC ABSTRACT io.opentelemetry.context.propagation.TextMapGetter (not serializable) === CLASS FILE FORMAT VERSION: 52.0 <- 52.0 GENERIC TEMPLATES: === C:java.lang.Object diff --git a/docs/apidiffs/current_vs_latest/opentelemetry-exporter-common.txt b/docs/apidiffs/current_vs_latest/opentelemetry-exporter-common.txt index 869d4d30904..d606d25e1e2 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.50.0-SNAPSHOT.jar against opentelemetry-exporter-common-1.49.0.jar +Comparing source compatibility of opentelemetry-exporter-common-1.51.0-SNAPSHOT.jar against opentelemetry-exporter-common-1.49.0.jar No changes. \ No newline at end of file diff --git a/docs/apidiffs/current_vs_latest/opentelemetry-exporter-logging-otlp.txt b/docs/apidiffs/current_vs_latest/opentelemetry-exporter-logging-otlp.txt index ace6c206aae..3213407d108 100644 --- a/docs/apidiffs/current_vs_latest/opentelemetry-exporter-logging-otlp.txt +++ b/docs/apidiffs/current_vs_latest/opentelemetry-exporter-logging-otlp.txt @@ -1,2 +1,2 @@ -Comparing source compatibility of opentelemetry-exporter-logging-otlp-1.50.0-SNAPSHOT.jar against opentelemetry-exporter-logging-otlp-1.49.0.jar +Comparing source compatibility of opentelemetry-exporter-logging-otlp-1.51.0-SNAPSHOT.jar against opentelemetry-exporter-logging-otlp-1.49.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 e59dd5cbe3f..66765bbd2d9 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.50.0-SNAPSHOT.jar against opentelemetry-exporter-logging-1.49.0.jar +Comparing source compatibility of opentelemetry-exporter-logging-1.51.0-SNAPSHOT.jar against opentelemetry-exporter-logging-1.49.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 b1878dec138..dccb68d091c 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.50.0-SNAPSHOT.jar against opentelemetry-exporter-otlp-common-1.49.0.jar +Comparing source compatibility of opentelemetry-exporter-otlp-common-1.51.0-SNAPSHOT.jar against opentelemetry-exporter-otlp-common-1.49.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 f42f38022a6..5b94efb9749 100644 --- a/docs/apidiffs/current_vs_latest/opentelemetry-exporter-otlp.txt +++ b/docs/apidiffs/current_vs_latest/opentelemetry-exporter-otlp.txt @@ -1,4 +1,4 @@ -Comparing source compatibility of opentelemetry-exporter-otlp-1.50.0-SNAPSHOT.jar against opentelemetry-exporter-otlp-1.49.0.jar +Comparing source compatibility of opentelemetry-exporter-otlp-1.51.0-SNAPSHOT.jar against opentelemetry-exporter-otlp-1.49.0.jar *** 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 setMeterProvider(io.opentelemetry.api.metrics.MeterProvider) 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 0ad6714e02e..f08abffd96d 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.50.0-SNAPSHOT.jar against opentelemetry-exporter-sender-grpc-managed-channel-1.49.0.jar +Comparing source compatibility of opentelemetry-exporter-sender-grpc-managed-channel-1.51.0-SNAPSHOT.jar against opentelemetry-exporter-sender-grpc-managed-channel-1.49.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 cf4339321be..0e46993c8de 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.50.0-SNAPSHOT.jar against opentelemetry-exporter-sender-jdk-1.49.0.jar +Comparing source compatibility of opentelemetry-exporter-sender-jdk-1.51.0-SNAPSHOT.jar against opentelemetry-exporter-sender-jdk-1.49.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 8cdc76c7541..a5d29fdf226 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.50.0-SNAPSHOT.jar against opentelemetry-exporter-sender-okhttp-1.49.0.jar +Comparing source compatibility of opentelemetry-exporter-sender-okhttp-1.51.0-SNAPSHOT.jar against opentelemetry-exporter-sender-okhttp-1.49.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 a34d027380c..d67132cc524 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.50.0-SNAPSHOT.jar against opentelemetry-exporter-zipkin-1.49.0.jar +Comparing source compatibility of opentelemetry-exporter-zipkin-1.51.0-SNAPSHOT.jar against opentelemetry-exporter-zipkin-1.49.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 d5033b059ec..6c00ffe689d 100644 --- a/docs/apidiffs/current_vs_latest/opentelemetry-extension-kotlin.txt +++ b/docs/apidiffs/current_vs_latest/opentelemetry-extension-kotlin.txt @@ -1,4 +1,4 @@ -Comparing source compatibility of opentelemetry-extension-kotlin-1.50.0-SNAPSHOT.jar against opentelemetry-extension-kotlin-1.49.0.jar +Comparing source compatibility of opentelemetry-extension-kotlin-1.51.0-SNAPSHOT.jar against opentelemetry-extension-kotlin-1.49.0.jar === UNCHANGED CLASS: PUBLIC FINAL io.opentelemetry.extension.kotlin.ContextExtensionsKt (not serializable) === CLASS FILE FORMAT VERSION: 52.0 <- 52.0 *** MODIFIED ANNOTATION: kotlin.Metadata 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 32775622b3e..3c10ead2330 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.50.0-SNAPSHOT.jar against opentelemetry-extension-trace-propagators-1.49.0.jar +Comparing source compatibility of opentelemetry-extension-trace-propagators-1.51.0-SNAPSHOT.jar against opentelemetry-extension-trace-propagators-1.49.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 e25b3d4a242..60af82fb8b1 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.50.0-SNAPSHOT.jar against opentelemetry-opentracing-shim-1.49.0.jar +Comparing source compatibility of opentelemetry-opentracing-shim-1.51.0-SNAPSHOT.jar against opentelemetry-opentracing-shim-1.49.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 89bd2da2c3f..7e8288c8dcb 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.50.0-SNAPSHOT.jar against opentelemetry-sdk-common-1.49.0.jar +Comparing source compatibility of opentelemetry-sdk-common-1.51.0-SNAPSHOT.jar against opentelemetry-sdk-common-1.49.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 2f0667e466a..1bf987b6fdc 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.50.0-SNAPSHOT.jar against opentelemetry-sdk-extension-autoconfigure-spi-1.49.0.jar +Comparing source compatibility of opentelemetry-sdk-extension-autoconfigure-spi-1.51.0-SNAPSHOT.jar against opentelemetry-sdk-extension-autoconfigure-spi-1.49.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 026cc3efc5b..fbea2fa8018 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.50.0-SNAPSHOT.jar against opentelemetry-sdk-extension-autoconfigure-1.49.0.jar +Comparing source compatibility of opentelemetry-sdk-extension-autoconfigure-1.51.0-SNAPSHOT.jar against opentelemetry-sdk-extension-autoconfigure-1.49.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 7f0c9afb95f..bfea6a73db4 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.50.0-SNAPSHOT.jar against opentelemetry-sdk-extension-jaeger-remote-sampler-1.49.0.jar +Comparing source compatibility of opentelemetry-sdk-extension-jaeger-remote-sampler-1.51.0-SNAPSHOT.jar against opentelemetry-sdk-extension-jaeger-remote-sampler-1.49.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 77f779d1926..aeb9aaf747d 100644 --- a/docs/apidiffs/current_vs_latest/opentelemetry-sdk-logs.txt +++ b/docs/apidiffs/current_vs_latest/opentelemetry-sdk-logs.txt @@ -1,4 +1,4 @@ -Comparing source compatibility of opentelemetry-sdk-logs-1.50.0-SNAPSHOT.jar against opentelemetry-sdk-logs-1.49.0.jar +Comparing source compatibility of opentelemetry-sdk-logs-1.51.0-SNAPSHOT.jar against opentelemetry-sdk-logs-1.49.0.jar *** MODIFIED INTERFACE: PUBLIC ABSTRACT io.opentelemetry.sdk.logs.data.LogRecordData (not serializable) === CLASS FILE FORMAT VERSION: 52.0 <- 52.0 +++ NEW METHOD: PUBLIC(+) java.lang.String getEventName() diff --git a/docs/apidiffs/current_vs_latest/opentelemetry-sdk-metrics.txt b/docs/apidiffs/current_vs_latest/opentelemetry-sdk-metrics.txt index 72a131718a9..3dc5b03f853 100644 --- a/docs/apidiffs/current_vs_latest/opentelemetry-sdk-metrics.txt +++ b/docs/apidiffs/current_vs_latest/opentelemetry-sdk-metrics.txt @@ -1,4 +1,4 @@ -Comparing source compatibility of opentelemetry-sdk-metrics-1.50.0-SNAPSHOT.jar against opentelemetry-sdk-metrics-1.49.0.jar +Comparing source compatibility of opentelemetry-sdk-metrics-1.51.0-SNAPSHOT.jar against opentelemetry-sdk-metrics-1.49.0.jar +++ NEW CLASS: PUBLIC(+) ABSTRACT(+) io.opentelemetry.sdk.metrics.data.DelegatingMetricData (not serializable) +++ CLASS FILE FORMAT VERSION: 52.0 <- n.a. +++ NEW INTERFACE: io.opentelemetry.sdk.metrics.data.MetricData diff --git a/docs/apidiffs/current_vs_latest/opentelemetry-sdk-testing.txt b/docs/apidiffs/current_vs_latest/opentelemetry-sdk-testing.txt index b7af8bfe5a6..b5f817ef929 100644 --- a/docs/apidiffs/current_vs_latest/opentelemetry-sdk-testing.txt +++ b/docs/apidiffs/current_vs_latest/opentelemetry-sdk-testing.txt @@ -1,4 +1,4 @@ -Comparing source compatibility of opentelemetry-sdk-testing-1.50.0-SNAPSHOT.jar against opentelemetry-sdk-testing-1.49.0.jar +Comparing source compatibility of opentelemetry-sdk-testing-1.51.0-SNAPSHOT.jar against opentelemetry-sdk-testing-1.49.0.jar *** MODIFIED CLASS: PUBLIC FINAL io.opentelemetry.sdk.testing.assertj.LogRecordDataAssert (not serializable) === CLASS FILE FORMAT VERSION: 52.0 <- 52.0 +++ NEW METHOD: PUBLIC(+) io.opentelemetry.sdk.testing.assertj.LogRecordDataAssert hasEventName(java.lang.String) diff --git a/docs/apidiffs/current_vs_latest/opentelemetry-sdk-trace.txt b/docs/apidiffs/current_vs_latest/opentelemetry-sdk-trace.txt index 9a3b5305423..8a439fc3dd5 100644 --- a/docs/apidiffs/current_vs_latest/opentelemetry-sdk-trace.txt +++ b/docs/apidiffs/current_vs_latest/opentelemetry-sdk-trace.txt @@ -1,4 +1,4 @@ -Comparing source compatibility of opentelemetry-sdk-trace-1.50.0-SNAPSHOT.jar against opentelemetry-sdk-trace-1.49.0.jar +Comparing source compatibility of opentelemetry-sdk-trace-1.51.0-SNAPSHOT.jar against opentelemetry-sdk-trace-1.49.0.jar *** MODIFIED CLASS: PUBLIC FINAL io.opentelemetry.sdk.trace.SdkTracerProviderBuilder (not serializable) === CLASS FILE FORMAT VERSION: 52.0 <- 52.0 +++ NEW METHOD: PUBLIC(+) io.opentelemetry.sdk.trace.SdkTracerProviderBuilder addSpanProcessorFirst(io.opentelemetry.sdk.trace.SpanProcessor) diff --git a/docs/apidiffs/current_vs_latest/opentelemetry-sdk.txt b/docs/apidiffs/current_vs_latest/opentelemetry-sdk.txt index 68adad94ecf..4c89342122a 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.50.0-SNAPSHOT.jar against opentelemetry-sdk-1.49.0.jar +Comparing source compatibility of opentelemetry-sdk-1.51.0-SNAPSHOT.jar against opentelemetry-sdk-1.49.0.jar No changes. \ No newline at end of file diff --git a/version.gradle.kts b/version.gradle.kts index c7f534fe0ea..74272dc6b59 100644 --- a/version.gradle.kts +++ b/version.gradle.kts @@ -1,7 +1,7 @@ val snapshot = true allprojects { - var ver = "1.50.0" + var ver = "1.51.0" val release = findProperty("otel.release") if (release != null) { ver += "-" + release From e419b5d588de2b9e6c0a4bfe0f2fb9917e85108c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Laurent=20Tr=C3=A9guier?= Date: Sun, 11 May 2025 18:55:11 +0200 Subject: [PATCH 02/42] Fix context storage provider property name (#7318) (#7342) --- .../src/main/java/io/opentelemetry/context/LazyStorage.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/context/src/main/java/io/opentelemetry/context/LazyStorage.java b/context/src/main/java/io/opentelemetry/context/LazyStorage.java index 12ec85043df..1727ee06fed 100644 --- a/context/src/main/java/io/opentelemetry/context/LazyStorage.java +++ b/context/src/main/java/io/opentelemetry/context/LazyStorage.java @@ -127,7 +127,8 @@ static ContextStorage createStorage(AtomicReference deferredStorageFa deferredStorageFailure.set( new IllegalStateException( "Found multiple ContextStorageProvider. Set the " - + "io.opentelemetry.context.ContextStorageProvider property to the fully " + + CONTEXT_STORAGE_PROVIDER_PROPERTY + + " property to the fully " + "qualified class name of the provider to use. Falling back to default " + "ContextStorage. Found providers: " + providers)); @@ -142,7 +143,8 @@ static ContextStorage createStorage(AtomicReference deferredStorageFa deferredStorageFailure.set( new IllegalStateException( - "io.opentelemetry.context.ContextStorageProvider property set but no matching class " + CONTEXT_STORAGE_PROVIDER_PROPERTY + + " property set but no matching class " + "could be found, requested: " + providerClassName + " but found providers: " From 394cd354a36a6159aa2533e31a64dea0247e0f27 Mon Sep 17 00:00:00 2001 From: jack-berg <34418638+jack-berg@users.noreply.github.com> Date: Tue, 13 May 2025 10:22:30 -0500 Subject: [PATCH 03/42] Post release 1.50.0 (#7344) --- .github/workflows/release.yml | 2 +- .../current_vs_latest/opentelemetry-api.txt | 6 +- .../opentelemetry-context.txt | 7 +-- .../opentelemetry-exporter-common.txt | 2 +- .../opentelemetry-exporter-logging-otlp.txt | 2 +- .../opentelemetry-exporter-logging.txt | 2 +- .../opentelemetry-exporter-otlp-common.txt | 2 +- .../opentelemetry-exporter-otlp.txt | 11 +--- ...y-exporter-sender-grpc-managed-channel.txt | 2 +- .../opentelemetry-exporter-sender-jdk.txt | 2 +- .../opentelemetry-exporter-sender-okhttp.txt | 2 +- .../opentelemetry-exporter-zipkin.txt | 2 +- .../opentelemetry-extension-kotlin.txt | 11 +--- ...ntelemetry-extension-trace-propagators.txt | 2 +- .../opentelemetry-opentracing-shim.txt | 2 +- .../opentelemetry-sdk-common.txt | 2 +- ...emetry-sdk-extension-autoconfigure-spi.txt | 2 +- ...ntelemetry-sdk-extension-autoconfigure.txt | 2 +- ...ry-sdk-extension-jaeger-remote-sampler.txt | 2 +- .../opentelemetry-sdk-logs.txt | 14 +---- .../opentelemetry-sdk-metrics.txt | 63 +------------------ .../opentelemetry-sdk-testing.txt | 35 +---------- .../opentelemetry-sdk-trace.txt | 6 +- .../current_vs_latest/opentelemetry-sdk.txt | 2 +- 24 files changed, 32 insertions(+), 153 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index c18f01299ed..8a100761237 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -188,7 +188,7 @@ jobs: git add docs/apidiffs - name: Use CLA approved bot - run: .github/scripts/use-cla-approved-bot.sh + run: .github/scripts/use-cla-approved-github-bot.sh - uses: actions/create-github-app-token@3ff1caaa28b64c9cc276ce0a02e2ff584f3900c5 # v2.0.2 id: otelbot-token diff --git a/docs/apidiffs/current_vs_latest/opentelemetry-api.txt b/docs/apidiffs/current_vs_latest/opentelemetry-api.txt index 251bac6a43b..fa356d6958e 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.51.0-SNAPSHOT.jar against opentelemetry-api-1.49.0.jar -*** MODIFIED INTERFACE: PUBLIC ABSTRACT io.opentelemetry.api.logs.LogRecordBuilder (not serializable) - === CLASS FILE FORMAT VERSION: 52.0 <- 52.0 - +++ NEW METHOD: PUBLIC(+) io.opentelemetry.api.logs.LogRecordBuilder setEventName(java.lang.String) +Comparing source compatibility of opentelemetry-api-1.51.0-SNAPSHOT.jar against opentelemetry-api-1.50.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 9efdc0f2021..6d7d86033e4 100644 --- a/docs/apidiffs/current_vs_latest/opentelemetry-context.txt +++ b/docs/apidiffs/current_vs_latest/opentelemetry-context.txt @@ -1,5 +1,2 @@ -Comparing source compatibility of opentelemetry-context-1.51.0-SNAPSHOT.jar against opentelemetry-context-1.49.0.jar -*** MODIFIED INTERFACE: PUBLIC ABSTRACT io.opentelemetry.context.propagation.TextMapGetter (not serializable) - === CLASS FILE FORMAT VERSION: 52.0 <- 52.0 - GENERIC TEMPLATES: === C:java.lang.Object - +++ NEW METHOD: PUBLIC(+) java.util.Iterator getAll(java.lang.Object, java.lang.String) +Comparing source compatibility of opentelemetry-context-1.51.0-SNAPSHOT.jar against opentelemetry-context-1.50.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 d606d25e1e2..def48216196 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.51.0-SNAPSHOT.jar against opentelemetry-exporter-common-1.49.0.jar +Comparing source compatibility of opentelemetry-exporter-common-1.51.0-SNAPSHOT.jar against opentelemetry-exporter-common-1.50.0.jar No changes. \ No newline at end of file diff --git a/docs/apidiffs/current_vs_latest/opentelemetry-exporter-logging-otlp.txt b/docs/apidiffs/current_vs_latest/opentelemetry-exporter-logging-otlp.txt index 3213407d108..1a6cc86e370 100644 --- a/docs/apidiffs/current_vs_latest/opentelemetry-exporter-logging-otlp.txt +++ b/docs/apidiffs/current_vs_latest/opentelemetry-exporter-logging-otlp.txt @@ -1,2 +1,2 @@ -Comparing source compatibility of opentelemetry-exporter-logging-otlp-1.51.0-SNAPSHOT.jar against opentelemetry-exporter-logging-otlp-1.49.0.jar +Comparing source compatibility of opentelemetry-exporter-logging-otlp-1.51.0-SNAPSHOT.jar against opentelemetry-exporter-logging-otlp-1.50.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 66765bbd2d9..a36a52dd829 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.51.0-SNAPSHOT.jar against opentelemetry-exporter-logging-1.49.0.jar +Comparing source compatibility of opentelemetry-exporter-logging-1.51.0-SNAPSHOT.jar against opentelemetry-exporter-logging-1.50.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 dccb68d091c..000f805f02c 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.51.0-SNAPSHOT.jar against opentelemetry-exporter-otlp-common-1.49.0.jar +Comparing source compatibility of opentelemetry-exporter-otlp-common-1.51.0-SNAPSHOT.jar against opentelemetry-exporter-otlp-common-1.50.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 5b94efb9749..1f5072e4cf3 100644 --- a/docs/apidiffs/current_vs_latest/opentelemetry-exporter-otlp.txt +++ b/docs/apidiffs/current_vs_latest/opentelemetry-exporter-otlp.txt @@ -1,9 +1,2 @@ -Comparing source compatibility of opentelemetry-exporter-otlp-1.51.0-SNAPSHOT.jar against opentelemetry-exporter-otlp-1.49.0.jar -*** 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 setMeterProvider(io.opentelemetry.api.metrics.MeterProvider) - +++ NEW METHOD: PUBLIC(+) io.opentelemetry.exporter.otlp.http.metrics.OtlpHttpMetricExporterBuilder setMeterProvider(java.util.function.Supplier) -*** 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 setMeterProvider(io.opentelemetry.api.metrics.MeterProvider) - +++ NEW METHOD: PUBLIC(+) io.opentelemetry.exporter.otlp.metrics.OtlpGrpcMetricExporterBuilder setMeterProvider(java.util.function.Supplier) +Comparing source compatibility of opentelemetry-exporter-otlp-1.51.0-SNAPSHOT.jar against opentelemetry-exporter-otlp-1.50.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 f08abffd96d..15137dbf8ae 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.51.0-SNAPSHOT.jar against opentelemetry-exporter-sender-grpc-managed-channel-1.49.0.jar +Comparing source compatibility of opentelemetry-exporter-sender-grpc-managed-channel-1.51.0-SNAPSHOT.jar against opentelemetry-exporter-sender-grpc-managed-channel-1.50.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 0e46993c8de..1dba6b4bca3 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.51.0-SNAPSHOT.jar against opentelemetry-exporter-sender-jdk-1.49.0.jar +Comparing source compatibility of opentelemetry-exporter-sender-jdk-1.51.0-SNAPSHOT.jar against opentelemetry-exporter-sender-jdk-1.50.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 a5d29fdf226..73fbc006078 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.51.0-SNAPSHOT.jar against opentelemetry-exporter-sender-okhttp-1.49.0.jar +Comparing source compatibility of opentelemetry-exporter-sender-okhttp-1.51.0-SNAPSHOT.jar against opentelemetry-exporter-sender-okhttp-1.50.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 d67132cc524..37fbce11347 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.51.0-SNAPSHOT.jar against opentelemetry-exporter-zipkin-1.49.0.jar +Comparing source compatibility of opentelemetry-exporter-zipkin-1.51.0-SNAPSHOT.jar against opentelemetry-exporter-zipkin-1.50.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 6c00ffe689d..37502198fa5 100644 --- a/docs/apidiffs/current_vs_latest/opentelemetry-extension-kotlin.txt +++ b/docs/apidiffs/current_vs_latest/opentelemetry-extension-kotlin.txt @@ -1,9 +1,2 @@ -Comparing source compatibility of opentelemetry-extension-kotlin-1.51.0-SNAPSHOT.jar against opentelemetry-extension-kotlin-1.49.0.jar -=== UNCHANGED CLASS: PUBLIC FINAL io.opentelemetry.extension.kotlin.ContextExtensionsKt (not serializable) - === CLASS FILE FORMAT VERSION: 52.0 <- 52.0 - *** MODIFIED ANNOTATION: kotlin.Metadata - === UNCHANGED ELEMENT: xi=48 - *** MODIFIED ELEMENT: mv=2,1,0 (<- 1,6,0) - === UNCHANGED ELEMENT: k=2 - === UNCHANGED ELEMENT: d1=�� � ��� ��� ��� ���� ����0�*�0�� ����0�*�0�� ����0�*�0�¨�� - === UNCHANGED ELEMENT: d2=asContextElement,Lkotlin/coroutines/CoroutineContext;,Lio/opentelemetry/context/Context;,Lio/opentelemetry/context/ImplicitContextKeyed;,getOpenTelemetryContext,opentelemetry-extension-kotlin +Comparing source compatibility of opentelemetry-extension-kotlin-1.51.0-SNAPSHOT.jar against opentelemetry-extension-kotlin-1.50.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 3c10ead2330..17e443a6e55 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.51.0-SNAPSHOT.jar against opentelemetry-extension-trace-propagators-1.49.0.jar +Comparing source compatibility of opentelemetry-extension-trace-propagators-1.51.0-SNAPSHOT.jar against opentelemetry-extension-trace-propagators-1.50.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 60af82fb8b1..cb43c782822 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.51.0-SNAPSHOT.jar against opentelemetry-opentracing-shim-1.49.0.jar +Comparing source compatibility of opentelemetry-opentracing-shim-1.51.0-SNAPSHOT.jar against opentelemetry-opentracing-shim-1.50.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 7e8288c8dcb..ddc7f6a4328 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.51.0-SNAPSHOT.jar against opentelemetry-sdk-common-1.49.0.jar +Comparing source compatibility of opentelemetry-sdk-common-1.51.0-SNAPSHOT.jar against opentelemetry-sdk-common-1.50.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 1bf987b6fdc..d190b03dbf8 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.51.0-SNAPSHOT.jar against opentelemetry-sdk-extension-autoconfigure-spi-1.49.0.jar +Comparing source compatibility of opentelemetry-sdk-extension-autoconfigure-spi-1.51.0-SNAPSHOT.jar against opentelemetry-sdk-extension-autoconfigure-spi-1.50.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 fbea2fa8018..d71a4556ee9 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.51.0-SNAPSHOT.jar against opentelemetry-sdk-extension-autoconfigure-1.49.0.jar +Comparing source compatibility of opentelemetry-sdk-extension-autoconfigure-1.51.0-SNAPSHOT.jar against opentelemetry-sdk-extension-autoconfigure-1.50.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 bfea6a73db4..aef1d257325 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.51.0-SNAPSHOT.jar against opentelemetry-sdk-extension-jaeger-remote-sampler-1.49.0.jar +Comparing source compatibility of opentelemetry-sdk-extension-jaeger-remote-sampler-1.51.0-SNAPSHOT.jar against opentelemetry-sdk-extension-jaeger-remote-sampler-1.50.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 aeb9aaf747d..12d600f52ef 100644 --- a/docs/apidiffs/current_vs_latest/opentelemetry-sdk-logs.txt +++ b/docs/apidiffs/current_vs_latest/opentelemetry-sdk-logs.txt @@ -1,12 +1,2 @@ -Comparing source compatibility of opentelemetry-sdk-logs-1.51.0-SNAPSHOT.jar against opentelemetry-sdk-logs-1.49.0.jar -*** MODIFIED INTERFACE: PUBLIC ABSTRACT io.opentelemetry.sdk.logs.data.LogRecordData (not serializable) - === CLASS FILE FORMAT VERSION: 52.0 <- 52.0 - +++ NEW METHOD: PUBLIC(+) java.lang.String getEventName() - +++ NEW ANNOTATION: javax.annotation.Nullable -*** MODIFIED INTERFACE: PUBLIC ABSTRACT io.opentelemetry.sdk.logs.ReadWriteLogRecord (not serializable) - === CLASS FILE FORMAT VERSION: 52.0 <- 52.0 - +++ NEW METHOD: PUBLIC(+) java.lang.String getEventName() - +++ NEW ANNOTATION: javax.annotation.Nullable -*** MODIFIED CLASS: PUBLIC FINAL io.opentelemetry.sdk.logs.SdkLoggerProviderBuilder (not serializable) - === CLASS FILE FORMAT VERSION: 52.0 <- 52.0 - +++ NEW METHOD: PUBLIC(+) io.opentelemetry.sdk.logs.SdkLoggerProviderBuilder addLogRecordProcessorFirst(io.opentelemetry.sdk.logs.LogRecordProcessor) +Comparing source compatibility of opentelemetry-sdk-logs-1.51.0-SNAPSHOT.jar against opentelemetry-sdk-logs-1.50.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 3dc5b03f853..48ee9a2bf74 100644 --- a/docs/apidiffs/current_vs_latest/opentelemetry-sdk-metrics.txt +++ b/docs/apidiffs/current_vs_latest/opentelemetry-sdk-metrics.txt @@ -1,61 +1,2 @@ -Comparing source compatibility of opentelemetry-sdk-metrics-1.51.0-SNAPSHOT.jar against opentelemetry-sdk-metrics-1.49.0.jar -+++ NEW CLASS: PUBLIC(+) ABSTRACT(+) io.opentelemetry.sdk.metrics.data.DelegatingMetricData (not serializable) - +++ CLASS FILE FORMAT VERSION: 52.0 <- n.a. - +++ NEW INTERFACE: io.opentelemetry.sdk.metrics.data.MetricData - +++ NEW SUPERCLASS: java.lang.Object - +++ NEW METHOD: PUBLIC(+) boolean equals(java.lang.Object) - +++ NEW METHOD: PUBLIC(+) io.opentelemetry.sdk.metrics.data.Data getData() - +++ NEW METHOD: PUBLIC(+) java.lang.String getDescription() - +++ NEW METHOD: PUBLIC(+) io.opentelemetry.sdk.common.InstrumentationScopeInfo getInstrumentationScopeInfo() - +++ NEW METHOD: PUBLIC(+) java.lang.String getName() - +++ NEW METHOD: PUBLIC(+) io.opentelemetry.sdk.resources.Resource getResource() - +++ NEW METHOD: PUBLIC(+) io.opentelemetry.sdk.metrics.data.MetricDataType getType() - +++ NEW METHOD: PUBLIC(+) java.lang.String getUnit() - +++ NEW METHOD: PUBLIC(+) int hashCode() - +++ NEW METHOD: PUBLIC(+) java.lang.String toString() -*** MODIFIED INTERFACE: PUBLIC ABSTRACT io.opentelemetry.sdk.metrics.data.DoubleExemplarData (not serializable) - === CLASS FILE FORMAT VERSION: 52.0 <- 52.0 - +++ NEW METHOD: PUBLIC(+) STATIC(+) io.opentelemetry.sdk.metrics.data.DoubleExemplarData create(io.opentelemetry.api.common.Attributes, long, io.opentelemetry.api.trace.SpanContext, double) -*** MODIFIED INTERFACE: PUBLIC ABSTRACT io.opentelemetry.sdk.metrics.data.DoublePointData (not serializable) - === CLASS FILE FORMAT VERSION: 52.0 <- 52.0 - +++ NEW METHOD: PUBLIC(+) STATIC(+) io.opentelemetry.sdk.metrics.data.DoublePointData create(long, long, io.opentelemetry.api.common.Attributes, double, java.util.List) -*** MODIFIED INTERFACE: PUBLIC ABSTRACT io.opentelemetry.sdk.metrics.data.ExponentialHistogramBuckets (not serializable) - === CLASS FILE FORMAT VERSION: 52.0 <- 52.0 - +++ NEW METHOD: PUBLIC(+) STATIC(+) io.opentelemetry.sdk.metrics.data.ExponentialHistogramBuckets create(int, int, java.util.List) -*** MODIFIED INTERFACE: PUBLIC ABSTRACT io.opentelemetry.sdk.metrics.data.ExponentialHistogramData (not serializable) - === CLASS FILE FORMAT VERSION: 52.0 <- 52.0 - +++ NEW METHOD: PUBLIC(+) STATIC(+) io.opentelemetry.sdk.metrics.data.ExponentialHistogramData create(io.opentelemetry.sdk.metrics.data.AggregationTemporality, java.util.Collection) -*** MODIFIED INTERFACE: PUBLIC ABSTRACT io.opentelemetry.sdk.metrics.data.ExponentialHistogramPointData (not serializable) - === CLASS FILE FORMAT VERSION: 52.0 <- 52.0 - +++ NEW METHOD: PUBLIC(+) STATIC(+) io.opentelemetry.sdk.metrics.data.ExponentialHistogramPointData create(int, double, long, boolean, double, boolean, double, io.opentelemetry.sdk.metrics.data.ExponentialHistogramBuckets, io.opentelemetry.sdk.metrics.data.ExponentialHistogramBuckets, long, long, io.opentelemetry.api.common.Attributes, java.util.List) -*** MODIFIED INTERFACE: PUBLIC ABSTRACT io.opentelemetry.sdk.metrics.data.GaugeData (not serializable) - === CLASS FILE FORMAT VERSION: 52.0 <- 52.0 - GENERIC TEMPLATES: === T:io.opentelemetry.sdk.metrics.data.PointData - +++ NEW METHOD: PUBLIC(+) STATIC(+) io.opentelemetry.sdk.metrics.data.GaugeData createDoubleGaugeData(java.util.Collection) - +++ NEW METHOD: PUBLIC(+) STATIC(+) io.opentelemetry.sdk.metrics.data.GaugeData createLongGaugeData(java.util.Collection) -*** MODIFIED INTERFACE: PUBLIC ABSTRACT io.opentelemetry.sdk.metrics.data.HistogramData (not serializable) - === CLASS FILE FORMAT VERSION: 52.0 <- 52.0 - +++ NEW METHOD: PUBLIC(+) STATIC(+) io.opentelemetry.sdk.metrics.data.HistogramData create(io.opentelemetry.sdk.metrics.data.AggregationTemporality, java.util.Collection) -*** MODIFIED INTERFACE: PUBLIC ABSTRACT io.opentelemetry.sdk.metrics.data.HistogramPointData (not serializable) - === CLASS FILE FORMAT VERSION: 52.0 <- 52.0 - +++ NEW METHOD: PUBLIC(+) STATIC(+) io.opentelemetry.sdk.metrics.data.HistogramPointData create(long, long, io.opentelemetry.api.common.Attributes, double, boolean, double, boolean, double, java.util.List, java.util.List) -*** MODIFIED INTERFACE: PUBLIC ABSTRACT io.opentelemetry.sdk.metrics.data.LongExemplarData (not serializable) - === CLASS FILE FORMAT VERSION: 52.0 <- 52.0 - +++ NEW METHOD: PUBLIC(+) STATIC(+) io.opentelemetry.sdk.metrics.data.LongExemplarData create(io.opentelemetry.api.common.Attributes, long, io.opentelemetry.api.trace.SpanContext, long) -*** MODIFIED INTERFACE: PUBLIC ABSTRACT io.opentelemetry.sdk.metrics.data.LongPointData (not serializable) - === CLASS FILE FORMAT VERSION: 52.0 <- 52.0 - +++ NEW METHOD: PUBLIC(+) STATIC(+) io.opentelemetry.sdk.metrics.data.LongPointData create(long, long, io.opentelemetry.api.common.Attributes, long) -*** MODIFIED INTERFACE: PUBLIC ABSTRACT io.opentelemetry.sdk.metrics.data.SumData (not serializable) - === CLASS FILE FORMAT VERSION: 52.0 <- 52.0 - GENERIC TEMPLATES: === T:io.opentelemetry.sdk.metrics.data.PointData - +++ NEW METHOD: PUBLIC(+) STATIC(+) io.opentelemetry.sdk.metrics.data.SumData createDoubleSumData(boolean, io.opentelemetry.sdk.metrics.data.AggregationTemporality, java.util.Collection) - +++ NEW METHOD: PUBLIC(+) STATIC(+) io.opentelemetry.sdk.metrics.data.SumData createLongSumData(boolean, io.opentelemetry.sdk.metrics.data.AggregationTemporality, java.util.Collection) -*** MODIFIED INTERFACE: PUBLIC ABSTRACT io.opentelemetry.sdk.metrics.data.SummaryData (not serializable) - === CLASS FILE FORMAT VERSION: 52.0 <- 52.0 - +++ NEW METHOD: PUBLIC(+) STATIC(+) io.opentelemetry.sdk.metrics.data.SummaryData create(java.util.Collection) -*** MODIFIED INTERFACE: PUBLIC ABSTRACT io.opentelemetry.sdk.metrics.data.SummaryPointData (not serializable) - === CLASS FILE FORMAT VERSION: 52.0 <- 52.0 - +++ NEW METHOD: PUBLIC(+) STATIC(+) io.opentelemetry.sdk.metrics.data.SummaryPointData create(long, long, io.opentelemetry.api.common.Attributes, long, double, java.util.List) -*** MODIFIED INTERFACE: PUBLIC ABSTRACT io.opentelemetry.sdk.metrics.data.ValueAtQuantile (not serializable) - === CLASS FILE FORMAT VERSION: 52.0 <- 52.0 - +++ NEW METHOD: PUBLIC(+) STATIC(+) io.opentelemetry.sdk.metrics.data.ValueAtQuantile create(double, double) +Comparing source compatibility of opentelemetry-sdk-metrics-1.51.0-SNAPSHOT.jar against opentelemetry-sdk-metrics-1.50.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 b5f817ef929..c617393b6ce 100644 --- a/docs/apidiffs/current_vs_latest/opentelemetry-sdk-testing.txt +++ b/docs/apidiffs/current_vs_latest/opentelemetry-sdk-testing.txt @@ -1,33 +1,2 @@ -Comparing source compatibility of opentelemetry-sdk-testing-1.51.0-SNAPSHOT.jar against opentelemetry-sdk-testing-1.49.0.jar -*** MODIFIED CLASS: PUBLIC FINAL io.opentelemetry.sdk.testing.assertj.LogRecordDataAssert (not serializable) - === CLASS FILE FORMAT VERSION: 52.0 <- 52.0 - +++ NEW METHOD: PUBLIC(+) io.opentelemetry.sdk.testing.assertj.LogRecordDataAssert hasEventName(java.lang.String) -**** MODIFIED CLASS: PUBLIC ABSTRACT io.opentelemetry.sdk.testing.logs.TestLogRecordData (not serializable) - === CLASS FILE FORMAT VERSION: 52.0 <- 52.0 - +++* NEW METHOD: PUBLIC(+) ABSTRACT(+) java.lang.String getEventName() - +++ NEW ANNOTATION: javax.annotation.Nullable -**** MODIFIED CLASS: PUBLIC ABSTRACT STATIC io.opentelemetry.sdk.testing.logs.TestLogRecordData$Builder (not serializable) - === CLASS FILE FORMAT VERSION: 52.0 <- 52.0 - +++* NEW METHOD: PUBLIC(+) ABSTRACT(+) io.opentelemetry.sdk.testing.logs.TestLogRecordData$Builder setEventName(java.lang.String) -+++ NEW CLASS: PUBLIC(+) ABSTRACT(+) io.opentelemetry.sdk.testing.metrics.TestMetricData (not serializable) - +++ CLASS FILE FORMAT VERSION: 52.0 <- n.a. - +++ NEW SUPERCLASS: java.lang.Object - +++ NEW CONSTRUCTOR: PUBLIC(+) TestMetricData() - +++ NEW METHOD: PUBLIC(+) STATIC(+) io.opentelemetry.sdk.testing.metrics.TestMetricData$Builder builder() -+++ NEW CLASS: PUBLIC(+) ABSTRACT(+) STATIC(+) io.opentelemetry.sdk.testing.metrics.TestMetricData$Builder (not serializable) - +++ CLASS FILE FORMAT VERSION: 52.0 <- n.a. - +++ NEW SUPERCLASS: java.lang.Object - +++ NEW CONSTRUCTOR: PUBLIC(+) TestMetricData$Builder() - +++ NEW METHOD: PUBLIC(+) io.opentelemetry.sdk.testing.metrics.TestMetricData build() - +++ NEW METHOD: PUBLIC(+) ABSTRACT(+) io.opentelemetry.sdk.testing.metrics.TestMetricData$Builder setDescription(java.lang.String) - +++ NEW METHOD: PUBLIC(+) io.opentelemetry.sdk.testing.metrics.TestMetricData$Builder setDoubleGaugeData(io.opentelemetry.sdk.metrics.data.GaugeData) - +++ NEW METHOD: PUBLIC(+) io.opentelemetry.sdk.testing.metrics.TestMetricData$Builder setDoubleSumData(io.opentelemetry.sdk.metrics.data.SumData) - +++ NEW METHOD: PUBLIC(+) io.opentelemetry.sdk.testing.metrics.TestMetricData$Builder setExponentialHistogramData(io.opentelemetry.sdk.metrics.data.ExponentialHistogramData) - +++ NEW METHOD: PUBLIC(+) io.opentelemetry.sdk.testing.metrics.TestMetricData$Builder setHistogramData(io.opentelemetry.sdk.metrics.data.HistogramData) - +++ NEW METHOD: PUBLIC(+) ABSTRACT(+) io.opentelemetry.sdk.testing.metrics.TestMetricData$Builder setInstrumentationScopeInfo(io.opentelemetry.sdk.common.InstrumentationScopeInfo) - +++ NEW METHOD: PUBLIC(+) io.opentelemetry.sdk.testing.metrics.TestMetricData$Builder setLongGaugeData(io.opentelemetry.sdk.metrics.data.GaugeData) - +++ NEW METHOD: PUBLIC(+) io.opentelemetry.sdk.testing.metrics.TestMetricData$Builder setLongSumData(io.opentelemetry.sdk.metrics.data.SumData) - +++ NEW METHOD: PUBLIC(+) ABSTRACT(+) io.opentelemetry.sdk.testing.metrics.TestMetricData$Builder setName(java.lang.String) - +++ NEW METHOD: PUBLIC(+) ABSTRACT(+) io.opentelemetry.sdk.testing.metrics.TestMetricData$Builder setResource(io.opentelemetry.sdk.resources.Resource) - +++ NEW METHOD: PUBLIC(+) io.opentelemetry.sdk.testing.metrics.TestMetricData$Builder setSummaryData(io.opentelemetry.sdk.metrics.data.SummaryData) - +++ NEW METHOD: PUBLIC(+) ABSTRACT(+) io.opentelemetry.sdk.testing.metrics.TestMetricData$Builder setUnit(java.lang.String) +Comparing source compatibility of opentelemetry-sdk-testing-1.51.0-SNAPSHOT.jar against opentelemetry-sdk-testing-1.50.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 8a439fc3dd5..cc1f1483cea 100644 --- a/docs/apidiffs/current_vs_latest/opentelemetry-sdk-trace.txt +++ b/docs/apidiffs/current_vs_latest/opentelemetry-sdk-trace.txt @@ -1,4 +1,2 @@ -Comparing source compatibility of opentelemetry-sdk-trace-1.51.0-SNAPSHOT.jar against opentelemetry-sdk-trace-1.49.0.jar -*** MODIFIED CLASS: PUBLIC FINAL io.opentelemetry.sdk.trace.SdkTracerProviderBuilder (not serializable) - === CLASS FILE FORMAT VERSION: 52.0 <- 52.0 - +++ NEW METHOD: PUBLIC(+) io.opentelemetry.sdk.trace.SdkTracerProviderBuilder addSpanProcessorFirst(io.opentelemetry.sdk.trace.SpanProcessor) +Comparing source compatibility of opentelemetry-sdk-trace-1.51.0-SNAPSHOT.jar against opentelemetry-sdk-trace-1.50.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 4c89342122a..696082a0627 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.51.0-SNAPSHOT.jar against opentelemetry-sdk-1.49.0.jar +Comparing source compatibility of opentelemetry-sdk-1.51.0-SNAPSHOT.jar against opentelemetry-sdk-1.50.0.jar No changes. \ No newline at end of file From bed02d5f616309db054e36c34bf66697c0a1ff35 Mon Sep 17 00:00:00 2001 From: Jonathan Halliday Date: Tue, 13 May 2025 16:53:53 +0100 Subject: [PATCH 04/42] Add gRPC export for profiles signal type. (#7301) Co-authored-by: Jack Berg --- .../internal/grpc/GrpcExporterUtil.java | 13 + exporters/otlp/profiles/build.gradle.kts | 6 + .../ExportProfilesServiceResponse.java | 15 ++ .../MarshalerProfilesServiceGrpc.java | 90 +++++++ .../otlp/profiles/NoopProfileExporter.java | 38 +++ .../profiles/OtlpGrpcProfileExporter.java | 96 +++++++ .../OtlpGrpcProfilesExporterBuilder.java | 235 ++++++++++++++++++ .../otlp/profiles/ProfileExporter.java | 49 ++++ .../otlp/profiles/FakeTelemetryUtil.java | 55 ++++ .../profiles/NoopProfileExporterTest.java | 18 ++ .../profiles/OtlpGrpcProfileExporterTest.java | 83 +++++++ .../otlp/testing-internal/build.gradle.kts | 3 + .../AbstractGrpcTelemetryExporterTest.java | 20 +- .../GrpcProfilesExporterBuilderWrapper.java | 133 ++++++++++ .../testing/internal/TelemetryExporter.java | 22 ++ .../internal/TelemetryExporterBuilder.java | 6 + 16 files changed, 878 insertions(+), 4 deletions(-) create mode 100644 exporters/otlp/profiles/src/main/java/io/opentelemetry/exporter/otlp/profiles/ExportProfilesServiceResponse.java create mode 100644 exporters/otlp/profiles/src/main/java/io/opentelemetry/exporter/otlp/profiles/MarshalerProfilesServiceGrpc.java create mode 100644 exporters/otlp/profiles/src/main/java/io/opentelemetry/exporter/otlp/profiles/NoopProfileExporter.java create mode 100644 exporters/otlp/profiles/src/main/java/io/opentelemetry/exporter/otlp/profiles/OtlpGrpcProfileExporter.java create mode 100644 exporters/otlp/profiles/src/main/java/io/opentelemetry/exporter/otlp/profiles/OtlpGrpcProfilesExporterBuilder.java create mode 100644 exporters/otlp/profiles/src/main/java/io/opentelemetry/exporter/otlp/profiles/ProfileExporter.java create mode 100644 exporters/otlp/profiles/src/test/java/io/opentelemetry/exporter/otlp/profiles/FakeTelemetryUtil.java create mode 100644 exporters/otlp/profiles/src/test/java/io/opentelemetry/exporter/otlp/profiles/NoopProfileExporterTest.java create mode 100644 exporters/otlp/profiles/src/test/java/io/opentelemetry/exporter/otlp/profiles/OtlpGrpcProfileExporterTest.java create mode 100644 exporters/otlp/testing-internal/src/main/java/io/opentelemetry/exporter/otlp/testing/internal/GrpcProfilesExporterBuilderWrapper.java diff --git a/exporters/common/src/main/java/io/opentelemetry/exporter/internal/grpc/GrpcExporterUtil.java b/exporters/common/src/main/java/io/opentelemetry/exporter/internal/grpc/GrpcExporterUtil.java index 2747b6a260e..65eb34801fe 100644 --- a/exporters/common/src/main/java/io/opentelemetry/exporter/internal/grpc/GrpcExporterUtil.java +++ b/exporters/common/src/main/java/io/opentelemetry/exporter/internal/grpc/GrpcExporterUtil.java @@ -28,6 +28,19 @@ public final class GrpcExporterUtil { public static final int GRPC_STATUS_DATA_LOSS = 15; static void logUnimplemented(Logger logger, String type, @Nullable String fullErrorMessage) { + + // hopefully temporary special handling for profile signal as it evolves towards stability. + if ("profile".equals(type)) { + logger.log( + Level.SEVERE, + "Failed to export profile. The profile signal type is still under development " + + "and the endpoint you are connecting to may not support it yet, " + + "or may support a different version. " + + "Full error message: " + + fullErrorMessage); + return; + } + String envVar; switch (type) { case "span": diff --git a/exporters/otlp/profiles/build.gradle.kts b/exporters/otlp/profiles/build.gradle.kts index 51979a5c8c6..00ddf18651d 100644 --- a/exporters/otlp/profiles/build.gradle.kts +++ b/exporters/otlp/profiles/build.gradle.kts @@ -15,10 +15,16 @@ dependencies { api(project(":exporters:common")) implementation(project(":exporters:otlp:common")) + implementation(project(":exporters:otlp:all")) + compileOnly("io.grpc:grpc-stub") + annotationProcessor("com.google.auto.value:auto-value") testCompileOnly("com.google.guava:guava") testImplementation("com.fasterxml.jackson.core:jackson-databind") testImplementation("com.google.protobuf:protobuf-java-util") testImplementation("io.opentelemetry.proto:opentelemetry-proto") + testImplementation(project(":exporters:otlp:testing-internal")) + testImplementation(project(":exporters:sender:okhttp")) + testImplementation("io.grpc:grpc-stub") } diff --git a/exporters/otlp/profiles/src/main/java/io/opentelemetry/exporter/otlp/profiles/ExportProfilesServiceResponse.java b/exporters/otlp/profiles/src/main/java/io/opentelemetry/exporter/otlp/profiles/ExportProfilesServiceResponse.java new file mode 100644 index 00000000000..ce0ce3c6d65 --- /dev/null +++ b/exporters/otlp/profiles/src/main/java/io/opentelemetry/exporter/otlp/profiles/ExportProfilesServiceResponse.java @@ -0,0 +1,15 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.exporter.otlp.profiles; + +// A Java object to correspond to the gRPC response for the ProfilesService.Export method. If fields +// are added to the type in the future, this can be converted to an actual class. +// +// It may seem like Void could be used instead but gRPC does not allow response values to be +// null. +enum ExportProfilesServiceResponse { + INSTANCE; +} diff --git a/exporters/otlp/profiles/src/main/java/io/opentelemetry/exporter/otlp/profiles/MarshalerProfilesServiceGrpc.java b/exporters/otlp/profiles/src/main/java/io/opentelemetry/exporter/otlp/profiles/MarshalerProfilesServiceGrpc.java new file mode 100644 index 00000000000..6f0bdd6ae5d --- /dev/null +++ b/exporters/otlp/profiles/src/main/java/io/opentelemetry/exporter/otlp/profiles/MarshalerProfilesServiceGrpc.java @@ -0,0 +1,90 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.exporter.otlp.profiles; + +import static io.grpc.MethodDescriptor.generateFullMethodName; + +import com.google.common.util.concurrent.ListenableFuture; +import io.grpc.CallOptions; +import io.grpc.Channel; +import io.grpc.MethodDescriptor; +import io.grpc.stub.ClientCalls; +import io.opentelemetry.exporter.internal.grpc.MarshalerInputStream; +import io.opentelemetry.exporter.internal.grpc.MarshalerServiceStub; +import io.opentelemetry.exporter.internal.marshal.Marshaler; +import java.io.InputStream; +import javax.annotation.Nullable; + +// Adapted from the protoc generated code for ProfilesServiceGrpc. +final class MarshalerProfilesServiceGrpc { + + private static final String SERVICE_NAME = + "opentelemetry.proto.collector.profiles.v1development.ProfilesService"; + + private static final MethodDescriptor.Marshaller REQUEST_MARSHALLER = + new MethodDescriptor.Marshaller() { + @Override + public InputStream stream(Marshaler value) { + return new MarshalerInputStream(value); + } + + @Override + public Marshaler parse(InputStream stream) { + throw new UnsupportedOperationException("Only for serializing"); + } + }; + + private static final MethodDescriptor.Marshaller + RESPONSE_MARSHALER = + new MethodDescriptor.Marshaller() { + @Override + public InputStream stream(ExportProfilesServiceResponse value) { + throw new UnsupportedOperationException("Only for parsing"); + } + + @Override + public ExportProfilesServiceResponse parse(InputStream stream) { + return ExportProfilesServiceResponse.INSTANCE; + } + }; + + private static final MethodDescriptor getExportMethod = + MethodDescriptor.newBuilder() + .setType(MethodDescriptor.MethodType.UNARY) + .setFullMethodName(generateFullMethodName(SERVICE_NAME, "Export")) + .setRequestMarshaller(REQUEST_MARSHALLER) + .setResponseMarshaller(RESPONSE_MARSHALER) + .build(); + + static ProfilesServiceFutureStub newFutureStub( + Channel channel, @Nullable String authorityOverride) { + return ProfilesServiceFutureStub.newStub( + (c, options) -> new ProfilesServiceFutureStub(c, options.withAuthority(authorityOverride)), + channel); + } + + static final class ProfilesServiceFutureStub + extends MarshalerServiceStub< + Marshaler, ExportProfilesServiceResponse, ProfilesServiceFutureStub> { + private ProfilesServiceFutureStub(Channel channel, CallOptions callOptions) { + super(channel, callOptions); + } + + @Override + protected MarshalerProfilesServiceGrpc.ProfilesServiceFutureStub build( + Channel channel, CallOptions callOptions) { + return new MarshalerProfilesServiceGrpc.ProfilesServiceFutureStub(channel, callOptions); + } + + @Override + public ListenableFuture export(Marshaler request) { + return ClientCalls.futureUnaryCall( + getChannel().newCall(getExportMethod, getCallOptions()), request); + } + } + + private MarshalerProfilesServiceGrpc() {} +} diff --git a/exporters/otlp/profiles/src/main/java/io/opentelemetry/exporter/otlp/profiles/NoopProfileExporter.java b/exporters/otlp/profiles/src/main/java/io/opentelemetry/exporter/otlp/profiles/NoopProfileExporter.java new file mode 100644 index 00000000000..0978692ad5c --- /dev/null +++ b/exporters/otlp/profiles/src/main/java/io/opentelemetry/exporter/otlp/profiles/NoopProfileExporter.java @@ -0,0 +1,38 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.exporter.otlp.profiles; + +import io.opentelemetry.sdk.common.CompletableResultCode; +import java.util.Collection; + +final class NoopProfileExporter implements ProfileExporter { + + private static final ProfileExporter INSTANCE = new NoopProfileExporter(); + + static ProfileExporter getInstance() { + return INSTANCE; + } + + @Override + public CompletableResultCode export(Collection profiles) { + return CompletableResultCode.ofSuccess(); + } + + @Override + public CompletableResultCode flush() { + return CompletableResultCode.ofSuccess(); + } + + @Override + public CompletableResultCode shutdown() { + return CompletableResultCode.ofSuccess(); + } + + @Override + public String toString() { + return "NoopProfilesExporter{}"; + } +} diff --git a/exporters/otlp/profiles/src/main/java/io/opentelemetry/exporter/otlp/profiles/OtlpGrpcProfileExporter.java b/exporters/otlp/profiles/src/main/java/io/opentelemetry/exporter/otlp/profiles/OtlpGrpcProfileExporter.java new file mode 100644 index 00000000000..a598c8b1a84 --- /dev/null +++ b/exporters/otlp/profiles/src/main/java/io/opentelemetry/exporter/otlp/profiles/OtlpGrpcProfileExporter.java @@ -0,0 +1,96 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.exporter.otlp.profiles; + +import io.opentelemetry.exporter.internal.grpc.GrpcExporter; +import io.opentelemetry.exporter.internal.grpc.GrpcExporterBuilder; +import io.opentelemetry.exporter.internal.marshal.Marshaler; +import io.opentelemetry.sdk.common.CompletableResultCode; +import java.util.Collection; +import java.util.StringJoiner; +import javax.annotation.concurrent.ThreadSafe; + +/** Exports profiles using OTLP via gRPC, using OpenTelemetry's protobuf model. */ +@ThreadSafe +public class OtlpGrpcProfileExporter implements ProfileExporter { + + private final GrpcExporterBuilder builder; + private final GrpcExporter delegate; + + /** + * Returns a new {@link OtlpGrpcProfileExporter} using the default values. + * + *

To load configuration values from environment variables and system properties, use opentelemetry-sdk-extension-autoconfigure. + * + * @return a new {@link OtlpGrpcProfileExporter} instance. + */ + public static OtlpGrpcProfileExporter getDefault() { + return builder().build(); + } + + /** + * Returns a new builder instance for this exporter. + * + * @return a new builder instance for this exporter. + */ + public static OtlpGrpcProfilesExporterBuilder builder() { + return new OtlpGrpcProfilesExporterBuilder(); + } + + OtlpGrpcProfileExporter( + GrpcExporterBuilder builder, GrpcExporter delegate) { + this.builder = builder; + this.delegate = delegate; + } + + /** + * Returns a builder with configuration values equal to those for this exporter. + * + *

IMPORTANT: Be sure to {@link #shutdown()} this instance if it will no longer be used. + */ + public OtlpGrpcProfilesExporterBuilder toBuilder() { + return new OtlpGrpcProfilesExporterBuilder(builder.copy()); + } + + /** + * Submits all the given profiles in a single batch to the OpenTelemetry collector. + * + * @param profiles the list of sampled profiles to be exported. + * @return the result of the operation + */ + @Override + public CompletableResultCode export(Collection profiles) { + ProfilesRequestMarshaler request = ProfilesRequestMarshaler.create(profiles); + return delegate.export(request, profiles.size()); + } + + /** + * The OTLP exporter does not batch items, so this method will immediately return with success. + * + * @return always Success + */ + @Override + public CompletableResultCode flush() { + return CompletableResultCode.ofSuccess(); + } + + /** + * Initiates an orderly shutdown in which preexisting calls continue but new calls are immediately + * cancelled. + */ + @Override + public CompletableResultCode shutdown() { + return delegate.shutdown(); + } + + @Override + public String toString() { + StringJoiner joiner = new StringJoiner(", ", "OtlpGrpcProfilesExporter{", "}"); + joiner.add(builder.toString(false)); + return joiner.toString(); + } +} 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 new file mode 100644 index 00000000000..604178d7fd5 --- /dev/null +++ b/exporters/otlp/profiles/src/main/java/io/opentelemetry/exporter/otlp/profiles/OtlpGrpcProfilesExporterBuilder.java @@ -0,0 +1,235 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.exporter.otlp.profiles; + +import static io.opentelemetry.api.internal.Utils.checkArgument; +import static java.util.Objects.requireNonNull; + +import io.grpc.ManagedChannel; +import io.opentelemetry.api.metrics.MeterProvider; +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; +import io.opentelemetry.sdk.common.export.RetryPolicy; +import java.net.URI; +import java.time.Duration; +import java.util.Map; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.TimeUnit; +import java.util.function.Supplier; +import javax.annotation.Nullable; +import javax.net.ssl.SSLContext; +import javax.net.ssl.X509TrustManager; + +/** Builder utility for this exporter. */ +public final class OtlpGrpcProfilesExporterBuilder { + + // Visible for testing + static final String GRPC_SERVICE_NAME = + "opentelemetry.proto.collector.profiles.v1development.ProfilesService"; + // Visible for testing + static final String GRPC_ENDPOINT_PATH = "/" + GRPC_SERVICE_NAME + "/Export"; + + private static final String DEFAULT_ENDPOINT_URL = "http://localhost:4317"; + private static final URI DEFAULT_ENDPOINT = URI.create(DEFAULT_ENDPOINT_URL); + private static final long DEFAULT_TIMEOUT_SECS = 10; + + // TODO maybe make more efficient by adding support for MEMORY_MODE + + // Visible for testing + final GrpcExporterBuilder delegate; + + OtlpGrpcProfilesExporterBuilder(GrpcExporterBuilder delegate) { + this.delegate = delegate; + delegate.setMeterProvider(MeterProvider::noop); + OtlpUserAgent.addUserAgentHeader(delegate::addConstantHeader); + } + + OtlpGrpcProfilesExporterBuilder() { + this( + new GrpcExporterBuilder<>( + "otlp", + "profile", + DEFAULT_TIMEOUT_SECS, + DEFAULT_ENDPOINT, + () -> MarshalerProfilesServiceGrpc::newFutureStub, + GRPC_ENDPOINT_PATH)); + } + + /** + * Sets the managed chanel to use when communicating with the backend. Takes precedence over + * {@link #setEndpoint(String)} if both are called. + * + *

Note: calling this overrides the spec compliant {@code User-Agent} header. To ensure spec + * compliance, set {@link io.grpc.ManagedChannelBuilder#userAgent(String)} to {@link + * OtlpUserAgent#getUserAgent()} when building the channel. + * + * @param channel the channel to use + * @return this builder's instance + * @deprecated Use {@link #setEndpoint(String)}. If you have a use case not satisfied by the + * methods on this builder, please file an issue to let us know what it is. + */ + @Deprecated + public OtlpGrpcProfilesExporterBuilder setChannel(ManagedChannel channel) { + delegate.setChannel(channel); + return this; + } + + /** + * Sets the maximum time to wait for the collector to process an exported batch of profiles. If + * unset, defaults to {@value DEFAULT_TIMEOUT_SECS}s. + */ + public OtlpGrpcProfilesExporterBuilder setTimeout(long timeout, TimeUnit unit) { + requireNonNull(unit, "unit"); + checkArgument(timeout >= 0, "timeout must be non-negative"); + delegate.setTimeout(timeout, unit); + return this; + } + + /** + * Sets the maximum time to wait for the collector to process an exported batch of profiles. If + * unset, defaults to {@value DEFAULT_TIMEOUT_SECS}s. + */ + public OtlpGrpcProfilesExporterBuilder setTimeout(Duration timeout) { + requireNonNull(timeout, "timeout"); + delegate.setTimeout(timeout); + return this; + } + + /** + * Sets the maximum time to wait for new connections to be established. If unset, defaults to + * {@value GrpcExporterBuilder#DEFAULT_CONNECT_TIMEOUT_SECS}s. + */ + public OtlpGrpcProfilesExporterBuilder setConnectTimeout(long timeout, TimeUnit unit) { + requireNonNull(unit, "unit"); + checkArgument(timeout >= 0, "timeout must be non-negative"); + delegate.setConnectTimeout(timeout, unit); + return this; + } + + /** + * Sets the maximum time to wait for new connections to be established. If unset, defaults to + * {@value GrpcExporterBuilder#DEFAULT_CONNECT_TIMEOUT_SECS}s. + */ + public OtlpGrpcProfilesExporterBuilder setConnectTimeout(Duration timeout) { + requireNonNull(timeout, "timeout"); + return setConnectTimeout(timeout.toNanos(), TimeUnit.NANOSECONDS); + } + + /** + * Sets the OTLP endpoint to connect to. If unset, defaults to {@value DEFAULT_ENDPOINT_URL}. The + * endpoint must start with either http:// or https://. + */ + public OtlpGrpcProfilesExporterBuilder setEndpoint(String endpoint) { + requireNonNull(endpoint, "endpoint"); + delegate.setEndpoint(endpoint); + 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 OtlpGrpcProfilesExporterBuilder setCompression(String compressionMethod) { + requireNonNull(compressionMethod, "compressionMethod"); + Compressor compressor = CompressorUtil.validateAndResolveCompressor(compressionMethod); + delegate.setCompression(compressor); + return this; + } + + /** + * Sets the certificate chain to use for verifying servers when TLS is enabled. The {@code byte[]} + * should contain an X.509 certificate collection in PEM format. If not set, TLS connections will + * use the system default trusted certificates. + */ + public OtlpGrpcProfilesExporterBuilder setTrustedCertificates(byte[] trustedCertificatesPem) { + delegate.setTrustManagerFromCerts(trustedCertificatesPem); + return this; + } + + /** + * Sets ths client key and the certificate chain to use for verifying client when TLS is enabled. + * The key must be PKCS8, and both must be in PEM format. + */ + public OtlpGrpcProfilesExporterBuilder setClientTls(byte[] privateKeyPem, byte[] certificatePem) { + delegate.setKeyManagerFromCerts(privateKeyPem, certificatePem); + return this; + } + + /** + * Sets the "bring-your-own" SSLContext for use with TLS. Users should call this _or_ set raw + * certificate bytes, but not both. + */ + public OtlpGrpcProfilesExporterBuilder setSslContext( + SSLContext sslContext, X509TrustManager trustManager) { + delegate.setSslContext(sslContext, trustManager); + return this; + } + + /** + * Add a constant header to requests. If the {@code key} collides with another constant header + * name or a one from {@link #setHeaders(Supplier)}, the values from both are included. + * + * @param key header key + * @param value header value + * @return this builder's instance + */ + public OtlpGrpcProfilesExporterBuilder addHeader(String key, String value) { + delegate.addConstantHeader(key, value); + return this; + } + + /** + * Set the supplier of headers to add to requests. If a key from the map collides with a constant + * from {@link #addHeader(String, String)}, the values from both are included. + */ + public OtlpGrpcProfilesExporterBuilder setHeaders(Supplier> headerSupplier) { + delegate.setHeadersSupplier(headerSupplier); + return this; + } + + /** + * Set the retry policy, or {@code null} to disable retry. Retry policy is {@link + * RetryPolicy#getDefault()} by default + */ + public OtlpGrpcProfilesExporterBuilder setRetryPolicy(@Nullable RetryPolicy retryPolicy) { + delegate.setRetryPolicy(retryPolicy); + return this; + } + + /** Set the {@link ClassLoader} used to load the sender API. */ + public OtlpGrpcProfilesExporterBuilder setServiceClassLoader(ClassLoader serviceClassLoader) { + requireNonNull(serviceClassLoader, "serviceClassLoader"); + delegate.setServiceClassLoader(serviceClassLoader); + return this; + } + + /** + * Set the {@link ExecutorService} used to execute requests. + * + *

NOTE: By calling this method, you are opting into managing the lifecycle of the {@code + * executorService}. {@link ExecutorService#shutdown()} will NOT be called when this exporter is + * shutdown. + */ + public OtlpGrpcProfilesExporterBuilder setExecutorService(ExecutorService executorService) { + requireNonNull(executorService, "executorService"); + delegate.setExecutorService(executorService); + return this; + } + + /** + * Constructs a new instance of the exporter based on the builder's values. + * + * @return a new exporter's instance + */ + public OtlpGrpcProfileExporter build() { + return new OtlpGrpcProfileExporter(delegate, delegate.build()); + } +} diff --git a/exporters/otlp/profiles/src/main/java/io/opentelemetry/exporter/otlp/profiles/ProfileExporter.java b/exporters/otlp/profiles/src/main/java/io/opentelemetry/exporter/otlp/profiles/ProfileExporter.java new file mode 100644 index 00000000000..1ba75a6757d --- /dev/null +++ b/exporters/otlp/profiles/src/main/java/io/opentelemetry/exporter/otlp/profiles/ProfileExporter.java @@ -0,0 +1,49 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.exporter.otlp.profiles; + +import io.opentelemetry.sdk.common.CompletableResultCode; +import java.io.Closeable; +import java.util.Collection; +import java.util.concurrent.TimeUnit; + +/** + * An interface that allows different profiling services to export recorded data in their own + * format. + */ +public interface ProfileExporter extends Closeable { + + /** + * Called to export sampled {@code ProfileData}s. Note that export operations can be performed + * simultaneously depending on the type of processor being used. + * + * @param profiles the collection of sampled profiles to be exported. + * @return the result of the export, which is often an asynchronous operation. + */ + CompletableResultCode export(Collection profiles); + + /** + * Exports the collection of sampled {@code ProfileData}s that have not yet been exported. Note + * that export operations can be performed simultaneously depending on the type of span processor + * being used. + * + * @return the result of the flush, which is often an asynchronous operation. + */ + CompletableResultCode flush(); + + /** + * Asynchronously terminate operation of the exporter. + * + * @return a {@link CompletableResultCode} which is completed when shutdown completes. + */ + CompletableResultCode shutdown(); + + /** Closes this {@link ProfileExporter}, releasing any resources. */ + @Override + default void close() { + shutdown().join(10, TimeUnit.SECONDS); + } +} diff --git a/exporters/otlp/profiles/src/test/java/io/opentelemetry/exporter/otlp/profiles/FakeTelemetryUtil.java b/exporters/otlp/profiles/src/test/java/io/opentelemetry/exporter/otlp/profiles/FakeTelemetryUtil.java new file mode 100644 index 00000000000..2c511bac6bf --- /dev/null +++ b/exporters/otlp/profiles/src/test/java/io/opentelemetry/exporter/otlp/profiles/FakeTelemetryUtil.java @@ -0,0 +1,55 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.exporter.otlp.profiles; + +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.exporter.otlp.internal.data.ImmutableProfileData; +import io.opentelemetry.exporter.otlp.internal.data.ImmutableValueTypeData; +import io.opentelemetry.sdk.common.InstrumentationScopeInfo; +import io.opentelemetry.sdk.resources.Resource; +import java.nio.ByteBuffer; +import java.util.Collections; + +// TODO eventually merge with io.opentelemetry.exporter.otlp.testing.internal.FakeTelemetryUtil +class FakeTelemetryUtil { + + private FakeTelemetryUtil() {} + + private static final InstrumentationScopeInfo SCOPE_INFO = + InstrumentationScopeInfo.builder("testLib") + .setVersion("1.0") + .setSchemaUrl("http://url") + .build(); + + /** Generate a fake {@link ProfileData}. */ + static ProfileData generateFakeProfileData() { + String profileId = "0123456789abcdef0123456789abcdef"; + return ImmutableProfileData.create( + Resource.create(Attributes.empty()), + SCOPE_INFO, + Collections.emptyList(), + Collections.emptyList(), + Collections.emptyList(), + Collections.emptyList(), + Collections.emptyList(), + Collections.emptyList(), + Collections.emptyList(), + Collections.emptyList(), + Collections.emptyList(), + Collections.emptyList(), + 5L, + 6L, + ImmutableValueTypeData.create(1, 2, AggregationTemporality.CUMULATIVE), + 7L, + Collections.emptyList(), + 0, + profileId, + Collections.emptyList(), + 3, + "format", + ByteBuffer.wrap(new byte[] {4, 5})); + } +} diff --git a/exporters/otlp/profiles/src/test/java/io/opentelemetry/exporter/otlp/profiles/NoopProfileExporterTest.java b/exporters/otlp/profiles/src/test/java/io/opentelemetry/exporter/otlp/profiles/NoopProfileExporterTest.java new file mode 100644 index 00000000000..c96ff85d70f --- /dev/null +++ b/exporters/otlp/profiles/src/test/java/io/opentelemetry/exporter/otlp/profiles/NoopProfileExporterTest.java @@ -0,0 +1,18 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.exporter.otlp.profiles; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.jupiter.api.Test; + +class NoopProfileExporterTest { + + @Test + void stringRepresentation() { + assertThat(NoopProfileExporter.getInstance()).hasToString("NoopProfilesExporter{}"); + } +} diff --git a/exporters/otlp/profiles/src/test/java/io/opentelemetry/exporter/otlp/profiles/OtlpGrpcProfileExporterTest.java b/exporters/otlp/profiles/src/test/java/io/opentelemetry/exporter/otlp/profiles/OtlpGrpcProfileExporterTest.java new file mode 100644 index 00000000000..eec37eeab17 --- /dev/null +++ b/exporters/otlp/profiles/src/test/java/io/opentelemetry/exporter/otlp/profiles/OtlpGrpcProfileExporterTest.java @@ -0,0 +1,83 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.exporter.otlp.profiles; + +import static org.assertj.core.api.Assertions.assertThat; + +import io.opentelemetry.exporter.internal.grpc.GrpcExporter; +import io.opentelemetry.exporter.internal.marshal.Marshaler; +import io.opentelemetry.exporter.otlp.testing.internal.AbstractGrpcTelemetryExporterTest; +import io.opentelemetry.exporter.otlp.testing.internal.TelemetryExporter; +import io.opentelemetry.exporter.otlp.testing.internal.TelemetryExporterBuilder; +import io.opentelemetry.exporter.sender.okhttp.internal.OkHttpGrpcSender; +import io.opentelemetry.internal.testing.slf4j.SuppressLogger; +import io.opentelemetry.proto.profiles.v1development.ResourceProfiles; +import java.io.Closeable; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.TimeUnit; +import org.junit.jupiter.api.Test; +import org.slf4j.event.Level; +import org.slf4j.event.LoggingEvent; + +class OtlpGrpcProfileExporterTest + extends AbstractGrpcTelemetryExporterTest { + + OtlpGrpcProfileExporterTest() { + super("profile", ResourceProfiles.getDefaultInstance()); + } + + @Test + void usingOkHttp() throws Exception { + try (Closeable exporter = OtlpGrpcProfileExporter.builder().build()) { + assertThat(exporter).extracting("delegate.grpcSender").isInstanceOf(OkHttpGrpcSender.class); + } + } + + @Test + @Override // whilst profile signal type is in development it uses a different error message + @SuppressLogger(GrpcExporter.class) + protected void testExport_Unimplemented() { + addGrpcError(12, "UNIMPLEMENTED"); + + TelemetryExporter exporter = nonRetryingExporter(); + + try { + assertThat( + exporter + .export(Collections.singletonList(generateFakeTelemetry())) + .join(10, TimeUnit.SECONDS) + .isSuccess()) + .isFalse(); + + LoggingEvent log = logs.assertContains("The profile signal type is still under development"); + assertThat(log.getLevel()).isEqualTo(Level.ERROR); + } finally { + exporter.shutdown(); + } + } + + @Override + protected TelemetryExporterBuilder exporterBuilder() { + return TelemetryExporterBuilder.wrap(OtlpGrpcProfileExporter.builder()); + } + + @Override + protected TelemetryExporterBuilder toBuilder( + TelemetryExporter exporter) { + return TelemetryExporterBuilder.wrap(((OtlpGrpcProfileExporter) exporter.unwrap()).toBuilder()); + } + + @Override + protected ProfileData generateFakeTelemetry() { + return FakeTelemetryUtil.generateFakeProfileData(); + } + + @Override + protected Marshaler[] toMarshalers(List telemetry) { + return ResourceProfilesMarshaler.create(telemetry); + } +} diff --git a/exporters/otlp/testing-internal/build.gradle.kts b/exporters/otlp/testing-internal/build.gradle.kts index 2c7c3cc9bd8..f4459686389 100644 --- a/exporters/otlp/testing-internal/build.gradle.kts +++ b/exporters/otlp/testing-internal/build.gradle.kts @@ -12,6 +12,9 @@ dependencies { api(project(":sdk:trace")) api(project(":sdk:testing")) + // ugly, but profiles doesn't have a separate API yet. + api(project(":exporters:otlp:profiles")) + api(project(":exporters:otlp:all")) // Must be compileOnly so gRPC isn't on the classpath for non-gRPC tests. diff --git a/exporters/otlp/testing-internal/src/main/java/io/opentelemetry/exporter/otlp/testing/internal/AbstractGrpcTelemetryExporterTest.java b/exporters/otlp/testing-internal/src/main/java/io/opentelemetry/exporter/otlp/testing/internal/AbstractGrpcTelemetryExporterTest.java index d8caa8ad22d..c87b6d1e199 100644 --- a/exporters/otlp/testing-internal/src/main/java/io/opentelemetry/exporter/otlp/testing/internal/AbstractGrpcTelemetryExporterTest.java +++ b/exporters/otlp/testing-internal/src/main/java/io/opentelemetry/exporter/otlp/testing/internal/AbstractGrpcTelemetryExporterTest.java @@ -39,6 +39,8 @@ import io.opentelemetry.proto.collector.logs.v1.ExportLogsServiceResponse; import io.opentelemetry.proto.collector.metrics.v1.ExportMetricsServiceRequest; import io.opentelemetry.proto.collector.metrics.v1.ExportMetricsServiceResponse; +import io.opentelemetry.proto.collector.profiles.v1development.ExportProfilesServiceRequest; +import io.opentelemetry.proto.collector.profiles.v1development.ExportProfilesServiceResponse; import io.opentelemetry.proto.collector.trace.v1.ExportTraceServiceRequest; import io.opentelemetry.proto.collector.trace.v1.ExportTraceServiceResponse; import io.opentelemetry.sdk.common.CompletableResultCode; @@ -138,6 +140,12 @@ protected void configure(ServerBuilder sb) { ExportLogsServiceRequest::parseFrom, ExportLogsServiceRequest::getResourceLogsList, ExportLogsServiceResponse.getDefaultInstance().toByteArray())); + sb.service( + "/opentelemetry.proto.collector.profiles.v1development.ProfilesService/Export", + new CollectorService<>( + ExportProfilesServiceRequest::parseFrom, + ExportProfilesServiceRequest::getResourceProfilesList, + ExportProfilesServiceResponse.getDefaultInstance().toByteArray())); sb.http(0); sb.https(0); @@ -180,7 +188,8 @@ protected CompletionStage handleMessage(ServiceRequestContext ctx, byte[ } } - @RegisterExtension LogCapturer logs = LogCapturer.create().captureForType(GrpcExporter.class); + @RegisterExtension + protected LogCapturer logs = LogCapturer.create().captureForType(GrpcExporter.class); private final String type; private final U resourceTelemetryInstance; @@ -702,7 +711,7 @@ void testExport_Unavailable() { @Test @SuppressLogger(GrpcExporter.class) - void testExport_Unimplemented() { + protected void testExport_Unimplemented() { addGrpcError(12, "UNIMPLEMENTED"); TelemetryExporter exporter = nonRetryingExporter(); @@ -725,6 +734,9 @@ void testExport_Unimplemented() { case "log": envVar = "OTEL_LOGS_EXPORTER"; break; + case "profile": + envVar = "OTEL_PROFILES_EXPORTER"; + break; default: throw new AssertionError(); } @@ -1149,11 +1161,11 @@ private List toProto(List telemetry) { .collect(Collectors.toList()); } - private TelemetryExporter nonRetryingExporter() { + protected TelemetryExporter nonRetryingExporter() { return exporterBuilder().setEndpoint(server.httpUri().toString()).setRetryPolicy(null).build(); } - private static void addGrpcError(int code, @Nullable String message) { + protected static void addGrpcError(int code, @Nullable String message) { grpcErrors.add(new ArmeriaStatusException(code, message)); } } diff --git a/exporters/otlp/testing-internal/src/main/java/io/opentelemetry/exporter/otlp/testing/internal/GrpcProfilesExporterBuilderWrapper.java b/exporters/otlp/testing-internal/src/main/java/io/opentelemetry/exporter/otlp/testing/internal/GrpcProfilesExporterBuilderWrapper.java new file mode 100644 index 00000000000..1adf15eddb2 --- /dev/null +++ b/exporters/otlp/testing-internal/src/main/java/io/opentelemetry/exporter/otlp/testing/internal/GrpcProfilesExporterBuilderWrapper.java @@ -0,0 +1,133 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.exporter.otlp.testing.internal; + +import io.grpc.ManagedChannel; +import io.opentelemetry.exporter.otlp.profiles.OtlpGrpcProfilesExporterBuilder; +import io.opentelemetry.exporter.otlp.profiles.ProfileData; +import io.opentelemetry.sdk.common.export.ProxyOptions; +import io.opentelemetry.sdk.common.export.RetryPolicy; +import java.time.Duration; +import java.util.Map; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.TimeUnit; +import java.util.function.Supplier; +import javax.annotation.Nullable; +import javax.net.ssl.SSLContext; +import javax.net.ssl.X509TrustManager; + +final class GrpcProfilesExporterBuilderWrapper implements TelemetryExporterBuilder { + private final OtlpGrpcProfilesExporterBuilder builder; + + GrpcProfilesExporterBuilderWrapper(OtlpGrpcProfilesExporterBuilder builder) { + this.builder = builder; + } + + @Override + public TelemetryExporterBuilder setEndpoint(String endpoint) { + builder.setEndpoint(endpoint); + return this; + } + + @Override + public TelemetryExporterBuilder setTimeout(long timeout, TimeUnit unit) { + builder.setTimeout(timeout, unit); + return this; + } + + @Override + public TelemetryExporterBuilder setTimeout(Duration timeout) { + builder.setTimeout(timeout); + return this; + } + + @Override + public TelemetryExporterBuilder setConnectTimeout(long timeout, TimeUnit unit) { + builder.setConnectTimeout(timeout, unit); + return this; + } + + @Override + public TelemetryExporterBuilder setConnectTimeout(Duration timeout) { + builder.setConnectTimeout(timeout); + return this; + } + + @Override + public TelemetryExporterBuilder setCompression(String compression) { + builder.setCompression(compression); + return this; + } + + @Override + public TelemetryExporterBuilder addHeader(String key, String value) { + builder.addHeader(key, value); + return this; + } + + @Override + public TelemetryExporterBuilder setHeaders( + Supplier> headerSupplier) { + builder.setHeaders(headerSupplier); + return this; + } + + @Override + public TelemetryExporterBuilder setTrustedCertificates(byte[] certificates) { + builder.setTrustedCertificates(certificates); + return this; + } + + @Override + public TelemetryExporterBuilder setClientTls( + byte[] privateKeyPem, byte[] certificatePem) { + builder.setClientTls(privateKeyPem, certificatePem); + return this; + } + + @Override + public TelemetryExporterBuilder setSslContext( + SSLContext sslContext, X509TrustManager trustManager) { + builder.setSslContext(sslContext, trustManager); + return this; + } + + @Override + public TelemetryExporterBuilder setRetryPolicy(@Nullable RetryPolicy retryPolicy) { + builder.setRetryPolicy(retryPolicy); + return this; + } + + @Override + public TelemetryExporterBuilder setProxyOptions(ProxyOptions proxyOptions) { + throw new UnsupportedOperationException("ProxyOptions are not supported for gRPC"); + } + + @Override + @SuppressWarnings("deprecation") // testing deprecated functionality + public TelemetryExporterBuilder setChannel(Object channel) { + builder.setChannel((ManagedChannel) channel); + return this; + } + + @Override + public TelemetryExporterBuilder setServiceClassLoader( + ClassLoader serviceClassLoader) { + builder.setServiceClassLoader(serviceClassLoader); + return this; + } + + @Override + public TelemetryExporterBuilder setExecutorService(ExecutorService executorService) { + builder.setExecutorService(executorService); + return this; + } + + @Override + public TelemetryExporter build() { + return TelemetryExporter.wrap(builder.build()); + } +} diff --git a/exporters/otlp/testing-internal/src/main/java/io/opentelemetry/exporter/otlp/testing/internal/TelemetryExporter.java b/exporters/otlp/testing-internal/src/main/java/io/opentelemetry/exporter/otlp/testing/internal/TelemetryExporter.java index 23af19dcefd..82778364d3b 100644 --- a/exporters/otlp/testing-internal/src/main/java/io/opentelemetry/exporter/otlp/testing/internal/TelemetryExporter.java +++ b/exporters/otlp/testing-internal/src/main/java/io/opentelemetry/exporter/otlp/testing/internal/TelemetryExporter.java @@ -5,6 +5,8 @@ package io.opentelemetry.exporter.otlp.testing.internal; +import io.opentelemetry.exporter.otlp.profiles.ProfileData; +import io.opentelemetry.exporter.otlp.profiles.ProfileExporter; import io.opentelemetry.sdk.common.CompletableResultCode; import io.opentelemetry.sdk.logs.data.LogRecordData; import io.opentelemetry.sdk.logs.export.LogRecordExporter; @@ -76,6 +78,26 @@ public CompletableResultCode shutdown() { }; } + /** Wraps a ProfilerExporter. */ + static TelemetryExporter wrap(ProfileExporter exporter) { + return new TelemetryExporter() { + @Override + public Object unwrap() { + return exporter; + } + + @Override + public CompletableResultCode export(Collection items) { + return exporter.export(items); + } + + @Override + public CompletableResultCode shutdown() { + return exporter.shutdown(); + } + }; + } + Object unwrap(); CompletableResultCode export(Collection items); diff --git a/exporters/otlp/testing-internal/src/main/java/io/opentelemetry/exporter/otlp/testing/internal/TelemetryExporterBuilder.java b/exporters/otlp/testing-internal/src/main/java/io/opentelemetry/exporter/otlp/testing/internal/TelemetryExporterBuilder.java index f8cf7ef4567..4283f361226 100644 --- a/exporters/otlp/testing-internal/src/main/java/io/opentelemetry/exporter/otlp/testing/internal/TelemetryExporterBuilder.java +++ b/exporters/otlp/testing-internal/src/main/java/io/opentelemetry/exporter/otlp/testing/internal/TelemetryExporterBuilder.java @@ -7,6 +7,8 @@ import io.opentelemetry.exporter.otlp.logs.OtlpGrpcLogRecordExporterBuilder; import io.opentelemetry.exporter.otlp.metrics.OtlpGrpcMetricExporterBuilder; +import io.opentelemetry.exporter.otlp.profiles.OtlpGrpcProfilesExporterBuilder; +import io.opentelemetry.exporter.otlp.profiles.ProfileData; import io.opentelemetry.exporter.otlp.trace.OtlpGrpcSpanExporterBuilder; import io.opentelemetry.sdk.common.export.ProxyOptions; import io.opentelemetry.sdk.common.export.RetryPolicy; @@ -36,6 +38,10 @@ static TelemetryExporterBuilder wrap(OtlpGrpcLogRecordExporterBui return new GrpcLogRecordExporterBuilderWrapper(builder); } + static TelemetryExporterBuilder wrap(OtlpGrpcProfilesExporterBuilder builder) { + return new GrpcProfilesExporterBuilderWrapper(builder); + } + TelemetryExporterBuilder setEndpoint(String endpoint); TelemetryExporterBuilder setTimeout(long timeout, TimeUnit unit); From 32194373165c0e20e590f9df3149235def0363cf Mon Sep 17 00:00:00 2001 From: Trask Stalnaker Date: Thu, 15 May 2025 12:37:01 -0700 Subject: [PATCH 05/42] Fix release workflow (#7341) --- .github/scripts/update-version.sh | 7 +++++++ .github/workflows/prepare-patch-release.yml | 2 +- .github/workflows/prepare-release-branch.yml | 4 ++-- 3 files changed, 10 insertions(+), 3 deletions(-) create mode 100755 .github/scripts/update-version.sh diff --git a/.github/scripts/update-version.sh b/.github/scripts/update-version.sh new file mode 100755 index 00000000000..43d3950ca87 --- /dev/null +++ b/.github/scripts/update-version.sh @@ -0,0 +1,7 @@ +#!/bin/bash -e + +version=$1 + +sed -Ei "s/[0-9]+\.[0-9]+\.[0-9]+/$version/" version.gradle.kts + +sed -Ei "1 s/(Comparing source compatibility of [a-z-]+)-[0-9]+\.[0-9]+\.[0-9]+(-SNAPSHOT)?.jar/\1-$version.jar/" docs/apidiffs/current_vs_latest/*.txt diff --git a/.github/workflows/prepare-patch-release.yml b/.github/workflows/prepare-patch-release.yml index db793322d61..c828b654173 100644 --- a/.github/workflows/prepare-patch-release.yml +++ b/.github/workflows/prepare-patch-release.yml @@ -37,7 +37,7 @@ jobs: echo "VERSION=$major_minor.$((patch + 1))" >> $GITHUB_ENV - name: Update version - run: sed -Ei "s/[0-9]+\.[0-9]+\.[0-9]+/$VERSION/" version.gradle.kts + run: .github/scripts/update-version.sh $VERSION - name: Update the change log with the approximate release date run: | diff --git a/.github/workflows/prepare-release-branch.yml b/.github/workflows/prepare-release-branch.yml index 8cf4c6764ab..74f17373417 100644 --- a/.github/workflows/prepare-release-branch.yml +++ b/.github/workflows/prepare-release-branch.yml @@ -100,11 +100,11 @@ jobs: echo "unexpected version: $version" exit 1 fi - echo "NEXT_VERSION=$next_version" >> $GITHUB_ENV + echo "NEXT_VERSION=${next_version}-SNAPSHOT" >> $GITHUB_ENV echo "VERSION=$version" >> $GITHUB_ENV - name: Update version - run: sed -Ei "s/[0-9]+\.[0-9]+\.[0-9]+/$NEXT_VERSION/" version.gradle.kts + run: .github/scripts/update-version.sh $NEXT_VERSION - name: Update the change log on main run: | From d698c1d326f24d9fb0dcc38443b38eaaa36205d4 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 15 May 2025 14:54:55 -0500 Subject: [PATCH 06/42] fix(deps): update dependency org.jetbrains.kotlin:kotlin-gradle-plugin to v2.1.21 (#7347) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- buildSrc/build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts index c08b70c190f..d1ff99c729e 100644 --- a/buildSrc/build.gradle.kts +++ b/buildSrc/build.gradle.kts @@ -64,7 +64,7 @@ dependencies { implementation("me.champeau.jmh:jmh-gradle-plugin:0.7.3") implementation("net.ltgt.gradle:gradle-errorprone-plugin:4.2.0") implementation("net.ltgt.gradle:gradle-nullaway-plugin:2.2.0") - implementation("org.jetbrains.kotlin:kotlin-gradle-plugin:2.1.20") + implementation("org.jetbrains.kotlin:kotlin-gradle-plugin:2.1.21") implementation("org.owasp:dependency-check-gradle:12.1.1") implementation("ru.vyarus:gradle-animalsniffer-plugin:2.0.1") } From 4c6ba0aed1e6d9d9a0e6d0067b7232b876c60bd9 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 15 May 2025 14:56:09 -0500 Subject: [PATCH 07/42] fix(deps): update dependency com.google.api.grpc:proto-google-common-protos to v2.57.0 (#7349) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- dependencyManagement/build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dependencyManagement/build.gradle.kts b/dependencyManagement/build.gradle.kts index a6fedbfc3e1..953659fd0b6 100644 --- a/dependencyManagement/build.gradle.kts +++ b/dependencyManagement/build.gradle.kts @@ -73,7 +73,7 @@ val DEPENDENCIES = listOf( "io.prometheus:simpleclient_httpserver:${prometheusClientVersion}", "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.56.0", + "com.google.api.grpc:proto-google-common-protos:2.57.0", "com.google.code.findbugs:jsr305:3.0.2", "com.google.guava:guava-beta-checker:1.0", "com.sun.net.httpserver:http:20070405", From 3c1f55683112b8e99a87fe8656432fcb09223271 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 15 May 2025 15:34:54 -0500 Subject: [PATCH 08/42] fix(deps): update prometheusserverversion to v1.3.7 (#7348) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Jack Berg --- dependencyManagement/build.gradle.kts | 2 +- .../exporter/prometheus/CollectorIntegrationTest.java | 7 ------- .../exporter/prometheus/PrometheusHttpServerTest.java | 4 ++-- 3 files changed, 3 insertions(+), 10 deletions(-) diff --git a/dependencyManagement/build.gradle.kts b/dependencyManagement/build.gradle.kts index 953659fd0b6..116f7f0fdd3 100644 --- a/dependencyManagement/build.gradle.kts +++ b/dependencyManagement/build.gradle.kts @@ -35,7 +35,7 @@ val mockitoVersion = "4.11.0" val slf4jVersion = "2.0.17" val opencensusVersion = "0.31.1" val prometheusClientVersion = "0.16.0" -val prometheusServerVersion = "1.3.6" +val prometheusServerVersion = "1.3.7" val armeriaVersion = "1.32.5" val junitVersion = "5.12.2" diff --git a/exporters/prometheus/src/test/java/io/opentelemetry/exporter/prometheus/CollectorIntegrationTest.java b/exporters/prometheus/src/test/java/io/opentelemetry/exporter/prometheus/CollectorIntegrationTest.java index f58fdf722a2..3e3ee1774df 100644 --- a/exporters/prometheus/src/test/java/io/opentelemetry/exporter/prometheus/CollectorIntegrationTest.java +++ b/exporters/prometheus/src/test/java/io/opentelemetry/exporter/prometheus/CollectorIntegrationTest.java @@ -134,13 +134,6 @@ void endToEnd() { // Resource attributes derived from the prometheus scrape config stringKeyValue("service.name", "app"), stringKeyValue("service.instance.id", "host.testcontainers.internal:" + prometheusPort), - // net.host.name, net.host.port and http.scheme are superseded by server.address, - // server.port, and url.scheme respectively and will be removed by default in a future - // collector release - // https://github.com/open-telemetry/opentelemetry-collector-contrib/pull/32829 - stringKeyValue("net.host.name", "host.testcontainers.internal"), - stringKeyValue("net.host.port", String.valueOf(prometheusPort)), - stringKeyValue("http.scheme", "http"), stringKeyValue("server.address", "host.testcontainers.internal"), stringKeyValue("server.port", String.valueOf(prometheusPort)), stringKeyValue("url.scheme", "http"), 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 e93d85d3f93..4faca5428cd 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 @@ -12,6 +12,7 @@ import com.google.common.collect.ImmutableList; import com.google.common.io.ByteStreams; +import com.google.protobuf.TextFormat; import com.linecorp.armeria.client.WebClient; import com.linecorp.armeria.client.retry.RetryRule; import com.linecorp.armeria.client.retry.RetryingClient; @@ -45,9 +46,8 @@ import io.opentelemetry.sdk.resources.Resource; import io.prometheus.metrics.exporter.httpserver.HTTPServer; import io.prometheus.metrics.exporter.httpserver.MetricsHandler; -import io.prometheus.metrics.expositionformats.generated.com_google_protobuf_4_29_3.Metrics; +import io.prometheus.metrics.expositionformats.generated.com_google_protobuf_4_30_2.Metrics; import io.prometheus.metrics.model.registry.PrometheusRegistry; -import io.prometheus.metrics.shaded.com_google_protobuf_4_29_3.TextFormat; import java.io.ByteArrayInputStream; import java.io.IOException; import java.net.ServerSocket; From 6bd13ffcbc6604bd0e217ccae2b0c16fb6ca90ef Mon Sep 17 00:00:00 2001 From: OpenTelemetry Bot <107717825+opentelemetrybot@users.noreply.github.com> Date: Thu, 15 May 2025 13:47:24 -0700 Subject: [PATCH 09/42] Update Renovate configuration (#7345) Co-authored-by: otelbot <197425009+otelbot@users.noreply.github.com> Co-authored-by: Jack Berg --- .github/renovate.json5 | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.github/renovate.json5 b/.github/renovate.json5 index 2056841ddb7..cc9dbd40a2b 100644 --- a/.github/renovate.json5 +++ b/.github/renovate.json5 @@ -1,9 +1,8 @@ { "$schema": "https://docs.renovatebot.com/renovate-schema.json", "extends": [ - "config:recommended", - "docker:pinDigests", - "helpers:pinGitHubActionDigests" + "config:best-practices", + "helpers:pinGitHubActionDigestsToSemver" ], "packageRules": [ { From deeceebe8dce49dc84a4d0cdb85f376fa4e510df Mon Sep 17 00:00:00 2001 From: Alex Brown <1159704+SoftlySplinter@users.noreply.github.com> Date: Thu, 15 May 2025 22:12:25 +0100 Subject: [PATCH 10/42] Run JDK HTTP sender on non-daemon threads. (#7322) Co-authored-by: Jack Berg --- .../sender/jdk/internal/JdkHttpSender.java | 28 +++++++++++++++---- 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/exporters/sender/jdk/src/main/java/io/opentelemetry/exporter/sender/jdk/internal/JdkHttpSender.java b/exporters/sender/jdk/src/main/java/io/opentelemetry/exporter/sender/jdk/internal/JdkHttpSender.java index 312149728af..87a23bf09e4 100644 --- a/exporters/sender/jdk/src/main/java/io/opentelemetry/exporter/sender/jdk/internal/JdkHttpSender.java +++ b/exporters/sender/jdk/src/main/java/io/opentelemetry/exporter/sender/jdk/internal/JdkHttpSender.java @@ -13,6 +13,7 @@ import io.opentelemetry.sdk.common.CompletableResultCode; import io.opentelemetry.sdk.common.export.ProxyOptions; import io.opentelemetry.sdk.common.export.RetryPolicy; +import io.opentelemetry.sdk.internal.DaemonThreadFactory; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.OutputStream; @@ -32,8 +33,9 @@ import java.util.concurrent.CompletableFuture; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; +import java.util.concurrent.SynchronousQueue; import java.util.concurrent.ThreadLocalRandom; +import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import java.util.function.Consumer; import java.util.function.Predicate; @@ -101,7 +103,7 @@ public final class JdkHttpSender implements HttpSender { .map(RetryPolicy::getRetryExceptionPredicate) .orElse(JdkHttpSender::isRetryableException); if (executorService == null) { - this.executorService = Executors.newFixedThreadPool(5); + this.executorService = newExecutor(); this.managedExecutor = true; } else { this.executorService = executorService; @@ -133,6 +135,16 @@ public final class JdkHttpSender implements HttpSender { executorService); } + private static ExecutorService newExecutor() { + return new ThreadPoolExecutor( + 0, + Integer.MAX_VALUE, + 60, + TimeUnit.SECONDS, + new SynchronousQueue<>(), + new DaemonThreadFactory("jdkhttp-executor")); + } + private static HttpClient configureClient( @Nullable SSLContext sslContext, long connectionTimeoutNanos, @@ -224,7 +236,8 @@ HttpResponse sendInternal(Marshaler marshaler) throws IOException { Thread.currentThread().interrupt(); break; // Break out and return response or throw } - // If after sleeping we've exceeded timeoutNanos, break out and return response or throw + // If after sleeping we've exceeded timeoutNanos, break out and return + // response or throw if ((System.nanoTime() - startTimeNanos) >= timeoutNanos) { break; } @@ -305,12 +318,15 @@ private HttpResponse sendRequest( } private static boolean isRetryableException(IOException throwable) { - // Almost all IOExceptions we've encountered are transient retryable, so we opt out of specific + // Almost all IOExceptions we've encountered are transient retryable, so we + // opt out of specific // IOExceptions that are unlikely to resolve rather than opting in. - // Known retryable IOException messages: "Connection reset", "/{remote ip}:{remote port} GOAWAY + // Known retryable IOException messages: "Connection reset", "/{remote + // ip}:{remote port} GOAWAY // received" // Known retryable HttpTimeoutException messages: "request timed out" - // Known retryable HttpConnectTimeoutException messages: "HTTP connect timed out" + // Known retryable HttpConnectTimeoutException messages: "HTTP connect timed + // out" return !(throwable instanceof SSLException); } From 452464820abe42a2c4f29269b79b767fc350fd5f Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 15 May 2025 16:41:52 -0500 Subject: [PATCH 11/42] fix(deps): update dependency com.google.protobuf:protobuf-bom to v4.31.0 (#7350) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- dependencyManagement/build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dependencyManagement/build.gradle.kts b/dependencyManagement/build.gradle.kts index 116f7f0fdd3..e01f4b3cbee 100644 --- a/dependencyManagement/build.gradle.kts +++ b/dependencyManagement/build.gradle.kts @@ -15,7 +15,7 @@ val DEPENDENCY_BOMS = listOf( "com.fasterxml.jackson:jackson-bom:2.19.0", "com.google.guava:guava-bom:33.4.8-jre", - "com.google.protobuf:protobuf-bom:4.30.2", + "com.google.protobuf:protobuf-bom:4.31.0", "com.squareup.okhttp3:okhttp-bom:4.12.0", "com.squareup.okio:okio-bom:3.11.0", // applies to transitive dependencies of okhttp "io.grpc:grpc-bom:1.72.0", From cc7d0855e7f04e529c794b965e247a86d052426c Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 15 May 2025 16:42:07 -0500 Subject: [PATCH 12/42] chore(deps): update weekly update (#7306) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/backport.yml | 2 +- .github/workflows/build-tracecontext-testsuite.yml | 2 +- .github/workflows/build.yml | 2 +- .github/workflows/codeql.yml | 4 ++-- .github/workflows/fossa.yml | 2 +- .github/workflows/ossf-scorecard.yml | 2 +- .github/workflows/prepare-patch-release.yml | 2 +- .github/workflows/prepare-release-branch.yml | 4 ++-- .github/workflows/release.yml | 2 +- .github/workflows/reusable-markdown-link-check.yml | 2 +- integration-tests/tracecontext/docker/Dockerfile | 4 ++-- 11 files changed, 14 insertions(+), 14 deletions(-) diff --git a/.github/workflows/backport.yml b/.github/workflows/backport.yml index c3fb6930ce4..9926527b8f3 100644 --- a/.github/workflows/backport.yml +++ b/.github/workflows/backport.yml @@ -29,7 +29,7 @@ jobs: - name: Use CLA approved github bot run: .github/scripts/use-cla-approved-github-bot.sh - - uses: actions/create-github-app-token@3ff1caaa28b64c9cc276ce0a02e2ff584f3900c5 # v2.0.2 + - uses: actions/create-github-app-token@df432ceedc7162793a195dd1713ff69aefc7379e # v2.0.6 id: otelbot-token with: app-id: ${{ vars.OTELBOT_APP_ID }} diff --git a/.github/workflows/build-tracecontext-testsuite.yml b/.github/workflows/build-tracecontext-testsuite.yml index 9b750533a81..0cffca0cfd6 100644 --- a/.github/workflows/build-tracecontext-testsuite.yml +++ b/.github/workflows/build-tracecontext-testsuite.yml @@ -29,7 +29,7 @@ jobs: password: ${{ secrets.GITHUB_TOKEN }} - name: Build and push - uses: docker/build-push-action@471d1dc4e07e5cdedd4c2171150001c434f0b7a4 # v6.15.0 + uses: docker/build-push-action@1dc73863535b631f98b2378be8619f83b136f4a0 # v6.17.0 with: context: integration-tests/tracecontext/docker push: true diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 1747e6a1b1d..66cf614c2de 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -99,7 +99,7 @@ jobs: exit 1 fi - - uses: codecov/codecov-action@ad3126e916f78f00edff4ed0317cf185271ccc2d # v5.4.2 + - uses: codecov/codecov-action@18283e04ce6e62d37312384ff67231eb8fd56d24 # v5.4.3 if: ${{ matrix.coverage }} env: CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 64bea75eeac..efa61755815 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -37,7 +37,7 @@ jobs: uses: gradle/actions/setup-gradle@06832c7b30a0129d7fb559bcc6e43d26f6374244 # v4.3.1 - name: Initialize CodeQL - uses: github/codeql-action/init@45775bd8235c68ba998cffa5171334d58593da47 # v3.28.15 + uses: github/codeql-action/init@60168efe1c415ce0f5521ea06d5c2062adbeed1b # v3.28.17 with: languages: java, actions # using "latest" helps to keep up with the latest Kotlin support @@ -51,4 +51,4 @@ jobs: run: ./gradlew assemble --no-build-cache --no-daemon - name: Perform CodeQL analysis - uses: github/codeql-action/analyze@45775bd8235c68ba998cffa5171334d58593da47 # v3.28.15 + uses: github/codeql-action/analyze@60168efe1c415ce0f5521ea06d5c2062adbeed1b # v3.28.17 diff --git a/.github/workflows/fossa.yml b/.github/workflows/fossa.yml index 03d4e568495..ddc524464ed 100644 --- a/.github/workflows/fossa.yml +++ b/.github/workflows/fossa.yml @@ -14,7 +14,7 @@ jobs: steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - - uses: fossas/fossa-action@c0a7d013f84c8ee5e910593186598625513cc1e4 # v1.6.0 + - uses: fossas/fossa-action@3ebcea1862c6ffbd5cf1b4d0bd6b3fe7bd6f2cac # v1.7.0 with: api-key: ${{secrets.FOSSA_API_KEY}} team: OpenTelemetry diff --git a/.github/workflows/ossf-scorecard.yml b/.github/workflows/ossf-scorecard.yml index 886c27e3e54..c969bba721a 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@45775bd8235c68ba998cffa5171334d58593da47 # v3.28.15 + uses: github/codeql-action/upload-sarif@60168efe1c415ce0f5521ea06d5c2062adbeed1b # v3.28.17 with: sarif_file: results.sarif diff --git a/.github/workflows/prepare-patch-release.yml b/.github/workflows/prepare-patch-release.yml index c828b654173..634164d3025 100644 --- a/.github/workflows/prepare-patch-release.yml +++ b/.github/workflows/prepare-patch-release.yml @@ -47,7 +47,7 @@ jobs: - name: Use CLA approved github bot run: .github/scripts/use-cla-approved-github-bot.sh - - uses: actions/create-github-app-token@3ff1caaa28b64c9cc276ce0a02e2ff584f3900c5 # v2.0.2 + - uses: actions/create-github-app-token@df432ceedc7162793a195dd1713ff69aefc7379e # v2.0.6 id: otelbot-token with: app-id: ${{ vars.OTELBOT_APP_ID }} diff --git a/.github/workflows/prepare-release-branch.yml b/.github/workflows/prepare-release-branch.yml index 74f17373417..e316f65a6d0 100644 --- a/.github/workflows/prepare-release-branch.yml +++ b/.github/workflows/prepare-release-branch.yml @@ -59,7 +59,7 @@ jobs: - name: Use CLA approved github bot run: .github/scripts/use-cla-approved-github-bot.sh - - uses: actions/create-github-app-token@3ff1caaa28b64c9cc276ce0a02e2ff584f3900c5 # v2.0.2 + - uses: actions/create-github-app-token@df432ceedc7162793a195dd1713ff69aefc7379e # v2.0.6 id: otelbot-token with: app-id: ${{ vars.OTELBOT_APP_ID }} @@ -115,7 +115,7 @@ jobs: - name: Use CLA approved github bot run: .github/scripts/use-cla-approved-github-bot.sh - - uses: actions/create-github-app-token@3ff1caaa28b64c9cc276ce0a02e2ff584f3900c5 # v2.0.2 + - uses: actions/create-github-app-token@df432ceedc7162793a195dd1713ff69aefc7379e # v2.0.6 id: otelbot-token with: app-id: ${{ vars.OTELBOT_APP_ID }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 8a100761237..50de2dc3194 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -190,7 +190,7 @@ jobs: - name: Use CLA approved bot run: .github/scripts/use-cla-approved-github-bot.sh - - uses: actions/create-github-app-token@3ff1caaa28b64c9cc276ce0a02e2ff584f3900c5 # v2.0.2 + - uses: actions/create-github-app-token@df432ceedc7162793a195dd1713ff69aefc7379e # v2.0.6 id: otelbot-token with: app-id: ${{ vars.OTELBOT_APP_ID }} diff --git a/.github/workflows/reusable-markdown-link-check.yml b/.github/workflows/reusable-markdown-link-check.yml index e547ef5f55e..36de638d8b9 100644 --- a/.github/workflows/reusable-markdown-link-check.yml +++ b/.github/workflows/reusable-markdown-link-check.yml @@ -12,7 +12,7 @@ jobs: steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - - uses: lycheeverse/lychee-action@1d97d84f0bc547f7b25f4c2170d87d810dc2fb2c # v2.4.0 + - uses: lycheeverse/lychee-action@82202e5e9c2f4ef1a55a3d02563e1cb6041e5332 # v2.4.1 with: # excluding links to pull requests and issues is done for performance args: > diff --git a/integration-tests/tracecontext/docker/Dockerfile b/integration-tests/tracecontext/docker/Dockerfile index cecfa66f329..96cdb43fac6 100644 --- a/integration-tests/tracecontext/docker/Dockerfile +++ b/integration-tests/tracecontext/docker/Dockerfile @@ -1,4 +1,4 @@ -FROM python:3.13.3@sha256:34dc8eb488136014caf530ec03a3a2403473a92d67a01a26256c365b5b2fc0d4 AS build +FROM python:3.13.3@sha256:653b0cf8fc50366277a21657209ddd54edd125499d20f3520c6b277eb8c828d3 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.3-slim@sha256:21e39cf1815802d4c6f89a0d3a166cc67ce58f95b6d1639e68a394c99310d2e5 +FROM python:3.13.3-slim@sha256:914bf5c12ea40a97a78b2bff97fbdb766cc36ec903bfb4358faf2b74d73b555b RUN pip install aiohttp From 04fc2124511c36945f5a239a419ffc97672899db Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 23 May 2025 09:51:34 -0700 Subject: [PATCH 13/42] fix(deps): update dependency checkstyle to v10.24.0 (#7361) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- buildSrc/src/main/kotlin/otel.java-conventions.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/buildSrc/src/main/kotlin/otel.java-conventions.gradle.kts b/buildSrc/src/main/kotlin/otel.java-conventions.gradle.kts index aa82f28f24f..2bf7d8ffb28 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.23.1" + toolVersion = "10.24.0" isIgnoreFailures = false configProperties["rootDir"] = rootDir } From 6153023d98e81e5aaa95153dff49de597fe699e2 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 23 May 2025 09:51:54 -0700 Subject: [PATCH 14/42] chore(deps): update dependency gradle to v8.14.1 (#7364) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/wrapper/gradle-wrapper.properties | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 247cf2a9f5c..9128c7d428d 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=61ad310d3c7d3e5da131b76bbf22b5a4c0786e9d892dae8c1658d4b484de3caa -distributionUrl=https\://services.gradle.org/distributions/gradle-8.14-bin.zip +distributionSha256Sum=845952a9d6afa783db70bb3b0effaae45ae5542ca2bb7929619e8af49cb634cf +distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.1-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME From 145ceaa94c031cda561e4c46e464fa4c32c9e1b4 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 27 May 2025 12:21:04 -0700 Subject: [PATCH 15/42] fix(deps): update dependency com.gradle.develocity:com.gradle.develocity.gradle.plugin to v4.0.2 (#7369) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- buildSrc/build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts index d1ff99c729e..0a1fc8b583f 100644 --- a/buildSrc/build.gradle.kts +++ b/buildSrc/build.gradle.kts @@ -54,7 +54,7 @@ dependencies { 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.3") - implementation("com.gradle.develocity:com.gradle.develocity.gradle.plugin:4.0.1") + 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") implementation("com.squareup.wire:wire-gradle-plugin") From 03e7f989657c5c2677d8f922020440193046fd88 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 27 May 2025 12:21:34 -0700 Subject: [PATCH 16/42] chore(deps): update plugin com.gradle.develocity to v4.0.2 (#7368) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- settings.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/settings.gradle.kts b/settings.gradle.kts index d6639a3846a..4752f7a31b6 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -1,7 +1,7 @@ pluginManagement { plugins { id("com.gradleup.shadow") version "8.3.6" - id("com.gradle.develocity") version "4.0.1" + id("com.gradle.develocity") version "4.0.2" id("de.undercouch.download") version "5.6.0" id("org.jsonschema2pojo") version "1.2.2" id("io.github.gradle-nexus.publish-plugin") version "2.0.0" From 90e030f213547a0116a6397455a046cf4019baf9 Mon Sep 17 00:00:00 2001 From: Robert Niedziela <175605712+robsunday@users.noreply.github.com> Date: Wed, 28 May 2025 00:34:26 +0200 Subject: [PATCH 17/42] Handle instrumentation node changes in yaml config file format 0.4 (#7357) --- .../incubator/config/InstrumentationConfigUtilTest.java | 8 ++++---- .../sdk/autoconfigure/DeclarativeConfigurationTest.java | 2 +- .../extension/incubator/fileconfig/SdkConfigProvider.java | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) 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 94da0f2d10b..d31001397ae 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 @@ -24,7 +24,7 @@ class InstrumentationConfigUtilTest { * href="/open-telemetry/opentelemetry-configuration/blob/main/examples/kitchen-sink.yaml">kitchen-sink.yaml. */ private static final String kitchenSinkInstrumentationConfig = - "instrumentation:\n" + "instrumentation/development:\n" + " general:\n" + " peer:\n" + " service_mapping:\n" @@ -54,11 +54,11 @@ class InstrumentationConfigUtilTest { private static final ConfigProvider kitchenSinkConfigProvider = toConfigProvider(kitchenSinkInstrumentationConfig); private static final ConfigProvider emptyInstrumentationConfigProvider = - toConfigProvider("instrumentation:\n"); + toConfigProvider("instrumentation/development:\n"); private static final ConfigProvider emptyGeneralConfigProvider = - toConfigProvider("instrumentation:\n general:\n"); + toConfigProvider("instrumentation/development:\n general:\n"); private static final ConfigProvider emptyHttpConfigProvider = - toConfigProvider("instrumentation:\n general:\n http:\n"); + toConfigProvider("instrumentation/development:\n general:\n http:\n"); private static ConfigProvider toConfigProvider(String configYaml) { OpenTelemetryConfigurationModel configuration = 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 2b323ffc800..345614ffc41 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 @@ -72,7 +72,7 @@ void setup() throws IOException { + " - simple:\n" + " exporter:\n" + " console: {}\n" - + "instrumentation:\n" + + "instrumentation/development:\n" + " general:\n" + " http:\n" + " client:\n" 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 8c8d549e007..d23c3799612 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 @@ -18,7 +18,7 @@ public final class SdkConfigProvider implements ConfigProvider { private SdkConfigProvider(OpenTelemetryConfigurationModel model) { DeclarativeConfigProperties configProperties = DeclarativeConfiguration.toConfigProperties(model); - this.instrumentationConfig = configProperties.getStructured("instrumentation"); + this.instrumentationConfig = configProperties.getStructured("instrumentation/development"); } /** From 53b1f7c624892457fe1193ab14f8c174383f8805 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 3 Jun 2025 14:06:19 -0500 Subject: [PATCH 18/42] fix(deps): update dependency io.grpc:grpc-bom to v1.73.0 (#7370) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- dependencyManagement/build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dependencyManagement/build.gradle.kts b/dependencyManagement/build.gradle.kts index e01f4b3cbee..0750b8ecdb5 100644 --- a/dependencyManagement/build.gradle.kts +++ b/dependencyManagement/build.gradle.kts @@ -18,7 +18,7 @@ val DEPENDENCY_BOMS = listOf( "com.google.protobuf:protobuf-bom:4.31.0", "com.squareup.okhttp3:okhttp-bom:4.12.0", "com.squareup.okio:okio-bom:3.11.0", // applies to transitive dependencies of okhttp - "io.grpc:grpc-bom:1.72.0", + "io.grpc:grpc-bom:1.73.0", "io.netty:netty-bom:4.2.1.Final", "io.zipkin.brave:brave-bom:6.2.0", "io.zipkin.reporter2:zipkin-reporter-bom:3.5.0", From afe3440e91c609b7d591fa2d8e299d0f90a20a54 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 3 Jun 2025 14:06:38 -0500 Subject: [PATCH 19/42] fix(deps): update dependency com.google.protobuf:protobuf-bom to v4.31.1 (#7375) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- dependencyManagement/build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dependencyManagement/build.gradle.kts b/dependencyManagement/build.gradle.kts index 0750b8ecdb5..bcb3c37299a 100644 --- a/dependencyManagement/build.gradle.kts +++ b/dependencyManagement/build.gradle.kts @@ -15,7 +15,7 @@ val DEPENDENCY_BOMS = listOf( "com.fasterxml.jackson:jackson-bom:2.19.0", "com.google.guava:guava-bom:33.4.8-jre", - "com.google.protobuf:protobuf-bom:4.31.0", + "com.google.protobuf:protobuf-bom:4.31.1", "com.squareup.okhttp3:okhttp-bom:4.12.0", "com.squareup.okio:okio-bom:3.11.0", // applies to transitive dependencies of okhttp "io.grpc:grpc-bom:1.73.0", From 5898a425c5653b921d68bf48ae32a30189e6879a Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 3 Jun 2025 14:06:55 -0500 Subject: [PATCH 20/42] fix(deps): update dependency io.zipkin.reporter2:zipkin-reporter-bom to v3.5.1 (#7379) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- dependencyManagement/build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dependencyManagement/build.gradle.kts b/dependencyManagement/build.gradle.kts index bcb3c37299a..74217173040 100644 --- a/dependencyManagement/build.gradle.kts +++ b/dependencyManagement/build.gradle.kts @@ -21,7 +21,7 @@ val DEPENDENCY_BOMS = listOf( "io.grpc:grpc-bom:1.73.0", "io.netty:netty-bom:4.2.1.Final", "io.zipkin.brave:brave-bom:6.2.0", - "io.zipkin.reporter2:zipkin-reporter-bom:3.5.0", + "io.zipkin.reporter2:zipkin-reporter-bom:3.5.1", "org.assertj:assertj-bom:3.27.3", "org.testcontainers:testcontainers-bom:1.21.0", "org.snakeyaml:snakeyaml-engine:2.9" From f94b056f834d4ecaf7372b30fc4d12bb2cb831ca Mon Sep 17 00:00:00 2001 From: Kevin Burke Date: Tue, 3 Jun 2025 12:08:41 -0700 Subject: [PATCH 21/42] sdk-extensions: remove 'Visible for test' comment (#7381) --- .../sdk/autoconfigure/LogRecordExporterConfiguration.java | 1 - 1 file changed, 1 deletion(-) diff --git a/sdk-extensions/autoconfigure/src/main/java/io/opentelemetry/sdk/autoconfigure/LogRecordExporterConfiguration.java b/sdk-extensions/autoconfigure/src/main/java/io/opentelemetry/sdk/autoconfigure/LogRecordExporterConfiguration.java index db0589ee183..b57b98d3c8b 100644 --- a/sdk-extensions/autoconfigure/src/main/java/io/opentelemetry/sdk/autoconfigure/LogRecordExporterConfiguration.java +++ b/sdk-extensions/autoconfigure/src/main/java/io/opentelemetry/sdk/autoconfigure/LogRecordExporterConfiguration.java @@ -35,7 +35,6 @@ final class LogRecordExporterConfiguration { EXPORTER_ARTIFACT_ID_BY_NAME.put("otlp", "opentelemetry-exporter-otlp"); } - // Visible for test static Map configureLogRecordExporters( ConfigProperties config, SpiHelper spiHelper, From 6982730ffb78e6a23e95fc987eab66946177945b Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 3 Jun 2025 14:08:57 -0500 Subject: [PATCH 22/42] fix(deps): update dependency org.testcontainers:testcontainers-bom to v1.21.1 (#7377) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- dependencyManagement/build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dependencyManagement/build.gradle.kts b/dependencyManagement/build.gradle.kts index 74217173040..2a0fd6547c8 100644 --- a/dependencyManagement/build.gradle.kts +++ b/dependencyManagement/build.gradle.kts @@ -23,7 +23,7 @@ val DEPENDENCY_BOMS = listOf( "io.zipkin.brave:brave-bom:6.2.0", "io.zipkin.reporter2:zipkin-reporter-bom:3.5.1", "org.assertj:assertj-bom:3.27.3", - "org.testcontainers:testcontainers-bom:1.21.0", + "org.testcontainers:testcontainers-bom:1.21.1", "org.snakeyaml:snakeyaml-engine:2.9" ) From ba37bf1a699a0f93f81bdaa5ac460466a657f555 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 3 Jun 2025 14:10:06 -0500 Subject: [PATCH 23/42] fix(deps): update spotless packages to v7.0.4 (#7371) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- buildSrc/build.gradle.kts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts index 0a1fc8b583f..7468ac8039f 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.3" + id("com.diffplug.spotless") version "7.0.4" } if (!hasLauncherForJavaVersion(17)) { @@ -53,7 +53,7 @@ dependencies { implementation(enforcedPlatform("com.squareup.wire:wire-bom:5.3.1")) 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.3") + implementation("com.diffplug.spotless:spotless-plugin-gradle:7.0.4") 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") From fd93c2b8c4fd98394881c429d38107abd4bd196c Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 3 Jun 2025 14:33:36 -0500 Subject: [PATCH 24/42] fix(deps): update dependency com.squareup.okio:okio-bom to v3.12.0 (#7372) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- dependencyManagement/build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dependencyManagement/build.gradle.kts b/dependencyManagement/build.gradle.kts index 2a0fd6547c8..629fc47a0f1 100644 --- a/dependencyManagement/build.gradle.kts +++ b/dependencyManagement/build.gradle.kts @@ -17,7 +17,7 @@ val DEPENDENCY_BOMS = listOf( "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.11.0", // applies to transitive dependencies of okhttp + "com.squareup.okio:okio-bom:3.12.0", // applies to transitive dependencies of okhttp "io.grpc:grpc-bom:1.73.0", "io.netty:netty-bom:4.2.1.Final", "io.zipkin.brave:brave-bom:6.2.0", From dfbea872b16a5cf76c38956728e70de914be3708 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 3 Jun 2025 14:33:51 -0500 Subject: [PATCH 25/42] fix(deps): update dependency com.squareup.wire:wire-bom to v5.3.2 (#7382) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- buildSrc/build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts index 7468ac8039f..a342477939f 100644 --- a/buildSrc/build.gradle.kts +++ b/buildSrc/build.gradle.kts @@ -50,7 +50,7 @@ repositories { } dependencies { - implementation(enforcedPlatform("com.squareup.wire:wire-bom:5.3.1")) + implementation(enforcedPlatform("com.squareup.wire:wire-bom:5.3.2")) 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") From 2c762e0be8c02821f27122f4eaaae0f92b9d9d55 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 3 Jun 2025 14:34:05 -0500 Subject: [PATCH 26/42] fix(deps): update dependency checkstyle to v10.25.0 (#7383) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- buildSrc/src/main/kotlin/otel.java-conventions.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/buildSrc/src/main/kotlin/otel.java-conventions.gradle.kts b/buildSrc/src/main/kotlin/otel.java-conventions.gradle.kts index 2bf7d8ffb28..6dc35c271cf 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.24.0" + toolVersion = "10.25.0" isIgnoreFailures = false configProperties["rootDir"] = rootDir } From f7207352a10c3dfe6e2aa6dd9e1600192b4c54b6 Mon Sep 17 00:00:00 2001 From: Jonas Kunz Date: Tue, 3 Jun 2025 23:40:38 +0200 Subject: [PATCH 27/42] Implement new SemConv exporter health metrics (#7265) Co-authored-by: jack-berg <34418638+jack-berg@users.noreply.github.com> Co-authored-by: Jack Berg --- .../opentelemetry-exporter-otlp.txt | 19 +- .../opentelemetry-exporter-zipkin.txt | 4 +- .../opentelemetry-sdk-common.txt | 11 +- exporters/common/build.gradle.kts | 1 + .../exporter/internal/ExporterMetrics.java | 138 ----------- .../exporter/internal/grpc/GrpcExporter.java | 44 ++-- .../internal/grpc/GrpcExporterBuilder.java | 35 ++- .../exporter/internal/http/HttpExporter.java | 43 ++-- .../internal/http/HttpExporterBuilder.java | 48 +++- .../metrics/ExporterInstrumentation.java | 149 ++++++++++++ .../internal/metrics/ExporterMetrics.java | 58 +++++ .../metrics/LegacyExporterMetrics.java | 198 ++++++++++++++++ .../internal/metrics/NoopExporterMetrics.java | 25 ++ .../metrics/SemConvExporterMetrics.java | 187 +++++++++++++++ .../internal/ExporterMetricsTest.java | 91 -------- .../grpc/GrpcExporterBuilderTest.java | 7 +- .../internal/grpc/GrpcExporterTest.java | 195 +++++++++++++++- .../internal/http/HttpExporterTest.java | 218 +++++++++++++++++- .../metrics/ExporterInstrumentationTest.java | 159 +++++++++++++ .../internal/grpc/GrpcExporterTest.java | 13 +- .../internal/http/HttpExporterTest.java | 23 +- .../otlp/trace/OltpExporterBenchmark.java | 20 +- .../OtlpHttpLogRecordExporterBuilder.java | 18 +- .../OtlpHttpMetricExporterBuilder.java | 16 +- .../trace/OtlpHttpSpanExporterBuilder.java | 18 +- .../OtlpGrpcLogRecordExporterBuilder.java | 16 +- .../OtlpGrpcMetricExporterBuilder.java | 16 +- .../trace/OtlpGrpcSpanExporterBuilder.java | 16 +- .../OtlpGrpcProfilesExporterBuilder.java | 4 +- .../AbstractGrpcTelemetryExporterTest.java | 132 ++++++++++- .../AbstractHttpTelemetryExporterTest.java | 127 +++++++++- .../GrpcLogRecordExporterBuilderWrapper.java | 16 ++ .../GrpcMetricExporterBuilderWrapper.java | 16 ++ .../GrpcProfilesExporterBuilderWrapper.java | 16 ++ .../GrpcSpanExporterBuilderWrapper.java | 16 ++ .../HttpLogRecordExporterBuilderWrapper.java | 16 ++ .../HttpMetricExporterBuilderWrapper.java | 16 ++ .../HttpSpanExporterBuilderWrapper.java | 16 ++ ...anagedChannelTelemetryExporterBuilder.java | 16 ++ .../internal/TelemetryExporterBuilder.java | 6 + .../exporter/zipkin/ZipkinSpanExporter.java | 33 ++- .../zipkin/ZipkinSpanExporterBuilder.java | 22 +- .../zipkin/ZipkinSpanExporterTest.java | 11 +- sdk/common/build.gradle.kts | 1 + .../sdk/common/InternalTelemetryVersion.java | 20 ++ .../sdk/internal/ComponentId.java | 68 ++++++ .../sdk/internal/SemConvAttributes.java | 36 +++ .../io/opentelemetry/sdk/internal/Signal.java | 39 ++++ .../sdk/internal/StandardComponentId.java | 62 +++++ .../sdk/internal/ComponentIdTest.java | 50 ++++ .../sdk/internal/SemConvAttributesTest.java | 35 +++ 51 files changed, 2222 insertions(+), 338 deletions(-) delete mode 100644 exporters/common/src/main/java/io/opentelemetry/exporter/internal/ExporterMetrics.java create mode 100644 exporters/common/src/main/java/io/opentelemetry/exporter/internal/metrics/ExporterInstrumentation.java create mode 100644 exporters/common/src/main/java/io/opentelemetry/exporter/internal/metrics/ExporterMetrics.java create mode 100644 exporters/common/src/main/java/io/opentelemetry/exporter/internal/metrics/LegacyExporterMetrics.java create mode 100644 exporters/common/src/main/java/io/opentelemetry/exporter/internal/metrics/NoopExporterMetrics.java create mode 100644 exporters/common/src/main/java/io/opentelemetry/exporter/internal/metrics/SemConvExporterMetrics.java delete mode 100644 exporters/common/src/test/java/io/opentelemetry/exporter/internal/ExporterMetricsTest.java create mode 100644 exporters/common/src/test/java/io/opentelemetry/exporter/internal/metrics/ExporterInstrumentationTest.java create mode 100644 sdk/common/src/main/java/io/opentelemetry/sdk/common/InternalTelemetryVersion.java create mode 100644 sdk/common/src/main/java/io/opentelemetry/sdk/internal/ComponentId.java create mode 100644 sdk/common/src/main/java/io/opentelemetry/sdk/internal/SemConvAttributes.java create mode 100644 sdk/common/src/main/java/io/opentelemetry/sdk/internal/Signal.java create mode 100644 sdk/common/src/main/java/io/opentelemetry/sdk/internal/StandardComponentId.java create mode 100644 sdk/common/src/test/java/io/opentelemetry/sdk/internal/ComponentIdTest.java create mode 100644 sdk/common/src/test/java/io/opentelemetry/sdk/internal/SemConvAttributesTest.java diff --git a/docs/apidiffs/current_vs_latest/opentelemetry-exporter-otlp.txt b/docs/apidiffs/current_vs_latest/opentelemetry-exporter-otlp.txt index 1f5072e4cf3..71020beb456 100644 --- a/docs/apidiffs/current_vs_latest/opentelemetry-exporter-otlp.txt +++ b/docs/apidiffs/current_vs_latest/opentelemetry-exporter-otlp.txt @@ -1,2 +1,19 @@ Comparing source compatibility of opentelemetry-exporter-otlp-1.51.0-SNAPSHOT.jar against opentelemetry-exporter-otlp-1.50.0.jar -No changes. \ No newline at end of file +*** 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 setInternalTelemetryVersion(io.opentelemetry.sdk.common.InternalTelemetryVersion) +*** 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 setInternalTelemetryVersion(io.opentelemetry.sdk.common.InternalTelemetryVersion) +*** 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 setInternalTelemetryVersion(io.opentelemetry.sdk.common.InternalTelemetryVersion) +*** 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 setInternalTelemetryVersion(io.opentelemetry.sdk.common.InternalTelemetryVersion) +*** 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 setInternalTelemetryVersion(io.opentelemetry.sdk.common.InternalTelemetryVersion) +*** 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 setInternalTelemetryVersion(io.opentelemetry.sdk.common.InternalTelemetryVersion) diff --git a/docs/apidiffs/current_vs_latest/opentelemetry-exporter-zipkin.txt b/docs/apidiffs/current_vs_latest/opentelemetry-exporter-zipkin.txt index 37fbce11347..7d6c8679df6 100644 --- a/docs/apidiffs/current_vs_latest/opentelemetry-exporter-zipkin.txt +++ b/docs/apidiffs/current_vs_latest/opentelemetry-exporter-zipkin.txt @@ -1,2 +1,4 @@ Comparing source compatibility of opentelemetry-exporter-zipkin-1.51.0-SNAPSHOT.jar against opentelemetry-exporter-zipkin-1.50.0.jar -No changes. \ No newline at end of file +*** MODIFIED CLASS: PUBLIC FINAL io.opentelemetry.exporter.zipkin.ZipkinSpanExporterBuilder (not serializable) + === CLASS FILE FORMAT VERSION: 52.0 <- 52.0 + +++ NEW METHOD: PUBLIC(+) io.opentelemetry.exporter.zipkin.ZipkinSpanExporterBuilder setInternalTelemetryVersion(io.opentelemetry.sdk.common.InternalTelemetryVersion) diff --git a/docs/apidiffs/current_vs_latest/opentelemetry-sdk-common.txt b/docs/apidiffs/current_vs_latest/opentelemetry-sdk-common.txt index ddc7f6a4328..a945cda018b 100644 --- a/docs/apidiffs/current_vs_latest/opentelemetry-sdk-common.txt +++ b/docs/apidiffs/current_vs_latest/opentelemetry-sdk-common.txt @@ -1,2 +1,11 @@ Comparing source compatibility of opentelemetry-sdk-common-1.51.0-SNAPSHOT.jar against opentelemetry-sdk-common-1.50.0.jar -No changes. \ No newline at end of file ++++ NEW ENUM: PUBLIC(+) FINAL(+) io.opentelemetry.sdk.common.InternalTelemetryVersion (compatible) + +++ CLASS FILE FORMAT VERSION: 52.0 <- n.a. + +++ NEW INTERFACE: java.lang.constant.Constable + +++ NEW INTERFACE: java.lang.Comparable + +++ NEW INTERFACE: java.io.Serializable + +++ NEW SUPERCLASS: java.lang.Enum + +++ NEW FIELD: PUBLIC(+) STATIC(+) FINAL(+) io.opentelemetry.sdk.common.InternalTelemetryVersion LEGACY + +++ NEW FIELD: PUBLIC(+) STATIC(+) FINAL(+) io.opentelemetry.sdk.common.InternalTelemetryVersion LATEST + +++ NEW METHOD: PUBLIC(+) STATIC(+) io.opentelemetry.sdk.common.InternalTelemetryVersion valueOf(java.lang.String) + +++ NEW METHOD: PUBLIC(+) STATIC(+) io.opentelemetry.sdk.common.InternalTelemetryVersion[] values() diff --git a/exporters/common/build.gradle.kts b/exporters/common/build.gradle.kts index 8d5f38224e7..a5a6275b6d8 100644 --- a/exporters/common/build.gradle.kts +++ b/exporters/common/build.gradle.kts @@ -29,6 +29,7 @@ dependencies { compileOnly("io.grpc:grpc-stub") testImplementation(project(":sdk:common")) + testImplementation(project(":sdk:testing")) testImplementation("com.google.protobuf:protobuf-java-util") testImplementation("com.linecorp.armeria:armeria-junit5") diff --git a/exporters/common/src/main/java/io/opentelemetry/exporter/internal/ExporterMetrics.java b/exporters/common/src/main/java/io/opentelemetry/exporter/internal/ExporterMetrics.java deleted file mode 100644 index 1075ac39e33..00000000000 --- a/exporters/common/src/main/java/io/opentelemetry/exporter/internal/ExporterMetrics.java +++ /dev/null @@ -1,138 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.exporter.internal; - -import static io.opentelemetry.api.common.AttributeKey.booleanKey; -import static io.opentelemetry.api.common.AttributeKey.stringKey; - -import io.opentelemetry.api.common.AttributeKey; -import io.opentelemetry.api.common.Attributes; -import io.opentelemetry.api.metrics.LongCounter; -import io.opentelemetry.api.metrics.Meter; -import io.opentelemetry.api.metrics.MeterProvider; -import java.util.function.Supplier; -import javax.annotation.Nullable; - -/** - * Helper for recording metrics from exporters. - * - *

This class is internal and is hence not for public use. Its APIs are unstable and can change - * at any time. - */ -public class ExporterMetrics { - - private static final AttributeKey ATTRIBUTE_KEY_TYPE = stringKey("type"); - private static final AttributeKey ATTRIBUTE_KEY_SUCCESS = booleanKey("success"); - - private final Supplier meterProviderSupplier; - private final String exporterName; - private final String transportName; - private final Attributes seenAttrs; - private final Attributes successAttrs; - private final Attributes failedAttrs; - - /** Access via {@link #seen()}. */ - @Nullable private volatile LongCounter seen; - - /** Access via {@link #exported()} . */ - @Nullable private volatile LongCounter exported; - - private ExporterMetrics( - Supplier meterProviderSupplier, - String exporterName, - String type, - String transportName) { - this.meterProviderSupplier = meterProviderSupplier; - this.exporterName = exporterName; - this.transportName = transportName; - this.seenAttrs = Attributes.builder().put(ATTRIBUTE_KEY_TYPE, type).build(); - this.successAttrs = this.seenAttrs.toBuilder().put(ATTRIBUTE_KEY_SUCCESS, true).build(); - this.failedAttrs = this.seenAttrs.toBuilder().put(ATTRIBUTE_KEY_SUCCESS, false).build(); - } - - /** Record number of records seen. */ - public void addSeen(long value) { - seen().add(value, seenAttrs); - } - - /** Record number of records which successfully exported. */ - public void addSuccess(long value) { - exported().add(value, successAttrs); - } - - /** Record number of records which failed to export. */ - public void addFailed(long value) { - exported().add(value, failedAttrs); - } - - private LongCounter seen() { - LongCounter seen = this.seen; - if (seen == null || isNoop(seen)) { - seen = meter().counterBuilder(exporterName + ".exporter.seen").build(); - this.seen = seen; - } - return seen; - } - - private LongCounter exported() { - LongCounter exported = this.exported; - if (exported == null || isNoop(exported)) { - exported = meter().counterBuilder(exporterName + ".exporter.exported").build(); - this.exported = exported; - } - return exported; - } - - private Meter meter() { - MeterProvider meterProvider = meterProviderSupplier.get(); - if (meterProvider == null) { - meterProvider = MeterProvider.noop(); - } - return meterProvider.get("io.opentelemetry.exporters." + exporterName + "-" + transportName); - } - - private static boolean isNoop(LongCounter counter) { - // This is a poor way to identify a Noop implementation, but the API doesn't provide a better - // way. Perhaps we could add a common "Noop" interface to allow for an instanceof check? - return counter.getClass().getSimpleName().startsWith("Noop"); - } - - /** - * Create an instance for recording exporter metrics under the meter {@code - * "io.opentelemetry.exporters." + exporterName + "-grpc}". - */ - public static ExporterMetrics createGrpc( - String exporterName, String type, Supplier meterProvider) { - return new ExporterMetrics(meterProvider, exporterName, type, "grpc"); - } - - /** - * Create an instance for recording exporter metrics under the meter {@code - * "io.opentelemetry.exporters." + exporterName + "-grpc-okhttp}". - */ - public static ExporterMetrics createGrpcOkHttp( - String exporterName, String type, Supplier meterProvider) { - return new ExporterMetrics(meterProvider, exporterName, type, "grpc-okhttp"); - } - - /** - * Create an instance for recording exporter metrics under the meter {@code - * "io.opentelemetry.exporters." + exporterName + "-http}". - */ - public static ExporterMetrics createHttpProtobuf( - String exporterName, String type, Supplier meterProvider) { - return new ExporterMetrics(meterProvider, exporterName, type, "http"); - } - - /** - * Create an instance for recording exporter metrics under the meter {@code - * "io.opentelemetry.exporters." + exporterName + "-http-json}". - */ - public static ExporterMetrics createHttpJson( - String exporterName, String type, Supplier meterProvider) { - return new ExporterMetrics(meterProvider, exporterName, type, "http-json"); - } -} diff --git a/exporters/common/src/main/java/io/opentelemetry/exporter/internal/grpc/GrpcExporter.java b/exporters/common/src/main/java/io/opentelemetry/exporter/internal/grpc/GrpcExporter.java index 128ddc436d5..3c8de2f541c 100644 --- a/exporters/common/src/main/java/io/opentelemetry/exporter/internal/grpc/GrpcExporter.java +++ b/exporters/common/src/main/java/io/opentelemetry/exporter/internal/grpc/GrpcExporter.java @@ -9,10 +9,12 @@ import static io.opentelemetry.exporter.internal.grpc.GrpcExporterUtil.GRPC_STATUS_UNIMPLEMENTED; import io.opentelemetry.api.metrics.MeterProvider; -import io.opentelemetry.exporter.internal.ExporterMetrics; import io.opentelemetry.exporter.internal.FailedExportException; import io.opentelemetry.exporter.internal.marshal.Marshaler; +import io.opentelemetry.exporter.internal.metrics.ExporterInstrumentation; import io.opentelemetry.sdk.common.CompletableResultCode; +import io.opentelemetry.sdk.common.InternalTelemetryVersion; +import io.opentelemetry.sdk.internal.StandardComponentId; import io.opentelemetry.sdk.internal.ThrottlingLogger; import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.Supplier; @@ -38,16 +40,19 @@ public final class GrpcExporter { private final String type; private final GrpcSender grpcSender; - private final ExporterMetrics exporterMetrics; + private final ExporterInstrumentation exporterMetrics; public GrpcExporter( - String exporterName, - String type, GrpcSender grpcSender, - Supplier meterProviderSupplier) { - this.type = type; + InternalTelemetryVersion internalTelemetryVersion, + StandardComponentId componentId, + Supplier meterProviderSupplier, + String endpoint) { + this.type = componentId.getStandardType().signal().logFriendlyName(); this.grpcSender = grpcSender; - this.exporterMetrics = ExporterMetrics.createGrpc(exporterName, type, meterProviderSupplier); + this.exporterMetrics = + new ExporterInstrumentation( + internalTelemetryVersion, meterProviderSupplier, componentId, endpoint); } public CompletableResultCode export(T exportRequest, int numItems) { @@ -55,28 +60,34 @@ public CompletableResultCode export(T exportRequest, int numItems) { return CompletableResultCode.ofFailure(); } - exporterMetrics.addSeen(numItems); + ExporterInstrumentation.Recording metricRecording = + exporterMetrics.startRecordingExport(numItems); CompletableResultCode result = new CompletableResultCode(); grpcSender.send( exportRequest, - grpcResponse -> onResponse(result, numItems, grpcResponse), - throwable -> onError(result, numItems, throwable)); + grpcResponse -> onResponse(result, metricRecording, grpcResponse), + throwable -> onError(result, metricRecording, throwable)); return result; } - private void onResponse(CompletableResultCode result, int numItems, GrpcResponse grpcResponse) { + private void onResponse( + CompletableResultCode result, + ExporterInstrumentation.Recording metricRecording, + GrpcResponse grpcResponse) { int statusCode = grpcResponse.grpcStatusValue(); + metricRecording.setGrpcStatusCode(statusCode); + if (statusCode == 0) { - exporterMetrics.addSuccess(numItems); + metricRecording.finishSuccessful(); result.succeed(); return; } - exporterMetrics.addFailed(numItems); + metricRecording.finishFailed(String.valueOf(statusCode)); switch (statusCode) { case GRPC_STATUS_UNIMPLEMENTED: if (loggedUnimplemented.compareAndSet(false, true)) { @@ -108,8 +119,11 @@ private void onResponse(CompletableResultCode result, int numItems, GrpcResponse result.failExceptionally(FailedExportException.grpcFailedWithResponse(grpcResponse)); } - private void onError(CompletableResultCode result, int numItems, Throwable e) { - exporterMetrics.addFailed(numItems); + private void onError( + CompletableResultCode result, + ExporterInstrumentation.Recording metricRecording, + Throwable e) { + metricRecording.finishFailed(e); logger.log( Level.SEVERE, "Failed to export " 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 d5cd41d1066..6f2ae64b862 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 @@ -14,7 +14,10 @@ import io.opentelemetry.exporter.internal.TlsConfigHelper; import io.opentelemetry.exporter.internal.compression.Compressor; import io.opentelemetry.exporter.internal.marshal.Marshaler; +import io.opentelemetry.sdk.common.InternalTelemetryVersion; import io.opentelemetry.sdk.common.export.RetryPolicy; +import io.opentelemetry.sdk.internal.ComponentId; +import io.opentelemetry.sdk.internal.StandardComponentId; import java.net.URI; import java.time.Duration; import java.util.ArrayList; @@ -48,8 +51,7 @@ public class GrpcExporterBuilder { private static final Logger LOGGER = Logger.getLogger(GrpcExporterBuilder.class.getName()); - private final String exporterName; - private final String type; + private final StandardComponentId.ExporterType exporterType; private final String grpcEndpointPath; private final Supplier>> grpcStubFactory; @@ -63,6 +65,8 @@ public class GrpcExporterBuilder { private TlsConfigHelper tlsConfigHelper = new TlsConfigHelper(); @Nullable private RetryPolicy retryPolicy = RetryPolicy.getDefault(); private Supplier meterProviderSupplier = GlobalOpenTelemetry::getMeterProvider; + private InternalTelemetryVersion internalTelemetryVersion = InternalTelemetryVersion.LEGACY; + private ClassLoader serviceClassLoader = GrpcExporterBuilder.class.getClassLoader(); @Nullable private ExecutorService executorService; @@ -70,14 +74,12 @@ public class GrpcExporterBuilder { @Nullable private Object grpcChannel; public GrpcExporterBuilder( - String exporterName, - String type, + StandardComponentId.ExporterType exporterType, long defaultTimeoutSecs, URI defaultEndpoint, Supplier>> grpcStubFactory, String grpcEndpointPath) { - this.exporterName = exporterName; - this.type = type; + this.exporterType = exporterType; this.grpcEndpointPath = grpcEndpointPath; timeoutNanos = TimeUnit.SECONDS.toNanos(defaultTimeoutSecs); endpoint = defaultEndpoint; @@ -150,6 +152,12 @@ public GrpcExporterBuilder setMeterProvider(Supplier meterProv return this; } + public GrpcExporterBuilder setInternalTelemetryVersion( + InternalTelemetryVersion internalTelemetryVersion) { + this.internalTelemetryVersion = internalTelemetryVersion; + return this; + } + public GrpcExporterBuilder setServiceClassLoader(ClassLoader servieClassLoader) { this.serviceClassLoader = servieClassLoader; return this; @@ -164,8 +172,7 @@ public GrpcExporterBuilder setExecutorService(ExecutorService executorService public GrpcExporterBuilder copy() { GrpcExporterBuilder copy = new GrpcExporterBuilder<>( - exporterName, - type, + exporterType, TimeUnit.NANOSECONDS.toSeconds(timeoutNanos), endpoint, grpcStubFactory, @@ -182,6 +189,7 @@ public GrpcExporterBuilder copy() { copy.retryPolicy = retryPolicy.toBuilder().build(); } copy.meterProviderSupplier = meterProviderSupplier; + copy.internalTelemetryVersion = internalTelemetryVersion; copy.grpcChannel = grpcChannel; return copy; } @@ -227,7 +235,12 @@ public GrpcExporter build() { executorService)); LOGGER.log(Level.FINE, "Using GrpcSender: " + grpcSender.getClass().getName()); - return new GrpcExporter<>(exporterName, type, grpcSender, meterProviderSupplier); + return new GrpcExporter<>( + grpcSender, + internalTelemetryVersion, + ComponentId.generateLazy(exporterType), + meterProviderSupplier, + endpoint.toString()); } public String toString(boolean includePrefixAndSuffix) { @@ -235,8 +248,6 @@ public String toString(boolean includePrefixAndSuffix) { includePrefixAndSuffix ? new StringJoiner(", ", "GrpcExporterBuilder{", "}") : new StringJoiner(", "); - joiner.add("exporterName=" + exporterName); - joiner.add("type=" + type); joiner.add("endpoint=" + endpoint.toString()); joiner.add("endpointPath=" + grpcEndpointPath); joiner.add("timeoutNanos=" + timeoutNanos); @@ -261,6 +272,8 @@ public String toString(boolean includePrefixAndSuffix) { if (executorService != null) { joiner.add("executorService=" + executorService); } + joiner.add("exporterType=" + exporterType.toString()); + joiner.add("internalTelemetrySchemaVersion=" + internalTelemetryVersion); // Note: omit tlsConfigHelper because we can't log the configuration in any readable way // Note: omit meterProviderSupplier because we can't log the configuration in any readable way return joiner.toString(); diff --git a/exporters/common/src/main/java/io/opentelemetry/exporter/internal/http/HttpExporter.java b/exporters/common/src/main/java/io/opentelemetry/exporter/internal/http/HttpExporter.java index 5ab37416b97..07533a95fea 100644 --- a/exporters/common/src/main/java/io/opentelemetry/exporter/internal/http/HttpExporter.java +++ b/exporters/common/src/main/java/io/opentelemetry/exporter/internal/http/HttpExporter.java @@ -6,11 +6,13 @@ package io.opentelemetry.exporter.internal.http; import io.opentelemetry.api.metrics.MeterProvider; -import io.opentelemetry.exporter.internal.ExporterMetrics; import io.opentelemetry.exporter.internal.FailedExportException; import io.opentelemetry.exporter.internal.grpc.GrpcExporterUtil; import io.opentelemetry.exporter.internal.marshal.Marshaler; +import io.opentelemetry.exporter.internal.metrics.ExporterInstrumentation; import io.opentelemetry.sdk.common.CompletableResultCode; +import io.opentelemetry.sdk.common.InternalTelemetryVersion; +import io.opentelemetry.sdk.internal.StandardComponentId; import io.opentelemetry.sdk.internal.ThrottlingLogger; import java.io.IOException; import java.util.concurrent.atomic.AtomicBoolean; @@ -35,20 +37,19 @@ public final class HttpExporter { private final String type; private final HttpSender httpSender; - private final ExporterMetrics exporterMetrics; + private final ExporterInstrumentation exporterMetrics; public HttpExporter( - String exporterName, - String type, + StandardComponentId componentId, HttpSender httpSender, Supplier meterProviderSupplier, - boolean exportAsJson) { - this.type = type; + InternalTelemetryVersion internalTelemetryVersion, + String endpoint) { + this.type = componentId.getStandardType().signal().logFriendlyName(); this.httpSender = httpSender; this.exporterMetrics = - exportAsJson - ? ExporterMetrics.createHttpJson(exporterName, type, meterProviderSupplier) - : ExporterMetrics.createHttpProtobuf(exporterName, type, meterProviderSupplier); + new ExporterInstrumentation( + internalTelemetryVersion, meterProviderSupplier, componentId, endpoint); } public CompletableResultCode export(T exportRequest, int numItems) { @@ -56,30 +57,35 @@ public CompletableResultCode export(T exportRequest, int numItems) { return CompletableResultCode.ofFailure(); } - exporterMetrics.addSeen(numItems); + ExporterInstrumentation.Recording metricRecording = + exporterMetrics.startRecordingExport(numItems); CompletableResultCode result = new CompletableResultCode(); httpSender.send( exportRequest, exportRequest.getBinarySerializedSize(), - httpResponse -> onResponse(result, numItems, httpResponse), - throwable -> onError(result, numItems, throwable)); + httpResponse -> onResponse(result, metricRecording, httpResponse), + throwable -> onError(result, metricRecording, throwable)); return result; } private void onResponse( - CompletableResultCode result, int numItems, HttpSender.Response httpResponse) { + CompletableResultCode result, + ExporterInstrumentation.Recording metricRecording, + HttpSender.Response httpResponse) { int statusCode = httpResponse.statusCode(); + metricRecording.setHttpStatusCode(statusCode); + if (statusCode >= 200 && statusCode < 300) { - exporterMetrics.addSuccess(numItems); + metricRecording.finishSuccessful(); result.succeed(); return; } - exporterMetrics.addFailed(numItems); + metricRecording.finishFailed(String.valueOf(statusCode)); byte[] body = null; try { @@ -102,8 +108,11 @@ private void onResponse( result.failExceptionally(FailedExportException.httpFailedWithResponse(httpResponse)); } - private void onError(CompletableResultCode result, int numItems, Throwable e) { - exporterMetrics.addFailed(numItems); + private void onError( + CompletableResultCode result, + ExporterInstrumentation.Recording metricRecording, + Throwable e) { + metricRecording.finishFailed(e); logger.log( Level.SEVERE, "Failed to export " 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 631dd0f9a9e..4cd671c32ca 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 @@ -12,8 +12,11 @@ import io.opentelemetry.exporter.internal.TlsConfigHelper; import io.opentelemetry.exporter.internal.compression.Compressor; import io.opentelemetry.exporter.internal.marshal.Marshaler; +import io.opentelemetry.sdk.common.InternalTelemetryVersion; import io.opentelemetry.sdk.common.export.ProxyOptions; import io.opentelemetry.sdk.common.export.RetryPolicy; +import io.opentelemetry.sdk.internal.ComponentId; +import io.opentelemetry.sdk.internal.StandardComponentId; import java.net.URI; import java.util.ArrayList; import java.util.Collections; @@ -45,8 +48,7 @@ public final class HttpExporterBuilder { private static final Logger LOGGER = Logger.getLogger(HttpExporterBuilder.class.getName()); - private final String exporterName; - private final String type; + private StandardComponentId.ExporterType exporterType; private String endpoint; @@ -61,12 +63,13 @@ public final class HttpExporterBuilder { private TlsConfigHelper tlsConfigHelper = new TlsConfigHelper(); @Nullable private RetryPolicy retryPolicy = RetryPolicy.getDefault(); private Supplier meterProviderSupplier = GlobalOpenTelemetry::getMeterProvider; + private InternalTelemetryVersion internalTelemetryVersion = InternalTelemetryVersion.LEGACY; private ClassLoader serviceClassLoader = HttpExporterBuilder.class.getClassLoader(); @Nullable private ExecutorService executorService; - public HttpExporterBuilder(String exporterName, String type, String defaultEndpoint) { - this.exporterName = exporterName; - this.type = type; + public HttpExporterBuilder( + StandardComponentId.ExporterType exporterType, String defaultEndpoint) { + this.exporterType = exporterType; endpoint = defaultEndpoint; } @@ -124,6 +127,12 @@ public HttpExporterBuilder setMeterProvider(Supplier meterProv return this; } + public HttpExporterBuilder setInternalTelemetryVersion( + InternalTelemetryVersion internalTelemetryVersion) { + this.internalTelemetryVersion = internalTelemetryVersion; + return this; + } + public HttpExporterBuilder setRetryPolicy(@Nullable RetryPolicy retryPolicy) { this.retryPolicy = retryPolicy; return this; @@ -146,12 +155,27 @@ public HttpExporterBuilder setExecutorService(ExecutorService executorService public HttpExporterBuilder exportAsJson() { this.exportAsJson = true; + exporterType = mapToJsonTypeIfPossible(exporterType); return this; } + private static StandardComponentId.ExporterType mapToJsonTypeIfPossible( + StandardComponentId.ExporterType componentType) { + switch (componentType) { + case OTLP_HTTP_SPAN_EXPORTER: + return StandardComponentId.ExporterType.OTLP_HTTP_JSON_SPAN_EXPORTER; + case OTLP_HTTP_LOG_EXPORTER: + return StandardComponentId.ExporterType.OTLP_HTTP_JSON_LOG_EXPORTER; + case OTLP_HTTP_METRIC_EXPORTER: + return StandardComponentId.ExporterType.OTLP_HTTP_JSON_METRIC_EXPORTER; + default: + return componentType; + } + } + @SuppressWarnings("BuilderReturnThis") public HttpExporterBuilder copy() { - HttpExporterBuilder copy = new HttpExporterBuilder<>(exporterName, type, endpoint); + HttpExporterBuilder copy = new HttpExporterBuilder<>(exporterType, endpoint); copy.endpoint = endpoint; copy.timeoutNanos = timeoutNanos; copy.connectTimeoutNanos = connectTimeoutNanos; @@ -164,6 +188,7 @@ public HttpExporterBuilder copy() { copy.retryPolicy = retryPolicy.toBuilder().build(); } copy.meterProviderSupplier = meterProviderSupplier; + copy.internalTelemetryVersion = internalTelemetryVersion; copy.proxyOptions = proxyOptions; return copy; } @@ -209,7 +234,12 @@ public HttpExporter build() { executorService)); LOGGER.log(Level.FINE, "Using HttpSender: " + httpSender.getClass().getName()); - return new HttpExporter<>(exporterName, type, httpSender, meterProviderSupplier, exportAsJson); + return new HttpExporter<>( + ComponentId.generateLazy(exporterType), + httpSender, + meterProviderSupplier, + internalTelemetryVersion, + endpoint); } public String toString(boolean includePrefixAndSuffix) { @@ -217,8 +247,6 @@ public String toString(boolean includePrefixAndSuffix) { includePrefixAndSuffix ? new StringJoiner(", ", "HttpExporterBuilder{", "}") : new StringJoiner(", "); - joiner.add("exporterName=" + exporterName); - joiner.add("type=" + type); joiner.add("endpoint=" + endpoint); joiner.add("timeoutNanos=" + timeoutNanos); joiner.add("proxyOptions=" + proxyOptions); @@ -241,6 +269,8 @@ public String toString(boolean includePrefixAndSuffix) { if (executorService != null) { joiner.add("executorService=" + executorService); } + joiner.add("exporterType=" + exporterType); + joiner.add("internalTelemetrySchemaVersion=" + internalTelemetryVersion); // Note: omit tlsConfigHelper because we can't log the configuration in any readable way // Note: omit meterProviderSupplier because we can't log the configuration in any readable way return joiner.toString(); diff --git a/exporters/common/src/main/java/io/opentelemetry/exporter/internal/metrics/ExporterInstrumentation.java b/exporters/common/src/main/java/io/opentelemetry/exporter/internal/metrics/ExporterInstrumentation.java new file mode 100644 index 00000000000..a6ae53481a9 --- /dev/null +++ b/exporters/common/src/main/java/io/opentelemetry/exporter/internal/metrics/ExporterInstrumentation.java @@ -0,0 +1,149 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.exporter.internal.metrics; + +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.common.AttributesBuilder; +import io.opentelemetry.api.metrics.MeterProvider; +import io.opentelemetry.sdk.common.InternalTelemetryVersion; +import io.opentelemetry.sdk.internal.SemConvAttributes; +import io.opentelemetry.sdk.internal.Signal; +import io.opentelemetry.sdk.internal.StandardComponentId; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.function.Supplier; +import javax.annotation.Nullable; + +/** + * This class is internal and is hence not for public use. Its APIs are unstable and can change at + * any time. + */ +public class ExporterInstrumentation { + + private final ExporterMetrics implementation; + + public ExporterInstrumentation( + InternalTelemetryVersion schema, + Supplier meterProviderSupplier, + StandardComponentId componentId, + String endpoint) { + + Signal signal = componentId.getStandardType().signal(); + switch (schema) { + case LEGACY: + implementation = + LegacyExporterMetrics.isSupportedType(componentId.getStandardType()) + ? new LegacyExporterMetrics(meterProviderSupplier, componentId.getStandardType()) + : NoopExporterMetrics.INSTANCE; + break; + case LATEST: + implementation = + signal == Signal.PROFILE + ? NoopExporterMetrics.INSTANCE + : new SemConvExporterMetrics( + meterProviderSupplier, signal, componentId, extractServerAttributes(endpoint)); + break; + default: + throw new IllegalStateException("Unhandled case: " + schema); + } + } + + // visible for testing + static Attributes extractServerAttributes(String httpEndpoint) { + try { + URI parsed = new URI(httpEndpoint); + AttributesBuilder builder = Attributes.builder(); + String host = parsed.getHost(); + if (host != null) { + builder.put(SemConvAttributes.SERVER_ADDRESS, host); + } + int port = parsed.getPort(); + if (port == -1) { + String scheme = parsed.getScheme(); + if ("https".equals(scheme)) { + port = 443; + } else if ("http".equals(scheme)) { + port = 80; + } + } + if (port != -1) { + builder.put(SemConvAttributes.SERVER_PORT, port); + } + return builder.build(); + } catch (URISyntaxException e) { + return Attributes.empty(); + } + } + + public Recording startRecordingExport(int itemCount) { + return new Recording(implementation.startRecordingExport(itemCount)); + } + + /** + * This class is internal and is hence not for public use. Its APIs are unstable and can change at + * any time. + */ + public static class Recording { + + private final ExporterMetrics.Recording delegate; + @Nullable private Long httpStatusCode; + @Nullable private Long grpcStatusCode; + + private Recording(ExporterMetrics.Recording delegate) { + this.delegate = delegate; + } + + public void setHttpStatusCode(long httpStatusCode) { + if (grpcStatusCode != null) { + throw new IllegalStateException( + "gRPC status code already set, can only set either gRPC or HTTP"); + } + this.httpStatusCode = httpStatusCode; + } + + public void setGrpcStatusCode(long grpcStatusCode) { + if (httpStatusCode != null) { + throw new IllegalStateException( + "HTTP status code already set, can only set either gRPC or HTTP"); + } + this.grpcStatusCode = grpcStatusCode; + } + + /** Callback to notify that the export was successful. */ + public void finishSuccessful() { + delegate.finishSuccessful(buildRequestAttributes()); + } + + /** + * Callback to notify that the export has failed with the given {@link Throwable} as failure + * cause. + * + * @param failureCause the cause of the failure + */ + public void finishFailed(Throwable failureCause) { + finishFailed(failureCause.getClass().getName()); + } + + /** + * Callback to notify that the export has failed. + * + * @param errorType a failure reason suitable for the error.type attribute + */ + public void finishFailed(String errorType) { + delegate.finishFailed(errorType, buildRequestAttributes()); + } + + private Attributes buildRequestAttributes() { + if (httpStatusCode != null) { + return Attributes.of(SemConvAttributes.HTTP_RESPONSE_STATUS_CODE, httpStatusCode); + } + if (grpcStatusCode != null) { + return Attributes.of(SemConvAttributes.RPC_GRPC_STATUS_CODE, grpcStatusCode); + } + return Attributes.empty(); + } + } +} diff --git a/exporters/common/src/main/java/io/opentelemetry/exporter/internal/metrics/ExporterMetrics.java b/exporters/common/src/main/java/io/opentelemetry/exporter/internal/metrics/ExporterMetrics.java new file mode 100644 index 00000000000..ca7d29187df --- /dev/null +++ b/exporters/common/src/main/java/io/opentelemetry/exporter/internal/metrics/ExporterMetrics.java @@ -0,0 +1,58 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.exporter.internal.metrics; + +import io.opentelemetry.api.common.Attributes; +import javax.annotation.Nullable; + +/** + * This class is internal and is hence not for public use. Its APIs are unstable and can change at + * any time. + */ +public interface ExporterMetrics { + + Recording startRecordingExport(int itemCount); + + /** + * This class is internal and is hence not for public use. Its APIs are unstable and can change at + * any time. + */ + abstract class Recording { + + private boolean alreadyEnded = false; + + protected Recording() {} + + public final void finishSuccessful(Attributes requestAttributes) { + ensureEndedOnce(); + doFinish(null, requestAttributes); + } + + public final void finishFailed(String errorType, Attributes requestAttributes) { + ensureEndedOnce(); + if (errorType == null || errorType.isEmpty()) { + throw new IllegalArgumentException("The export failed but no failure reason was provided"); + } + doFinish(errorType, requestAttributes); + } + + private void ensureEndedOnce() { + if (alreadyEnded) { + throw new IllegalStateException("Recording already ended"); + } + alreadyEnded = true; + } + + /** + * Invoked when the export has finished, either successfully or failed. + * + * @param errorType null if the export was successful, otherwise a failure reason suitable for + * the error.type attribute + * @param requestAttributes additional attributes to add to request metrics + */ + protected abstract void doFinish(@Nullable String errorType, Attributes requestAttributes); + } +} diff --git a/exporters/common/src/main/java/io/opentelemetry/exporter/internal/metrics/LegacyExporterMetrics.java b/exporters/common/src/main/java/io/opentelemetry/exporter/internal/metrics/LegacyExporterMetrics.java new file mode 100644 index 00000000000..8db64eab47d --- /dev/null +++ b/exporters/common/src/main/java/io/opentelemetry/exporter/internal/metrics/LegacyExporterMetrics.java @@ -0,0 +1,198 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.exporter.internal.metrics; + +import static io.opentelemetry.api.common.AttributeKey.booleanKey; +import static io.opentelemetry.api.common.AttributeKey.stringKey; + +import io.opentelemetry.api.common.AttributeKey; +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.metrics.LongCounter; +import io.opentelemetry.api.metrics.Meter; +import io.opentelemetry.api.metrics.MeterProvider; +import io.opentelemetry.sdk.internal.Signal; +import io.opentelemetry.sdk.internal.StandardComponentId; +import java.util.function.Supplier; +import javax.annotation.Nullable; + +/** + * Implements health metrics for exporters which were defined prior to the standardization in + * semantic conventions. + * + *

This class is internal and is hence not for public use. Its APIs are unstable and can change + * at any time. + */ +public class LegacyExporterMetrics implements ExporterMetrics { + + private static final AttributeKey ATTRIBUTE_KEY_TYPE = stringKey("type"); + private static final AttributeKey ATTRIBUTE_KEY_SUCCESS = booleanKey("success"); + + private final Supplier meterProviderSupplier; + private final String exporterName; + private final String transportName; + private final Attributes seenAttrs; + private final Attributes successAttrs; + private final Attributes failedAttrs; + + /** Access via {@link #seen()}. */ + @Nullable private volatile LongCounter seen; + + /** Access via {@link #exported()} . */ + @Nullable private volatile LongCounter exported; + + LegacyExporterMetrics( + Supplier meterProviderSupplier, + StandardComponentId.ExporterType exporterType) { + this.meterProviderSupplier = meterProviderSupplier; + this.exporterName = getExporterName(exporterType); + this.transportName = getTransportName(exporterType); + this.seenAttrs = + Attributes.builder().put(ATTRIBUTE_KEY_TYPE, getTypeString(exporterType.signal())).build(); + this.successAttrs = this.seenAttrs.toBuilder().put(ATTRIBUTE_KEY_SUCCESS, true).build(); + this.failedAttrs = this.seenAttrs.toBuilder().put(ATTRIBUTE_KEY_SUCCESS, false).build(); + } + + public static boolean isSupportedType(StandardComponentId.ExporterType exporterType) { + switch (exporterType) { + case OTLP_GRPC_SPAN_EXPORTER: + case OTLP_HTTP_SPAN_EXPORTER: + case OTLP_HTTP_JSON_SPAN_EXPORTER: + case ZIPKIN_HTTP_SPAN_EXPORTER: + case ZIPKIN_HTTP_JSON_SPAN_EXPORTER: + case OTLP_GRPC_LOG_EXPORTER: + case OTLP_HTTP_LOG_EXPORTER: + case OTLP_HTTP_JSON_LOG_EXPORTER: + case OTLP_GRPC_METRIC_EXPORTER: + case OTLP_HTTP_METRIC_EXPORTER: + case OTLP_HTTP_JSON_METRIC_EXPORTER: + return true; + default: + return false; + } + } + + private static String getTypeString(Signal signal) { + switch (signal) { + case SPAN: + return "span"; + case LOG: + return "log"; + case METRIC: + return "metric"; + case PROFILE: + throw new IllegalArgumentException("Profiles are not supported"); + } + throw new IllegalArgumentException("Unhandled signal type: " + signal); + } + + private static String getExporterName(StandardComponentId.ExporterType exporterType) { + switch (exporterType) { + case OTLP_GRPC_SPAN_EXPORTER: + case OTLP_HTTP_SPAN_EXPORTER: + case OTLP_HTTP_JSON_SPAN_EXPORTER: + case OTLP_GRPC_LOG_EXPORTER: + case OTLP_HTTP_LOG_EXPORTER: + case OTLP_HTTP_JSON_LOG_EXPORTER: + case OTLP_GRPC_METRIC_EXPORTER: + case OTLP_HTTP_METRIC_EXPORTER: + case OTLP_HTTP_JSON_METRIC_EXPORTER: + return "otlp"; + case ZIPKIN_HTTP_SPAN_EXPORTER: + case ZIPKIN_HTTP_JSON_SPAN_EXPORTER: + return "zipkin"; + case OTLP_GRPC_PROFILES_EXPORTER: + throw new IllegalArgumentException("Profiles are not supported"); + } + throw new IllegalArgumentException("Not a supported exporter type: " + exporterType); + } + + private static String getTransportName(StandardComponentId.ExporterType exporterType) { + switch (exporterType) { + case OTLP_GRPC_SPAN_EXPORTER: + case OTLP_GRPC_LOG_EXPORTER: + case OTLP_GRPC_METRIC_EXPORTER: + return "grpc"; + case OTLP_HTTP_SPAN_EXPORTER: + case OTLP_HTTP_LOG_EXPORTER: + case OTLP_HTTP_METRIC_EXPORTER: + case ZIPKIN_HTTP_SPAN_EXPORTER: + return "http"; + case OTLP_HTTP_JSON_SPAN_EXPORTER: + case OTLP_HTTP_JSON_LOG_EXPORTER: + case OTLP_HTTP_JSON_METRIC_EXPORTER: + case ZIPKIN_HTTP_JSON_SPAN_EXPORTER: + return "http-json"; + case OTLP_GRPC_PROFILES_EXPORTER: + throw new IllegalArgumentException("Profiles are not supported"); + } + throw new IllegalArgumentException("Not a supported exporter type: " + exporterType); + } + + /** Record number of records seen. */ + private void addSeen(long value) { + seen().add(value, seenAttrs); + } + + /** Record number of records which successfully exported. */ + private void addSuccess(long value) { + exported().add(value, successAttrs); + } + + /** Record number of records which failed to export. */ + private void addFailed(long value) { + exported().add(value, failedAttrs); + } + + private LongCounter seen() { + LongCounter seen = this.seen; + if (seen == null || SemConvExporterMetrics.isNoop(seen)) { + seen = meter().counterBuilder(exporterName + ".exporter.seen").build(); + this.seen = seen; + } + return seen; + } + + private LongCounter exported() { + LongCounter exported = this.exported; + if (exported == null || SemConvExporterMetrics.isNoop(exported)) { + exported = meter().counterBuilder(exporterName + ".exporter.exported").build(); + this.exported = exported; + } + return exported; + } + + private Meter meter() { + MeterProvider meterProvider = meterProviderSupplier.get(); + if (meterProvider == null) { + meterProvider = MeterProvider.noop(); + } + return meterProvider.get("io.opentelemetry.exporters." + exporterName + "-" + transportName); + } + + @Override + public ExporterMetrics.Recording startRecordingExport(int itemCount) { + return new Recording(itemCount); + } + + private class Recording extends ExporterMetrics.Recording { + + private final int itemCount; + + private Recording(int itemCount) { + this.itemCount = itemCount; + addSeen(itemCount); + } + + @Override + protected void doFinish(@Nullable String errorType, Attributes requestAttributes) { + if (errorType != null) { + addFailed(itemCount); + } else { + addSuccess(itemCount); + } + } + } +} diff --git a/exporters/common/src/main/java/io/opentelemetry/exporter/internal/metrics/NoopExporterMetrics.java b/exporters/common/src/main/java/io/opentelemetry/exporter/internal/metrics/NoopExporterMetrics.java new file mode 100644 index 00000000000..9190dddb20e --- /dev/null +++ b/exporters/common/src/main/java/io/opentelemetry/exporter/internal/metrics/NoopExporterMetrics.java @@ -0,0 +1,25 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.exporter.internal.metrics; + +import io.opentelemetry.api.common.Attributes; +import javax.annotation.Nullable; + +class NoopExporterMetrics implements ExporterMetrics { + + static final NoopExporterMetrics INSTANCE = new NoopExporterMetrics(); + + @Override + public Recording startRecordingExport(int itemCount) { + return new NoopRecording(); + } + + private static class NoopRecording extends Recording { + + @Override + protected void doFinish(@Nullable String errorType, Attributes requestAttributes) {} + } +} diff --git a/exporters/common/src/main/java/io/opentelemetry/exporter/internal/metrics/SemConvExporterMetrics.java b/exporters/common/src/main/java/io/opentelemetry/exporter/internal/metrics/SemConvExporterMetrics.java new file mode 100644 index 00000000000..0d815e95e7e --- /dev/null +++ b/exporters/common/src/main/java/io/opentelemetry/exporter/internal/metrics/SemConvExporterMetrics.java @@ -0,0 +1,187 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.exporter.internal.metrics; + +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.common.AttributesBuilder; +import io.opentelemetry.api.metrics.DoubleHistogram; +import io.opentelemetry.api.metrics.LongCounter; +import io.opentelemetry.api.metrics.LongUpDownCounter; +import io.opentelemetry.api.metrics.Meter; +import io.opentelemetry.api.metrics.MeterProvider; +import io.opentelemetry.sdk.common.Clock; +import io.opentelemetry.sdk.internal.ComponentId; +import io.opentelemetry.sdk.internal.SemConvAttributes; +import io.opentelemetry.sdk.internal.Signal; +import java.util.Collections; +import java.util.function.Supplier; +import javax.annotation.Nullable; + +/** + * This class is internal and is hence not for public use. Its APIs are unstable and can change at + * any time. + */ +public class SemConvExporterMetrics implements ExporterMetrics { + + private static final Clock CLOCK = Clock.getDefault(); + + private final Supplier meterProviderSupplier; + private final Signal signal; + private final ComponentId componentId; + private final Attributes additionalAttributes; + + @Nullable private volatile LongUpDownCounter inflight = null; + @Nullable private volatile LongCounter exported = null; + @Nullable private volatile DoubleHistogram duration = null; + @Nullable private volatile Attributes allAttributes = null; + + public SemConvExporterMetrics( + Supplier meterProviderSupplier, + Signal signal, + ComponentId componentId, + Attributes additionalAttributes) { + this.meterProviderSupplier = meterProviderSupplier; + this.componentId = componentId; + this.signal = signal; + this.additionalAttributes = additionalAttributes; + } + + @Override + public ExporterMetrics.Recording startRecordingExport(int itemCount) { + return new Recording(itemCount); + } + + private Meter meter() { + MeterProvider meterProvider = meterProviderSupplier.get(); + if (meterProvider == null) { + meterProvider = MeterProvider.noop(); + } + return meterProvider.get("io.opentelemetry.exporters." + componentId.getTypeName()); + } + + private Attributes allAttributes() { + // attributes are initialized lazily to trigger lazy initialization of the componentId + Attributes allAttributes = this.allAttributes; + if (allAttributes == null) { + AttributesBuilder builder = Attributes.builder(); + builder.put(SemConvAttributes.OTEL_COMPONENT_TYPE, componentId.getTypeName()); + builder.put(SemConvAttributes.OTEL_COMPONENT_NAME, componentId.getComponentName()); + builder.putAll(additionalAttributes); + allAttributes = builder.build(); + this.allAttributes = allAttributes; + } + return allAttributes; + } + + private LongUpDownCounter inflight() { + LongUpDownCounter inflight = this.inflight; + if (inflight == null || isNoop(inflight)) { + String unit = signal.getMetricUnit(); + inflight = + meter() + .upDownCounterBuilder(signal.getExporterMetricNamespace() + ".inflight") + .setUnit("{" + unit + "}") + .setDescription( + "The number of " + + unit + + "s which were passed to the exporter, but that have not been exported yet (neither successful, nor failed)") + .build(); + this.inflight = inflight; + } + return inflight; + } + + private LongCounter exported() { + LongCounter exported = this.exported; + if (exported == null || isNoop(exported)) { + String unit = signal.getMetricUnit(); + exported = + meter() + .counterBuilder(signal.getExporterMetricNamespace() + ".exported") + .setUnit("{" + unit + "}") + .setDescription( + "The number of " + + unit + + "s for which the export has finished, either successful or failed") + .build(); + this.exported = exported; + } + return exported; + } + + private DoubleHistogram duration() { + DoubleHistogram duration = this.duration; + if (duration == null || isNoop(duration)) { + duration = + meter() + .histogramBuilder("otel.sdk.exporter.operation.duration") + .setUnit("s") + .setDescription("The duration of exporting a batch of telemetry records") + .setExplicitBucketBoundariesAdvice(Collections.emptyList()) + .build(); + this.duration = duration; + } + return duration; + } + + private void incrementInflight(long count) { + inflight().add(count, allAttributes()); + } + + private void decrementInflight(long count) { + inflight().add(-count, allAttributes()); + } + + private void incrementExported(long count, @Nullable String errorType) { + exported().add(count, getAttributesWithPotentialError(errorType, Attributes.empty())); + } + + static boolean isNoop(Object instrument) { + // This is a poor way to identify a Noop implementation, but the API doesn't provide a better + // way. Perhaps we could add a common "Noop" interface to allow for an instanceof check? + return instrument.getClass().getSimpleName().startsWith("Noop"); + } + + private Attributes getAttributesWithPotentialError( + @Nullable String errorType, Attributes additionalAttributes) { + Attributes attributes = allAttributes(); + boolean errorPresent = errorType != null && !errorType.isEmpty(); + if (errorPresent || !additionalAttributes.isEmpty()) { + AttributesBuilder builder = attributes.toBuilder(); + if (errorPresent) { + builder.put(SemConvAttributes.ERROR_TYPE, errorType); + } + attributes = builder.putAll(additionalAttributes).build(); + } + return attributes; + } + + private void recordDuration( + double seconds, @Nullable String errorType, Attributes requestAttributes) { + duration().record(seconds, getAttributesWithPotentialError(errorType, requestAttributes)); + } + + private class Recording extends ExporterMetrics.Recording { + + private final int itemCount; + + private final long startNanoTime; + + private Recording(int itemCount) { + this.itemCount = itemCount; + startNanoTime = CLOCK.nanoTime(); + incrementInflight(itemCount); + } + + @Override + protected void doFinish(@Nullable String errorType, Attributes requestAttributes) { + decrementInflight(itemCount); + incrementExported(itemCount, errorType); + long durationNanos = CLOCK.nanoTime() - startNanoTime; + recordDuration(durationNanos / 1_000_000_000.0, errorType, requestAttributes); + } + } +} diff --git a/exporters/common/src/test/java/io/opentelemetry/exporter/internal/ExporterMetricsTest.java b/exporters/common/src/test/java/io/opentelemetry/exporter/internal/ExporterMetricsTest.java deleted file mode 100644 index 30651ed8831..00000000000 --- a/exporters/common/src/test/java/io/opentelemetry/exporter/internal/ExporterMetricsTest.java +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.exporter.internal; - -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoInteractions; -import static org.mockito.Mockito.when; - -import io.opentelemetry.api.metrics.MeterProvider; -import io.opentelemetry.sdk.common.CompletableResultCode; -import io.opentelemetry.sdk.metrics.InstrumentType; -import io.opentelemetry.sdk.metrics.SdkMeterProvider; -import io.opentelemetry.sdk.metrics.data.AggregationTemporality; -import io.opentelemetry.sdk.metrics.export.CollectionRegistration; -import io.opentelemetry.sdk.metrics.export.MetricReader; -import java.util.function.Supplier; -import org.junit.jupiter.api.Test; - -class ExporterMetricsTest { - - @SuppressWarnings("unchecked") - Supplier meterProviderSupplier = mock(Supplier.class); - - @Test - void createHttpProtobuf_validMeterProvider() { - when(meterProviderSupplier.get()) - .thenReturn( - SdkMeterProvider.builder() - // Have to provide a valid reader. - .registerMetricReader( - new MetricReader() { - @Override - public void register(CollectionRegistration registration) {} - - @Override - public CompletableResultCode forceFlush() { - return CompletableResultCode.ofSuccess(); - } - - @Override - public CompletableResultCode shutdown() { - return CompletableResultCode.ofSuccess(); - } - - @Override - public AggregationTemporality getAggregationTemporality( - InstrumentType instrumentType) { - return AggregationTemporality.CUMULATIVE; - } - }) - .build()); - ExporterMetrics exporterMetrics = - ExporterMetrics.createHttpProtobuf("test", "test", meterProviderSupplier); - verifyNoInteractions(meterProviderSupplier); // Ensure lazy - - // Verify the supplier is only called once per underlying meter. - exporterMetrics.addSeen(10); - exporterMetrics.addSeen(20); - verify(meterProviderSupplier, times(1)).get(); - exporterMetrics.addSuccess(30); - exporterMetrics.addSuccess(40); - verify(meterProviderSupplier, times(2)).get(); - exporterMetrics.addFailed(50); - exporterMetrics.addFailed(60); - verify(meterProviderSupplier, times(2)).get(); - } - - @Test - void createHttpProtobuf_noopMeterProvider() { - when(meterProviderSupplier.get()).thenReturn(MeterProvider.noop()); - ExporterMetrics exporterMetrics = - ExporterMetrics.createHttpProtobuf("test", "test", meterProviderSupplier); - verifyNoInteractions(meterProviderSupplier); // Ensure lazy - - // Verify the supplier is invoked multiple times since it returns a noop meter. - exporterMetrics.addSeen(10); - exporterMetrics.addSeen(20); - verify(meterProviderSupplier, times(2)).get(); - exporterMetrics.addSuccess(30); - exporterMetrics.addSuccess(40); - verify(meterProviderSupplier, times(4)).get(); - exporterMetrics.addFailed(50); - exporterMetrics.addFailed(60); - verify(meterProviderSupplier, times(6)).get(); - } -} 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 e562729ae1d..cdf797df320 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 @@ -9,6 +9,7 @@ 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 org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -21,7 +22,11 @@ class GrpcExporterBuilderTest { void setUp() { builder = new GrpcExporterBuilder<>( - "otlp", "span", 0, URI.create("http://localhost:4317"), null, "/test"); + StandardComponentId.ExporterType.OTLP_GRPC_SPAN_EXPORTER, + 0, + URI.create("http://localhost:4317"), + null, + "/test"); } @Test diff --git a/exporters/common/src/test/java/io/opentelemetry/exporter/internal/grpc/GrpcExporterTest.java b/exporters/common/src/test/java/io/opentelemetry/exporter/internal/grpc/GrpcExporterTest.java index f918e782e0d..0ea1fa4a10e 100644 --- a/exporters/common/src/test/java/io/opentelemetry/exporter/internal/grpc/GrpcExporterTest.java +++ b/exporters/common/src/test/java/io/opentelemetry/exporter/internal/grpc/GrpcExporterTest.java @@ -5,10 +5,28 @@ package io.opentelemetry.exporter.internal.grpc; +import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doAnswer; +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.exporter.internal.marshal.Marshaler; +import io.opentelemetry.internal.testing.slf4j.SuppressLogger; +import io.opentelemetry.sdk.common.InternalTelemetryVersion; +import io.opentelemetry.sdk.internal.ComponentId; +import io.opentelemetry.sdk.internal.SemConvAttributes; +import io.opentelemetry.sdk.internal.StandardComponentId; +import io.opentelemetry.sdk.metrics.SdkMeterProvider; +import io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions; +import io.opentelemetry.sdk.testing.exporter.InMemoryMetricReader; +import java.io.IOException; import java.net.URI; +import java.util.function.Consumer; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EnumSource; +import org.mockito.Mockito; class GrpcExporterTest { @@ -17,11 +35,186 @@ void build_NoGrpcSenderProvider() { assertThatThrownBy( () -> new GrpcExporterBuilder<>( - "exporter", "type", 10, new URI("http://localhost"), null, "/path") + StandardComponentId.ExporterType.OTLP_GRPC_SPAN_EXPORTER, + 10, + new URI("http://localhost"), + null, + "/path") .build()) .isInstanceOf(IllegalStateException.class) .hasMessage( "No GrpcSenderProvider found on classpath. Please add dependency on " + "opentelemetry-exporter-sender-okhttp or opentelemetry-exporter-sender-grpc-upstream"); } + + @ParameterizedTest + @EnumSource + @SuppressWarnings("unchecked") + @SuppressLogger(GrpcExporter.class) + void testInternalTelemetry(StandardComponentId.ExporterType exporterType) { + String signalMetricPrefix; + String expectedUnit; + switch (exporterType.signal()) { + case SPAN: + signalMetricPrefix = "otel.sdk.exporter.span."; + expectedUnit = "{span}"; + break; + case LOG: + signalMetricPrefix = "otel.sdk.exporter.log."; + expectedUnit = "{log_record}"; + break; + case METRIC: + signalMetricPrefix = "otel.sdk.exporter.metric_data_point."; + expectedUnit = "{data_point}"; + break; + case PROFILE: + return; // Not yet supported + default: + throw new IllegalStateException(); + } + + InMemoryMetricReader inMemoryMetrics = InMemoryMetricReader.create(); + try (SdkMeterProvider meterProvider = + SdkMeterProvider.builder().registerMetricReader(inMemoryMetrics).build()) { + + StandardComponentId id = ComponentId.generateLazy(exporterType); + + Attributes expectedAttributes = + Attributes.builder() + .put(SemConvAttributes.OTEL_COMPONENT_TYPE, id.getTypeName()) + .put(SemConvAttributes.OTEL_COMPONENT_NAME, id.getComponentName()) + .put(SemConvAttributes.SERVER_ADDRESS, "testing") + .put(SemConvAttributes.SERVER_PORT, 1234) + .build(); + + GrpcSender mockSender = Mockito.mock(GrpcSender.class); + Marshaler mockMarshaller = Mockito.mock(Marshaler.class); + + GrpcExporter exporter = + new GrpcExporter( + mockSender, + InternalTelemetryVersion.LATEST, + id, + () -> meterProvider, + "http://testing:1234"); + + doAnswer( + invoc -> { + Consumer onResponse = invoc.getArgument(1); + + assertThat(inMemoryMetrics.collectAllMetrics()) + .hasSize(1) + .anySatisfy( + metric -> + OpenTelemetryAssertions.assertThat(metric) + .hasName(signalMetricPrefix + "inflight") + .hasUnit(expectedUnit) + .hasLongSumSatisfying( + ma -> + ma.isNotMonotonic() + .hasPointsSatisfying( + pa -> + pa.hasAttributes(expectedAttributes) + .hasValue(42)))); + + onResponse.accept(GrpcResponse.create(0, null)); + return null; + }) + .when(mockSender) + .send(any(), any(), any()); + + exporter.export(mockMarshaller, 42); + + doAnswer( + invoc -> { + Consumer onResponse = invoc.getArgument(1); + onResponse.accept( + GrpcResponse.create(GrpcExporterUtil.GRPC_STATUS_UNAVAILABLE, null)); + return null; + }) + .when(mockSender) + .send(any(), any(), any()); + exporter.export(mockMarshaller, 15); + + doAnswer( + invoc -> { + Consumer onError = invoc.getArgument(2); + onError.accept(new IOException("Computer says no")); + return null; + }) + .when(mockSender) + .send(any(), any(), any()); + exporter.export(mockMarshaller, 7); + + assertThat(inMemoryMetrics.collectAllMetrics()) + .hasSize(3) + .anySatisfy( + metric -> + OpenTelemetryAssertions.assertThat(metric) + .hasName(signalMetricPrefix + "inflight") + .hasUnit(expectedUnit) + .hasLongSumSatisfying( + ma -> + ma.hasPointsSatisfying( + pa -> pa.hasAttributes(expectedAttributes).hasValue(0)))) + .anySatisfy( + metric -> + OpenTelemetryAssertions.assertThat(metric) + .hasName(signalMetricPrefix + "exported") + .hasUnit(expectedUnit) + .hasLongSumSatisfying( + ma -> + ma.hasPointsSatisfying( + pa -> pa.hasAttributes(expectedAttributes).hasValue(42), + pa -> + pa.hasAttributes( + expectedAttributes.toBuilder() + .put( + SemConvAttributes.ERROR_TYPE, + "" + GrpcExporterUtil.GRPC_STATUS_UNAVAILABLE) + .build()) + .hasValue(15), + pa -> + pa.hasAttributes( + expectedAttributes.toBuilder() + .put( + SemConvAttributes.ERROR_TYPE, + "java.io.IOException") + .build()) + .hasValue(7)))) + .anySatisfy( + metric -> + OpenTelemetryAssertions.assertThat(metric) + .hasName("otel.sdk.exporter.operation.duration") + .hasUnit("s") + .hasHistogramSatisfying( + ma -> + ma.hasPointsSatisfying( + pa -> + pa.hasAttributes( + expectedAttributes.toBuilder() + .put(SemConvAttributes.RPC_GRPC_STATUS_CODE, 0) + .build()) + .hasBucketCounts(1), + pa -> + pa.hasAttributes( + expectedAttributes.toBuilder() + .put( + SemConvAttributes.ERROR_TYPE, + "" + GrpcExporterUtil.GRPC_STATUS_UNAVAILABLE) + .put( + SemConvAttributes.RPC_GRPC_STATUS_CODE, + GrpcExporterUtil.GRPC_STATUS_UNAVAILABLE) + .build()) + .hasBucketCounts(1), + pa -> + pa.hasAttributes( + expectedAttributes.toBuilder() + .put( + SemConvAttributes.ERROR_TYPE, + "java.io.IOException") + .build()) + .hasBucketCounts(1)))); + } + } } diff --git a/exporters/common/src/test/java/io/opentelemetry/exporter/internal/http/HttpExporterTest.java b/exporters/common/src/test/java/io/opentelemetry/exporter/internal/http/HttpExporterTest.java index 0afde5a871b..7dcfa3de68e 100644 --- a/exporters/common/src/test/java/io/opentelemetry/exporter/internal/http/HttpExporterTest.java +++ b/exporters/common/src/test/java/io/opentelemetry/exporter/internal/http/HttpExporterTest.java @@ -5,18 +5,234 @@ package io.opentelemetry.exporter.internal.http; +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.Mockito.doAnswer; +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.exporter.internal.marshal.Marshaler; +import io.opentelemetry.internal.testing.slf4j.SuppressLogger; +import io.opentelemetry.sdk.common.InternalTelemetryVersion; +import io.opentelemetry.sdk.internal.ComponentId; +import io.opentelemetry.sdk.internal.SemConvAttributes; +import io.opentelemetry.sdk.internal.StandardComponentId; +import io.opentelemetry.sdk.metrics.SdkMeterProvider; +import io.opentelemetry.sdk.testing.exporter.InMemoryMetricReader; +import java.io.IOException; +import java.util.function.Consumer; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EnumSource; +import org.mockito.Mockito; class HttpExporterTest { @Test void build_NoHttpSenderProvider() { - assertThatThrownBy(() -> new HttpExporterBuilder<>("name", "type", "http://localhost").build()) + assertThatThrownBy( + () -> + new HttpExporterBuilder<>( + StandardComponentId.ExporterType.OTLP_HTTP_SPAN_EXPORTER, + "http://localhost") + .build()) .isInstanceOf(IllegalStateException.class) .hasMessage( "No HttpSenderProvider found on classpath. Please add dependency on " + "opentelemetry-exporter-sender-okhttp or opentelemetry-exporter-sender-jdk"); } + + @ParameterizedTest + @EnumSource + @SuppressLogger(HttpExporter.class) + void testInternalTelemetry(StandardComponentId.ExporterType exporterType) { + String signalMetricPrefix; + String expectedUnit; + switch (exporterType.signal()) { + case SPAN: + signalMetricPrefix = "otel.sdk.exporter.span."; + expectedUnit = "{span}"; + break; + case LOG: + signalMetricPrefix = "otel.sdk.exporter.log."; + expectedUnit = "{log_record}"; + break; + case METRIC: + signalMetricPrefix = "otel.sdk.exporter.metric_data_point."; + expectedUnit = "{data_point}"; + break; + case PROFILE: + return; // Not yet supported + default: + throw new IllegalStateException(); + } + + InMemoryMetricReader inMemoryMetrics = InMemoryMetricReader.create(); + try (SdkMeterProvider meterProvider = + SdkMeterProvider.builder().registerMetricReader(inMemoryMetrics).build()) { + + StandardComponentId id = ComponentId.generateLazy(exporterType); + + Attributes expectedAttributes = + Attributes.builder() + .put(SemConvAttributes.OTEL_COMPONENT_TYPE, id.getTypeName()) + .put(SemConvAttributes.OTEL_COMPONENT_NAME, id.getComponentName()) + .put(SemConvAttributes.SERVER_ADDRESS, "testing") + .put(SemConvAttributes.SERVER_PORT, 1234) + .build(); + + HttpSender mockSender = Mockito.mock(HttpSender.class); + Marshaler mockMarshaller = Mockito.mock(Marshaler.class); + + HttpExporter exporter = + new HttpExporter( + id, + mockSender, + () -> meterProvider, + InternalTelemetryVersion.LATEST, + "http://testing:1234"); + + doAnswer( + invoc -> { + Consumer onResponse = invoc.getArgument(2); + + assertThat(inMemoryMetrics.collectAllMetrics()) + .hasSize(1) + .anySatisfy( + metric -> + assertThat(metric) + .hasName(signalMetricPrefix + "inflight") + .hasUnit(expectedUnit) + .hasLongSumSatisfying( + ma -> + ma.isNotMonotonic() + .hasPointsSatisfying( + pa -> + pa.hasAttributes(expectedAttributes) + .hasValue(42)))); + + onResponse.accept(new FakeHttpResponse(200, "Ok")); + return null; + }) + .when(mockSender) + .send(any(), anyInt(), any(), any()); + + exporter.export(mockMarshaller, 42); + + doAnswer( + invoc -> { + Consumer onResponse = invoc.getArgument(2); + onResponse.accept(new FakeHttpResponse(404, "Not Found")); + return null; + }) + .when(mockSender) + .send(any(), anyInt(), any(), any()); + exporter.export(mockMarshaller, 15); + + doAnswer( + invoc -> { + Consumer onError = invoc.getArgument(3); + onError.accept(new IOException("Computer says no")); + return null; + }) + .when(mockSender) + .send(any(), anyInt(), any(), any()); + exporter.export(mockMarshaller, 7); + + assertThat(inMemoryMetrics.collectAllMetrics()) + .hasSize(3) + .anySatisfy( + metric -> + assertThat(metric) + .hasName(signalMetricPrefix + "inflight") + .hasUnit(expectedUnit) + .hasLongSumSatisfying( + ma -> + ma.hasPointsSatisfying( + pa -> pa.hasAttributes(expectedAttributes).hasValue(0)))) + .anySatisfy( + metric -> + assertThat(metric) + .hasName(signalMetricPrefix + "exported") + .hasUnit(expectedUnit) + .hasLongSumSatisfying( + ma -> + ma.hasPointsSatisfying( + pa -> pa.hasAttributes(expectedAttributes).hasValue(42), + pa -> + pa.hasAttributes( + expectedAttributes.toBuilder() + .put(SemConvAttributes.ERROR_TYPE, "404") + .build()) + .hasValue(15), + pa -> + pa.hasAttributes( + expectedAttributes.toBuilder() + .put( + SemConvAttributes.ERROR_TYPE, + "java.io.IOException") + .build()) + .hasValue(7)))) + .anySatisfy( + metric -> + assertThat(metric) + .hasName("otel.sdk.exporter.operation.duration") + .hasUnit("s") + .hasHistogramSatisfying( + ma -> + ma.hasPointsSatisfying( + pa -> + pa.hasAttributes( + expectedAttributes.toBuilder() + .put( + SemConvAttributes.HTTP_RESPONSE_STATUS_CODE, + 200) + .build()) + .hasBucketCounts(1), + pa -> + pa.hasAttributes( + expectedAttributes.toBuilder() + .put(SemConvAttributes.ERROR_TYPE, "404") + .put( + SemConvAttributes.HTTP_RESPONSE_STATUS_CODE, + 404) + .build()) + .hasBucketCounts(1), + pa -> + pa.hasAttributes( + expectedAttributes.toBuilder() + .put( + SemConvAttributes.ERROR_TYPE, + "java.io.IOException") + .build()) + .hasBucketCounts(1)))); + } + } + + private static class FakeHttpResponse implements HttpSender.Response { + + final int statusCode; + final String statusMessage; + + FakeHttpResponse(int statusCode, String statusMessage) { + this.statusCode = statusCode; + this.statusMessage = statusMessage; + } + + @Override + public int statusCode() { + return statusCode; + } + + @Override + public String statusMessage() { + return statusMessage; + } + + @Override + public byte[] responseBody() throws IOException { + return new byte[0]; + } + } } diff --git a/exporters/common/src/test/java/io/opentelemetry/exporter/internal/metrics/ExporterInstrumentationTest.java b/exporters/common/src/test/java/io/opentelemetry/exporter/internal/metrics/ExporterInstrumentationTest.java new file mode 100644 index 00000000000..444bbd3b935 --- /dev/null +++ b/exporters/common/src/test/java/io/opentelemetry/exporter/internal/metrics/ExporterInstrumentationTest.java @@ -0,0 +1,159 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.exporter.internal.metrics; + +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat; +import static org.mockito.Mockito.atLeastOnce; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoInteractions; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.when; + +import io.opentelemetry.api.metrics.MeterProvider; +import io.opentelemetry.sdk.common.CompletableResultCode; +import io.opentelemetry.sdk.common.InternalTelemetryVersion; +import io.opentelemetry.sdk.internal.ComponentId; +import io.opentelemetry.sdk.internal.SemConvAttributes; +import io.opentelemetry.sdk.internal.StandardComponentId; +import io.opentelemetry.sdk.metrics.InstrumentType; +import io.opentelemetry.sdk.metrics.SdkMeterProvider; +import io.opentelemetry.sdk.metrics.data.AggregationTemporality; +import io.opentelemetry.sdk.metrics.export.CollectionRegistration; +import io.opentelemetry.sdk.metrics.export.MetricReader; +import java.util.function.Supplier; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EnumSource; +import org.mockito.Mockito; + +class ExporterInstrumentationTest { + + @SuppressWarnings("unchecked") + Supplier meterProviderSupplier = mock(Supplier.class); + + @ParameterizedTest + @EnumSource() + void validMeterProvider(InternalTelemetryVersion schemaVersion) { + when(meterProviderSupplier.get()) + .thenReturn( + SdkMeterProvider.builder() + // Have to provide a valid reader. + .registerMetricReader( + new MetricReader() { + @Override + public void register(CollectionRegistration registration) {} + + @Override + public CompletableResultCode forceFlush() { + return CompletableResultCode.ofSuccess(); + } + + @Override + public CompletableResultCode shutdown() { + return CompletableResultCode.ofSuccess(); + } + + @Override + public AggregationTemporality getAggregationTemporality( + InstrumentType instrumentType) { + return AggregationTemporality.CUMULATIVE; + } + }) + .build()); + ExporterInstrumentation instrumentation = + new ExporterInstrumentation( + schemaVersion, + meterProviderSupplier, + ComponentId.generateLazy(StandardComponentId.ExporterType.OTLP_GRPC_SPAN_EXPORTER), + "http://testing:1234"); + verifyNoInteractions(meterProviderSupplier); // Ensure lazy + + // Verify the supplier is only called once per underlying meter. + + instrumentation.startRecordingExport(42).finishFailed("foo"); + instrumentation.startRecordingExport(42).finishSuccessful(); + verify(meterProviderSupplier, atLeastOnce()).get(); + + instrumentation.startRecordingExport(42).finishFailed("foo"); + instrumentation.startRecordingExport(42).finishSuccessful(); + verifyNoMoreInteractions(meterProviderSupplier); + } + + @ParameterizedTest + @EnumSource() + void noopMeterProvider(InternalTelemetryVersion schemaVersion) { + + when(meterProviderSupplier.get()).thenReturn(MeterProvider.noop()); + ExporterInstrumentation instrumentation = + new ExporterInstrumentation( + schemaVersion, + meterProviderSupplier, + ComponentId.generateLazy(StandardComponentId.ExporterType.OTLP_GRPC_SPAN_EXPORTER), + "http://testing:1234"); + verifyNoInteractions(meterProviderSupplier); // Ensure lazy + + // Verify the supplier is invoked multiple times since it returns a noop meter. + instrumentation.startRecordingExport(42).finishFailed("foo"); + instrumentation.startRecordingExport(42).finishSuccessful(); + verify(meterProviderSupplier, atLeastOnce()).get(); + + Mockito.clearInvocations((Object) meterProviderSupplier); + instrumentation.startRecordingExport(42).finishFailed("foo"); + instrumentation.startRecordingExport(42).finishSuccessful(); + verify(meterProviderSupplier, atLeastOnce()).get(); + } + + @Test + void serverAttributesInvalidUrl() { + assertThat(ExporterInstrumentation.extractServerAttributes("^")).isEmpty(); + } + + @Test + void serverAttributesEmptyUrl() { + assertThat(ExporterInstrumentation.extractServerAttributes("")).isEmpty(); + } + + @Test + void serverAttributesHttps() { + assertThat(ExporterInstrumentation.extractServerAttributes("https://example.com/foo/bar?a=b")) + .hasSize(2) + .containsEntry(SemConvAttributes.SERVER_ADDRESS, "example.com") + .containsEntry(SemConvAttributes.SERVER_PORT, 443); + + assertThat( + ExporterInstrumentation.extractServerAttributes("https://example.com:1234/foo/bar?a=b")) + .hasSize(2) + .containsEntry(SemConvAttributes.SERVER_ADDRESS, "example.com") + .containsEntry(SemConvAttributes.SERVER_PORT, 1234); + } + + @Test + void serverAttributesHttp() { + assertThat(ExporterInstrumentation.extractServerAttributes("http://example.com/foo/bar?a=b")) + .hasSize(2) + .containsEntry(SemConvAttributes.SERVER_ADDRESS, "example.com") + .containsEntry(SemConvAttributes.SERVER_PORT, 80); + + assertThat( + ExporterInstrumentation.extractServerAttributes("http://example.com:1234/foo/bar?a=b")) + .hasSize(2) + .containsEntry(SemConvAttributes.SERVER_ADDRESS, "example.com") + .containsEntry(SemConvAttributes.SERVER_PORT, 1234); + } + + @Test + void serverAttributesUnknownScheme() { + assertThat(ExporterInstrumentation.extractServerAttributes("custom://foo")) + .hasSize(1) + .containsEntry(SemConvAttributes.SERVER_ADDRESS, "foo"); + + assertThat(ExporterInstrumentation.extractServerAttributes("custom://foo:1234")) + .hasSize(2) + .containsEntry(SemConvAttributes.SERVER_ADDRESS, "foo") + .containsEntry(SemConvAttributes.SERVER_PORT, 1234); + } +} diff --git a/exporters/common/src/testGrpcSenderProvider/java/io/opentelemetry/exporter/internal/grpc/GrpcExporterTest.java b/exporters/common/src/testGrpcSenderProvider/java/io/opentelemetry/exporter/internal/grpc/GrpcExporterTest.java index 4e2979dd275..d6aa748fc2e 100644 --- a/exporters/common/src/testGrpcSenderProvider/java/io/opentelemetry/exporter/internal/grpc/GrpcExporterTest.java +++ b/exporters/common/src/testGrpcSenderProvider/java/io/opentelemetry/exporter/internal/grpc/GrpcExporterTest.java @@ -20,6 +20,7 @@ import io.opentelemetry.exporter.sender.grpc.managedchannel.internal.UpstreamGrpcSender; import io.opentelemetry.exporter.sender.okhttp.internal.OkHttpGrpcSender; import io.opentelemetry.internal.testing.slf4j.SuppressLogger; +import io.opentelemetry.sdk.internal.StandardComponentId; import java.net.URI; import java.net.URISyntaxException; import javax.annotation.Nullable; @@ -39,8 +40,7 @@ void build_multipleSendersNoConfiguration() { assertThatCode( () -> new GrpcExporterBuilder<>( - "exporter", - "type", + StandardComponentId.ExporterType.OTLP_GRPC_SPAN_EXPORTER, 10, new URI("http://localhost"), () -> DummyServiceFutureStub::newFutureStub, @@ -63,8 +63,7 @@ void build_multipleSendersNoConfiguration() { void build_multipleSendersWithUpstream() throws URISyntaxException { assertThat( new GrpcExporterBuilder<>( - "exporter", - "type", + StandardComponentId.ExporterType.OTLP_GRPC_SPAN_EXPORTER, 10, new URI("http://localhost"), () -> DummyServiceFutureStub::newFutureStub, @@ -84,8 +83,7 @@ void build_multipleSendersWithUpstream() throws URISyntaxException { void build_multipleSendersWithOkHttp() throws URISyntaxException { assertThat( new GrpcExporterBuilder<>( - "exporter", - "type", + StandardComponentId.ExporterType.OTLP_GRPC_SPAN_EXPORTER, 10, new URI("http://localhost"), () -> DummyServiceFutureStub::newFutureStub, @@ -106,8 +104,7 @@ void build_multipleSendersNoMatch() { assertThatThrownBy( () -> new GrpcExporterBuilder<>( - "exporter", - "type", + StandardComponentId.ExporterType.OTLP_GRPC_SPAN_EXPORTER, 10, new URI("http://localhost"), () -> DummyServiceFutureStub::newFutureStub, diff --git a/exporters/common/src/testHttpSenderProvider/java/io/opentelemetry/exporter/internal/http/HttpExporterTest.java b/exporters/common/src/testHttpSenderProvider/java/io/opentelemetry/exporter/internal/http/HttpExporterTest.java index 9dd270d5b3c..033d8f15ad3 100644 --- a/exporters/common/src/testHttpSenderProvider/java/io/opentelemetry/exporter/internal/http/HttpExporterTest.java +++ b/exporters/common/src/testHttpSenderProvider/java/io/opentelemetry/exporter/internal/http/HttpExporterTest.java @@ -12,6 +12,7 @@ import io.opentelemetry.exporter.sender.jdk.internal.JdkHttpSender; import io.opentelemetry.exporter.sender.okhttp.internal.OkHttpHttpSender; import io.opentelemetry.internal.testing.slf4j.SuppressLogger; +import io.opentelemetry.sdk.internal.StandardComponentId; import org.assertj.core.api.Assertions; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; @@ -27,7 +28,11 @@ class HttpExporterTest { @SuppressLogger(HttpExporterBuilder.class) void build_multipleSendersNoConfiguration() { Assertions.assertThatCode( - () -> new HttpExporterBuilder<>("exporter", "type", "http://localhost").build()) + () -> + new HttpExporterBuilder<>( + StandardComponentId.ExporterType.OTLP_HTTP_SPAN_EXPORTER, + "http://localhost") + .build()) .doesNotThrowAnyException(); logCapturer.assertContains( @@ -41,7 +46,10 @@ void build_multipleSendersNoConfiguration() { key = "io.opentelemetry.exporter.internal.http.HttpSenderProvider", value = "io.opentelemetry.exporter.sender.jdk.internal.JdkHttpSenderProvider") void build_multipleSendersWithJdk() { - assertThat(new HttpExporterBuilder<>("exporter", "type", "http://localhost").build()) + assertThat( + new HttpExporterBuilder<>( + StandardComponentId.ExporterType.OTLP_HTTP_SPAN_EXPORTER, "http://localhost") + .build()) .extracting("httpSender") .isInstanceOf(JdkHttpSender.class); @@ -53,7 +61,10 @@ void build_multipleSendersWithJdk() { key = "io.opentelemetry.exporter.internal.http.HttpSenderProvider", value = "io.opentelemetry.exporter.sender.okhttp.internal.OkHttpHttpSenderProvider") void build_multipleSendersWithOkHttp() { - assertThat(new HttpExporterBuilder<>("exporter", "type", "http://localhost").build()) + assertThat( + new HttpExporterBuilder<>( + StandardComponentId.ExporterType.OTLP_HTTP_SPAN_EXPORTER, "http://localhost") + .build()) .extracting("httpSender") .isInstanceOf(OkHttpHttpSender.class); @@ -66,7 +77,11 @@ void build_multipleSendersWithOkHttp() { value = "foo") void build_multipleSendersNoMatch() { assertThatThrownBy( - () -> new HttpExporterBuilder<>("exporter", "type", "http://localhost").build()) + () -> + new HttpExporterBuilder<>( + StandardComponentId.ExporterType.OTLP_HTTP_SPAN_EXPORTER, + "http://localhost") + .build()) .isInstanceOf(IllegalStateException.class) .hasMessage( "No HttpSenderProvider matched configured io.opentelemetry.exporter.internal.http.HttpSenderProvider: foo"); diff --git a/exporters/otlp/all/src/jmh/java/io/opentelemetry/exporter/otlp/trace/OltpExporterBenchmark.java b/exporters/otlp/all/src/jmh/java/io/opentelemetry/exporter/otlp/trace/OltpExporterBenchmark.java index f45272a4bca..586fe4e9960 100644 --- a/exporters/otlp/all/src/jmh/java/io/opentelemetry/exporter/otlp/trace/OltpExporterBenchmark.java +++ b/exporters/otlp/all/src/jmh/java/io/opentelemetry/exporter/otlp/trace/OltpExporterBenchmark.java @@ -23,6 +23,9 @@ import io.opentelemetry.proto.collector.trace.v1.ExportTraceServiceResponse; import io.opentelemetry.proto.collector.trace.v1.TraceServiceGrpc; import io.opentelemetry.sdk.common.CompletableResultCode; +import io.opentelemetry.sdk.common.InternalTelemetryVersion; +import io.opentelemetry.sdk.internal.ComponentId; +import io.opentelemetry.sdk.internal.StandardComponentId; import java.net.URI; import java.util.Collections; import java.util.concurrent.TimeUnit; @@ -82,20 +85,19 @@ public void setUp() { .build(); upstreamGrpcExporter = new GrpcExporter<>( - "otlp", - "span", new UpstreamGrpcSender<>( MarshalerTraceServiceGrpc.newFutureStub(defaultGrpcChannel, null), /* shutdownChannel= */ false, 10, Collections::emptyMap, null), - MeterProvider::noop); + InternalTelemetryVersion.LATEST, + ComponentId.generateLazy(StandardComponentId.ExporterType.OTLP_GRPC_SPAN_EXPORTER), + MeterProvider::noop, + "http://localhost"); okhttpGrpcSender = new GrpcExporter<>( - "otlp", - "span", new OkHttpGrpcSender<>( URI.create("http://localhost:" + server.activeLocalPort()) .resolve(OtlpGrpcSpanExporterBuilder.GRPC_ENDPOINT_PATH) @@ -108,11 +110,15 @@ public void setUp() { null, null, null), - MeterProvider::noop); + InternalTelemetryVersion.LATEST, + ComponentId.generateLazy(StandardComponentId.ExporterType.OTLP_GRPC_SPAN_EXPORTER), + MeterProvider::noop, + "http://localhost"); httpExporter = new HttpExporterBuilder( - "otlp", "span", "http://localhost:" + server.activeLocalPort() + "/v1/traces") + StandardComponentId.ExporterType.OTLP_HTTP_SPAN_EXPORTER, + "http://localhost:" + server.activeLocalPort() + "/v1/traces") .build(); } 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 1ba267c7235..c7426f1880b 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 @@ -16,9 +16,11 @@ import io.opentelemetry.exporter.internal.http.HttpExporterBuilder; import io.opentelemetry.exporter.internal.marshal.Marshaler; import io.opentelemetry.exporter.otlp.internal.OtlpUserAgent; +import io.opentelemetry.sdk.common.InternalTelemetryVersion; import io.opentelemetry.sdk.common.export.MemoryMode; import io.opentelemetry.sdk.common.export.ProxyOptions; import io.opentelemetry.sdk.common.export.RetryPolicy; +import io.opentelemetry.sdk.internal.StandardComponentId; import java.time.Duration; import java.util.Map; import java.util.concurrent.ExecutorService; @@ -48,7 +50,10 @@ public final class OtlpHttpLogRecordExporterBuilder { } OtlpHttpLogRecordExporterBuilder() { - this(new HttpExporterBuilder<>("otlp", "log", DEFAULT_ENDPOINT), DEFAULT_MEMORY_MODE); + this( + new HttpExporterBuilder<>( + StandardComponentId.ExporterType.OTLP_HTTP_LOG_EXPORTER, DEFAULT_ENDPOINT), + DEFAULT_MEMORY_MODE); } /** @@ -212,6 +217,17 @@ public OtlpHttpLogRecordExporterBuilder setMeterProvider( return this; } + /** + * Sets the {@link InternalTelemetryVersion} defining which self-monitoring metrics this exporter + * collects. + */ + public OtlpHttpLogRecordExporterBuilder setInternalTelemetryVersion( + InternalTelemetryVersion schemaVersion) { + requireNonNull(schemaVersion, "schemaVersion"); + delegate.setInternalTelemetryVersion(schemaVersion); + return this; + } + /** * Set the {@link MemoryMode}. If unset, defaults to {@link #DEFAULT_MEMORY_MODE}. * 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 5307e2991ab..f553c6d6bcf 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 @@ -16,9 +16,11 @@ import io.opentelemetry.exporter.internal.http.HttpExporterBuilder; import io.opentelemetry.exporter.internal.marshal.Marshaler; import io.opentelemetry.exporter.otlp.internal.OtlpUserAgent; +import io.opentelemetry.sdk.common.InternalTelemetryVersion; import io.opentelemetry.sdk.common.export.MemoryMode; import io.opentelemetry.sdk.common.export.ProxyOptions; import io.opentelemetry.sdk.common.export.RetryPolicy; +import io.opentelemetry.sdk.internal.StandardComponentId; import io.opentelemetry.sdk.metrics.InstrumentType; import io.opentelemetry.sdk.metrics.data.MetricData; import io.opentelemetry.sdk.metrics.export.AggregationTemporalitySelector; @@ -67,7 +69,8 @@ public final class OtlpHttpMetricExporterBuilder { OtlpHttpMetricExporterBuilder() { this( - new HttpExporterBuilder<>("otlp", "metric", DEFAULT_ENDPOINT), + new HttpExporterBuilder<>( + StandardComponentId.ExporterType.OTLP_HTTP_METRIC_EXPORTER, DEFAULT_ENDPOINT), DEFAULT_AGGREGATION_TEMPORALITY_SELECTOR, DefaultAggregationSelector.getDefault(), DEFAULT_MEMORY_MODE); @@ -243,6 +246,17 @@ public OtlpHttpMetricExporterBuilder setProxyOptions(ProxyOptions proxyOptions) return this; } + /** + * Sets the {@link InternalTelemetryVersion} defining which self-monitoring metrics this exporter + * collects. + */ + public OtlpHttpMetricExporterBuilder setInternalTelemetryVersion( + InternalTelemetryVersion schemaVersion) { + requireNonNull(schemaVersion, "schemaVersion"); + delegate.setInternalTelemetryVersion(schemaVersion); + return this; + } + /** * Sets the {@link MeterProvider} to use to collect metrics related to export. If not set, uses * {@link GlobalOpenTelemetry#getMeterProvider()}. 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 11bd7192db9..8f8cd9ec6a7 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 @@ -16,9 +16,11 @@ import io.opentelemetry.exporter.internal.http.HttpExporterBuilder; import io.opentelemetry.exporter.internal.marshal.Marshaler; import io.opentelemetry.exporter.otlp.internal.OtlpUserAgent; +import io.opentelemetry.sdk.common.InternalTelemetryVersion; import io.opentelemetry.sdk.common.export.MemoryMode; import io.opentelemetry.sdk.common.export.ProxyOptions; import io.opentelemetry.sdk.common.export.RetryPolicy; +import io.opentelemetry.sdk.internal.StandardComponentId; import java.time.Duration; import java.util.Map; import java.util.concurrent.ExecutorService; @@ -48,7 +50,10 @@ public final class OtlpHttpSpanExporterBuilder { } OtlpHttpSpanExporterBuilder() { - this(new HttpExporterBuilder<>("otlp", "span", DEFAULT_ENDPOINT), DEFAULT_MEMORY_MODE); + this( + new HttpExporterBuilder<>( + StandardComponentId.ExporterType.OTLP_HTTP_SPAN_EXPORTER, DEFAULT_ENDPOINT), + DEFAULT_MEMORY_MODE); } /** @@ -213,6 +218,17 @@ public OtlpHttpSpanExporterBuilder setMeterProvider( return this; } + /** + * Sets the {@link InternalTelemetryVersion} defining which self-monitoring metrics this exporter + * collects. + */ + public OtlpHttpSpanExporterBuilder setInternalTelemetryVersion( + InternalTelemetryVersion schemaVersion) { + requireNonNull(schemaVersion, "schemaVersion"); + delegate.setInternalTelemetryVersion(schemaVersion); + return this; + } + /** * Set the {@link MemoryMode}. If unset, defaults to {@link #DEFAULT_MEMORY_MODE}. * 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 a26629ed7f4..88f07f98710 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 @@ -17,8 +17,10 @@ import io.opentelemetry.exporter.internal.grpc.GrpcExporterBuilder; import io.opentelemetry.exporter.internal.marshal.Marshaler; import io.opentelemetry.exporter.otlp.internal.OtlpUserAgent; +import io.opentelemetry.sdk.common.InternalTelemetryVersion; import io.opentelemetry.sdk.common.export.MemoryMode; import io.opentelemetry.sdk.common.export.RetryPolicy; +import io.opentelemetry.sdk.internal.StandardComponentId; import java.net.URI; import java.time.Duration; import java.util.Map; @@ -59,8 +61,7 @@ public final class OtlpGrpcLogRecordExporterBuilder { OtlpGrpcLogRecordExporterBuilder() { this( new GrpcExporterBuilder<>( - "otlp", - "log", + StandardComponentId.ExporterType.OTLP_GRPC_LOG_EXPORTER, DEFAULT_TIMEOUT_SECS, DEFAULT_ENDPOINT, () -> MarshalerLogsServiceGrpc::newFutureStub, @@ -245,6 +246,17 @@ public OtlpGrpcLogRecordExporterBuilder setMeterProvider( return this; } + /** + * Sets the {@link InternalTelemetryVersion} defining which self-monitoring metrics this exporter + * collects. + */ + public OtlpGrpcLogRecordExporterBuilder setInternalTelemetryVersion( + InternalTelemetryVersion schemaVersion) { + requireNonNull(schemaVersion, "schemaVersion"); + delegate.setInternalTelemetryVersion(schemaVersion); + return this; + } + /** * Set the {@link MemoryMode}. If unset, defaults to {@link #DEFAULT_MEMORY_MODE}. * 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 b5b74c44ce9..ec969083515 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 @@ -17,8 +17,10 @@ import io.opentelemetry.exporter.internal.grpc.GrpcExporterBuilder; import io.opentelemetry.exporter.internal.marshal.Marshaler; import io.opentelemetry.exporter.otlp.internal.OtlpUserAgent; +import io.opentelemetry.sdk.common.InternalTelemetryVersion; import io.opentelemetry.sdk.common.export.MemoryMode; import io.opentelemetry.sdk.common.export.RetryPolicy; +import io.opentelemetry.sdk.internal.StandardComponentId; import io.opentelemetry.sdk.metrics.InstrumentType; import io.opentelemetry.sdk.metrics.data.MetricData; import io.opentelemetry.sdk.metrics.export.AggregationTemporalitySelector; @@ -76,8 +78,7 @@ public final class OtlpGrpcMetricExporterBuilder { OtlpGrpcMetricExporterBuilder() { this( new GrpcExporterBuilder<>( - "otlp", - "metric", + StandardComponentId.ExporterType.OTLP_GRPC_METRIC_EXPORTER, DEFAULT_TIMEOUT_SECS, DEFAULT_ENDPOINT, () -> MarshalerMetricsServiceGrpc::newFutureStub, @@ -273,6 +274,17 @@ public OtlpGrpcMetricExporterBuilder setRetryPolicy(@Nullable RetryPolicy retryP return this; } + /** + * Sets the {@link InternalTelemetryVersion} defining which self-monitoring metrics this exporter + * collects. + */ + public OtlpGrpcMetricExporterBuilder setInternalTelemetryVersion( + InternalTelemetryVersion schemaVersion) { + requireNonNull(schemaVersion, "schemaVersion"); + delegate.setInternalTelemetryVersion(schemaVersion); + return this; + } + /** * Sets the {@link MeterProvider} to use to collect metrics related to export. If not set, uses * {@link GlobalOpenTelemetry#getMeterProvider()}. 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 d7452606d74..cbe12a97193 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 @@ -17,8 +17,10 @@ import io.opentelemetry.exporter.internal.grpc.GrpcExporterBuilder; import io.opentelemetry.exporter.internal.marshal.Marshaler; import io.opentelemetry.exporter.otlp.internal.OtlpUserAgent; +import io.opentelemetry.sdk.common.InternalTelemetryVersion; import io.opentelemetry.sdk.common.export.MemoryMode; import io.opentelemetry.sdk.common.export.RetryPolicy; +import io.opentelemetry.sdk.internal.StandardComponentId; import java.net.URI; import java.time.Duration; import java.util.Map; @@ -55,8 +57,7 @@ public final class OtlpGrpcSpanExporterBuilder { OtlpGrpcSpanExporterBuilder() { this( new GrpcExporterBuilder<>( - "otlp", - "span", + StandardComponentId.ExporterType.OTLP_GRPC_SPAN_EXPORTER, DEFAULT_TIMEOUT_SECS, DEFAULT_ENDPOINT, () -> MarshalerTraceServiceGrpc::newFutureStub, @@ -242,6 +243,17 @@ public OtlpGrpcSpanExporterBuilder setMeterProvider( return this; } + /** + * Sets the {@link InternalTelemetryVersion} defining which self-monitoring metrics this exporter + * collects. + */ + public OtlpGrpcSpanExporterBuilder setInternalTelemetryVersion( + InternalTelemetryVersion schemaVersion) { + requireNonNull(schemaVersion, "schemaVersion"); + delegate.setInternalTelemetryVersion(schemaVersion); + return this; + } + /** * Set the {@link MemoryMode}. If unset, defaults to {@link #DEFAULT_MEMORY_MODE}. * 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 604178d7fd5..91d34b00e6e 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 @@ -17,6 +17,7 @@ import io.opentelemetry.exporter.internal.marshal.Marshaler; import io.opentelemetry.exporter.otlp.internal.OtlpUserAgent; import io.opentelemetry.sdk.common.export.RetryPolicy; +import io.opentelemetry.sdk.internal.StandardComponentId; import java.net.URI; import java.time.Duration; import java.util.Map; @@ -54,8 +55,7 @@ public final class OtlpGrpcProfilesExporterBuilder { OtlpGrpcProfilesExporterBuilder() { this( new GrpcExporterBuilder<>( - "otlp", - "profile", + StandardComponentId.ExporterType.OTLP_GRPC_PROFILES_EXPORTER, DEFAULT_TIMEOUT_SECS, DEFAULT_ENDPOINT, () -> MarshalerProfilesServiceGrpc::newFutureStub, diff --git a/exporters/otlp/testing-internal/src/main/java/io/opentelemetry/exporter/otlp/testing/internal/AbstractGrpcTelemetryExporterTest.java b/exporters/otlp/testing-internal/src/main/java/io/opentelemetry/exporter/otlp/testing/internal/AbstractGrpcTelemetryExporterTest.java index c87b6d1e199..7db5dbe7d0b 100644 --- a/exporters/otlp/testing-internal/src/main/java/io/opentelemetry/exporter/otlp/testing/internal/AbstractGrpcTelemetryExporterTest.java +++ b/exporters/otlp/testing-internal/src/main/java/io/opentelemetry/exporter/otlp/testing/internal/AbstractGrpcTelemetryExporterTest.java @@ -5,6 +5,8 @@ package io.opentelemetry.exporter.otlp.testing.internal; +import static io.opentelemetry.api.common.AttributeKey.stringKey; +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.satisfies; import static org.assertj.core.api.Assertions.as; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatCode; @@ -44,7 +46,13 @@ import io.opentelemetry.proto.collector.trace.v1.ExportTraceServiceRequest; import io.opentelemetry.proto.collector.trace.v1.ExportTraceServiceResponse; import io.opentelemetry.sdk.common.CompletableResultCode; +import io.opentelemetry.sdk.common.InternalTelemetryVersion; import io.opentelemetry.sdk.common.export.RetryPolicy; +import io.opentelemetry.sdk.internal.SemConvAttributes; +import io.opentelemetry.sdk.metrics.SdkMeterProvider; +import io.opentelemetry.sdk.testing.assertj.AttributeAssertion; +import io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions; +import io.opentelemetry.sdk.testing.exporter.InMemoryMetricReader; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.UncheckedIOException; @@ -1070,8 +1078,6 @@ void stringRepresentation() throws IOException, CertificateEncodingException { assertThat(telemetryExporter.unwrap().toString()) .matches( "OtlpGrpc[a-zA-Z]*Exporter\\{" - + "exporterName=otlp, " - + "type=[a-zA_Z]*, " + "endpoint=http://localhost:4317, " + "endpointPath=.*, " + "timeoutNanos=" @@ -1111,8 +1117,6 @@ void stringRepresentation() throws IOException, CertificateEncodingException { assertThat(telemetryExporter.unwrap().toString()) .matches( "OtlpGrpc[a-zA-Z]*Exporter\\{" - + "exporterName=otlp, " - + "type=[a-zA_Z]*, " + "endpoint=http://example:4317, " + "endpointPath=.*, " + "timeoutNanos=" @@ -1131,6 +1135,126 @@ void stringRepresentation() throws IOException, CertificateEncodingException { } } + @Test + void latestInternalTelemetry() { + // Profiles do not expose metrics yet, so skip + assumeThat(type).isNotEqualTo("profile"); + + InMemoryMetricReader inMemoryMetrics = InMemoryMetricReader.create(); + try (SdkMeterProvider meterProvider = + SdkMeterProvider.builder().registerMetricReader(inMemoryMetrics).build()) { + + TelemetryExporter exporter = + exporterBuilder() + .setEndpoint(server.httpUri().toString()) + .setMeterProvider(() -> meterProvider) + .setInternalTelemetryVersion(InternalTelemetryVersion.LATEST) + .build(); + + List telemetry = Collections.singletonList(generateFakeTelemetry()); + assertThat(exporter.export(telemetry).join(10, TimeUnit.SECONDS).isSuccess()).isTrue(); + + List expectedAttributes = + Arrays.asList( + satisfies( + SemConvAttributes.OTEL_COMPONENT_TYPE, + str -> str.matches("otlp_grpc_(log|metric|span)_exporter")), + satisfies( + SemConvAttributes.OTEL_COMPONENT_NAME, + str -> str.matches("otlp_grpc_(log|metric|span)_exporter/\\d+")), + satisfies( + SemConvAttributes.SERVER_PORT, str -> str.isEqualTo(server.httpUri().getPort())), + satisfies( + SemConvAttributes.SERVER_ADDRESS, + str -> str.isEqualTo(server.httpUri().getHost()))); + + assertThat(inMemoryMetrics.collectAllMetrics()) + .hasSize(3) + .anySatisfy( + metric -> + OpenTelemetryAssertions.assertThat(metric) + .satisfies( + m -> + assertThat(m.getName()) + .matches( + "otel.sdk.exporter.(span|metric_data_point|log).inflight")) + .hasLongSumSatisfying( + ma -> + ma.hasPointsSatisfying( + pa -> pa.hasAttributesSatisfying(expectedAttributes)))) + .anySatisfy( + metric -> + OpenTelemetryAssertions.assertThat(metric) + .satisfies( + m -> + assertThat(m.getName()) + .matches( + "otel.sdk.exporter.(span|metric_data_point|log).exported")) + .hasLongSumSatisfying( + ma -> + ma.hasPointsSatisfying( + pa -> pa.hasAttributesSatisfying(expectedAttributes)))) + .anySatisfy( + metric -> + OpenTelemetryAssertions.assertThat(metric) + .hasName("otel.sdk.exporter.operation.duration") + .hasHistogramSatisfying( + ma -> + ma.hasPointsSatisfying( + pa -> + pa.hasAttributesSatisfying(expectedAttributes) + .hasBucketCounts(1)))); + } + } + + @Test + void legacyInternalTelemetry() { + // Profiles do not expose metrics yet, so skip + assumeThat(type).isNotEqualTo("profile"); + + InMemoryMetricReader inMemoryMetrics = InMemoryMetricReader.create(); + try (SdkMeterProvider meterProvider = + SdkMeterProvider.builder().registerMetricReader(inMemoryMetrics).build()) { + + TelemetryExporter exporter = + exporterBuilder() + .setEndpoint(server.httpUri().toString()) + .setMeterProvider(() -> meterProvider) + .setInternalTelemetryVersion(InternalTelemetryVersion.LEGACY) + .build(); + + List telemetry = Collections.singletonList(generateFakeTelemetry()); + assertThat(exporter.export(telemetry).join(10, TimeUnit.SECONDS).isSuccess()).isTrue(); + + assertThat(inMemoryMetrics.collectAllMetrics()) + .hasSize(2) + .anySatisfy( + metric -> + OpenTelemetryAssertions.assertThat(metric) + .hasName("otlp.exporter.seen") + .hasLongSumSatisfying( + ma -> + ma.hasPointsSatisfying( + pa -> + pa.hasAttributesSatisfying( + satisfies( + stringKey("type"), + str -> str.matches("log|span|metric")))))) + .anySatisfy( + metric -> + OpenTelemetryAssertions.assertThat(metric) + .hasName("otlp.exporter.exported") + .hasLongSumSatisfying( + ma -> + ma.hasPointsSatisfying( + pa -> + pa.hasAttributesSatisfying( + satisfies( + stringKey("type"), + str -> str.matches("log|span|metric")))))); + } + } + protected abstract TelemetryExporterBuilder exporterBuilder(); protected abstract TelemetryExporterBuilder toBuilder(TelemetryExporter exporter); diff --git a/exporters/otlp/testing-internal/src/main/java/io/opentelemetry/exporter/otlp/testing/internal/AbstractHttpTelemetryExporterTest.java b/exporters/otlp/testing-internal/src/main/java/io/opentelemetry/exporter/otlp/testing/internal/AbstractHttpTelemetryExporterTest.java index 705593c65fb..97e1b1d931d 100644 --- a/exporters/otlp/testing-internal/src/main/java/io/opentelemetry/exporter/otlp/testing/internal/AbstractHttpTelemetryExporterTest.java +++ b/exporters/otlp/testing-internal/src/main/java/io/opentelemetry/exporter/otlp/testing/internal/AbstractHttpTelemetryExporterTest.java @@ -5,7 +5,9 @@ package io.opentelemetry.exporter.otlp.testing.internal; -import static org.assertj.core.api.Assertions.assertThat; +import static io.opentelemetry.api.common.AttributeKey.stringKey; +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat; +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.satisfies; import static org.assertj.core.api.Assertions.assertThatCode; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.junit.jupiter.api.Named.named; @@ -40,8 +42,13 @@ import io.opentelemetry.proto.collector.trace.v1.ExportTraceServiceRequest; import io.opentelemetry.proto.collector.trace.v1.ExportTraceServiceResponse; import io.opentelemetry.sdk.common.CompletableResultCode; +import io.opentelemetry.sdk.common.InternalTelemetryVersion; import io.opentelemetry.sdk.common.export.ProxyOptions; import io.opentelemetry.sdk.common.export.RetryPolicy; +import io.opentelemetry.sdk.internal.SemConvAttributes; +import io.opentelemetry.sdk.metrics.SdkMeterProvider; +import io.opentelemetry.sdk.testing.assertj.AttributeAssertion; +import io.opentelemetry.sdk.testing.exporter.InMemoryMetricReader; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; @@ -941,8 +948,6 @@ void stringRepresentation() throws IOException, CertificateEncodingException { assertThat(telemetryExporter.unwrap().toString()) .matches( "OtlpHttp[a-zA-Z]*Exporter\\{" - + "exporterName=otlp, " - + "type=[a-zA_Z]*, " + "endpoint=http://localhost:4318/v1/[a-zA-Z]*, " + "timeoutNanos=" + TimeUnit.SECONDS.toNanos(10) @@ -983,8 +988,6 @@ void stringRepresentation() throws IOException, CertificateEncodingException { assertThat(telemetryExporter.unwrap().toString()) .matches( "OtlpHttp[a-zA-Z]*Exporter\\{" - + "exporterName=otlp, " - + "type=[a-zA_Z]*, " + "endpoint=http://example:4318/v1/[a-zA-Z]*, " + "timeoutNanos=" + TimeUnit.SECONDS.toNanos(5) @@ -1004,6 +1007,120 @@ void stringRepresentation() throws IOException, CertificateEncodingException { } } + @Test + void latestInternalTelemetry() { + InMemoryMetricReader inMemoryMetrics = InMemoryMetricReader.create(); + try (SdkMeterProvider meterProvider = + SdkMeterProvider.builder().registerMetricReader(inMemoryMetrics).build()) { + + TelemetryExporter exporter = + exporterBuilder() + .setEndpoint(server.httpUri() + path) + .setMeterProvider(() -> meterProvider) + .setInternalTelemetryVersion(InternalTelemetryVersion.LATEST) + .build(); + + List telemetry = Collections.singletonList(generateFakeTelemetry()); + assertThat(exporter.export(telemetry).join(10, TimeUnit.SECONDS).isSuccess()).isTrue(); + + List expectedAttributes = + Arrays.asList( + satisfies( + SemConvAttributes.OTEL_COMPONENT_TYPE, + str -> str.matches("otlp_http_(log|metric|span)_exporter")), + satisfies( + SemConvAttributes.OTEL_COMPONENT_NAME, + str -> str.matches("otlp_http_(log|metric|span)_exporter/\\d+")), + satisfies( + SemConvAttributes.SERVER_PORT, str -> str.isEqualTo(server.httpUri().getPort())), + satisfies( + SemConvAttributes.SERVER_ADDRESS, + str -> str.isEqualTo(server.httpUri().getHost()))); + + assertThat(inMemoryMetrics.collectAllMetrics()) + .hasSize(3) + .anySatisfy( + metric -> + assertThat(metric) + .satisfies( + m -> + assertThat(m.getName()) + .matches( + "otel.sdk.exporter.(span|metric_data_point|log).inflight")) + .hasLongSumSatisfying( + ma -> + ma.hasPointsSatisfying( + pa -> pa.hasAttributesSatisfying(expectedAttributes)))) + .anySatisfy( + metric -> + assertThat(metric) + .satisfies( + m -> + assertThat(m.getName()) + .matches( + "otel.sdk.exporter.(span|metric_data_point|log).exported")) + .hasLongSumSatisfying( + ma -> + ma.hasPointsSatisfying( + pa -> pa.hasAttributesSatisfying(expectedAttributes)))) + .anySatisfy( + metric -> + assertThat(metric) + .hasName("otel.sdk.exporter.operation.duration") + .hasHistogramSatisfying( + ma -> + ma.hasPointsSatisfying( + pa -> + pa.hasAttributesSatisfying(expectedAttributes) + .hasBucketCounts(1)))); + } + } + + @Test + void legacyInternalTelemetry() { + InMemoryMetricReader inMemoryMetrics = InMemoryMetricReader.create(); + try (SdkMeterProvider meterProvider = + SdkMeterProvider.builder().registerMetricReader(inMemoryMetrics).build()) { + + TelemetryExporter exporter = + exporterBuilder() + .setEndpoint(server.httpUri() + path) + .setMeterProvider(() -> meterProvider) + .setInternalTelemetryVersion(InternalTelemetryVersion.LEGACY) + .build(); + + List telemetry = Collections.singletonList(generateFakeTelemetry()); + assertThat(exporter.export(telemetry).join(10, TimeUnit.SECONDS).isSuccess()).isTrue(); + + assertThat(inMemoryMetrics.collectAllMetrics()) + .hasSize(2) + .anySatisfy( + metric -> + assertThat(metric) + .hasName("otlp.exporter.seen") + .hasLongSumSatisfying( + ma -> + ma.hasPointsSatisfying( + pa -> + pa.hasAttributesSatisfying( + satisfies( + stringKey("type"), + str -> str.matches("log|span|metric")))))) + .anySatisfy( + metric -> + assertThat(metric) + .hasName("otlp.exporter.exported") + .hasLongSumSatisfying( + ma -> + ma.hasPointsSatisfying( + pa -> + pa.hasAttributesSatisfying( + satisfies( + stringKey("type"), + str -> str.matches("log|span|metric")))))); + } + } + protected abstract TelemetryExporterBuilder exporterBuilder(); protected abstract TelemetryExporterBuilder toBuilder(TelemetryExporter exporter); diff --git a/exporters/otlp/testing-internal/src/main/java/io/opentelemetry/exporter/otlp/testing/internal/GrpcLogRecordExporterBuilderWrapper.java b/exporters/otlp/testing-internal/src/main/java/io/opentelemetry/exporter/otlp/testing/internal/GrpcLogRecordExporterBuilderWrapper.java index e0a2da5c767..4ec4f920184 100644 --- a/exporters/otlp/testing-internal/src/main/java/io/opentelemetry/exporter/otlp/testing/internal/GrpcLogRecordExporterBuilderWrapper.java +++ b/exporters/otlp/testing-internal/src/main/java/io/opentelemetry/exporter/otlp/testing/internal/GrpcLogRecordExporterBuilderWrapper.java @@ -6,7 +6,9 @@ package io.opentelemetry.exporter.otlp.testing.internal; import io.grpc.ManagedChannel; +import io.opentelemetry.api.metrics.MeterProvider; import io.opentelemetry.exporter.otlp.logs.OtlpGrpcLogRecordExporterBuilder; +import io.opentelemetry.sdk.common.InternalTelemetryVersion; import io.opentelemetry.sdk.common.export.ProxyOptions; import io.opentelemetry.sdk.common.export.RetryPolicy; import io.opentelemetry.sdk.logs.data.LogRecordData; @@ -127,6 +129,20 @@ public TelemetryExporterBuilder setExecutorService( return this; } + @Override + public TelemetryExporterBuilder setMeterProvider( + Supplier meterProviderSupplier) { + builder.setMeterProvider(meterProviderSupplier); + return this; + } + + @Override + public TelemetryExporterBuilder setInternalTelemetryVersion( + InternalTelemetryVersion schemaVersion) { + builder.setInternalTelemetryVersion(schemaVersion); + return this; + } + @Override public TelemetryExporter build() { return TelemetryExporter.wrap(builder.build()); diff --git a/exporters/otlp/testing-internal/src/main/java/io/opentelemetry/exporter/otlp/testing/internal/GrpcMetricExporterBuilderWrapper.java b/exporters/otlp/testing-internal/src/main/java/io/opentelemetry/exporter/otlp/testing/internal/GrpcMetricExporterBuilderWrapper.java index 09bd0a339be..873c1e511b8 100644 --- a/exporters/otlp/testing-internal/src/main/java/io/opentelemetry/exporter/otlp/testing/internal/GrpcMetricExporterBuilderWrapper.java +++ b/exporters/otlp/testing-internal/src/main/java/io/opentelemetry/exporter/otlp/testing/internal/GrpcMetricExporterBuilderWrapper.java @@ -6,7 +6,9 @@ package io.opentelemetry.exporter.otlp.testing.internal; import io.grpc.ManagedChannel; +import io.opentelemetry.api.metrics.MeterProvider; import io.opentelemetry.exporter.otlp.metrics.OtlpGrpcMetricExporterBuilder; +import io.opentelemetry.sdk.common.InternalTelemetryVersion; import io.opentelemetry.sdk.common.export.ProxyOptions; import io.opentelemetry.sdk.common.export.RetryPolicy; import io.opentelemetry.sdk.metrics.data.MetricData; @@ -126,6 +128,20 @@ public TelemetryExporterBuilder setExecutorService(ExecutorService e return this; } + @Override + public TelemetryExporterBuilder setMeterProvider( + Supplier meterProviderSupplier) { + builder.setMeterProvider(meterProviderSupplier); + return this; + } + + @Override + public TelemetryExporterBuilder setInternalTelemetryVersion( + InternalTelemetryVersion schemaVersion) { + builder.setInternalTelemetryVersion(schemaVersion); + return this; + } + @Override public TelemetryExporter build() { return TelemetryExporter.wrap(builder.build()); diff --git a/exporters/otlp/testing-internal/src/main/java/io/opentelemetry/exporter/otlp/testing/internal/GrpcProfilesExporterBuilderWrapper.java b/exporters/otlp/testing-internal/src/main/java/io/opentelemetry/exporter/otlp/testing/internal/GrpcProfilesExporterBuilderWrapper.java index 1adf15eddb2..f5f47692bde 100644 --- a/exporters/otlp/testing-internal/src/main/java/io/opentelemetry/exporter/otlp/testing/internal/GrpcProfilesExporterBuilderWrapper.java +++ b/exporters/otlp/testing-internal/src/main/java/io/opentelemetry/exporter/otlp/testing/internal/GrpcProfilesExporterBuilderWrapper.java @@ -6,8 +6,10 @@ package io.opentelemetry.exporter.otlp.testing.internal; import io.grpc.ManagedChannel; +import io.opentelemetry.api.metrics.MeterProvider; import io.opentelemetry.exporter.otlp.profiles.OtlpGrpcProfilesExporterBuilder; import io.opentelemetry.exporter.otlp.profiles.ProfileData; +import io.opentelemetry.sdk.common.InternalTelemetryVersion; import io.opentelemetry.sdk.common.export.ProxyOptions; import io.opentelemetry.sdk.common.export.RetryPolicy; import java.time.Duration; @@ -126,6 +128,20 @@ public TelemetryExporterBuilder setExecutorService(ExecutorService return this; } + @Override + public TelemetryExporterBuilder setMeterProvider( + Supplier meterProviderSupplier) { + // Not yet supported + return this; + } + + @Override + public TelemetryExporterBuilder setInternalTelemetryVersion( + InternalTelemetryVersion schemaVersion) { + // Not yet supported + return this; + } + @Override public TelemetryExporter build() { return TelemetryExporter.wrap(builder.build()); diff --git a/exporters/otlp/testing-internal/src/main/java/io/opentelemetry/exporter/otlp/testing/internal/GrpcSpanExporterBuilderWrapper.java b/exporters/otlp/testing-internal/src/main/java/io/opentelemetry/exporter/otlp/testing/internal/GrpcSpanExporterBuilderWrapper.java index 0ddb5e46d85..8a1afe962f7 100644 --- a/exporters/otlp/testing-internal/src/main/java/io/opentelemetry/exporter/otlp/testing/internal/GrpcSpanExporterBuilderWrapper.java +++ b/exporters/otlp/testing-internal/src/main/java/io/opentelemetry/exporter/otlp/testing/internal/GrpcSpanExporterBuilderWrapper.java @@ -6,7 +6,9 @@ package io.opentelemetry.exporter.otlp.testing.internal; import io.grpc.ManagedChannel; +import io.opentelemetry.api.metrics.MeterProvider; import io.opentelemetry.exporter.otlp.trace.OtlpGrpcSpanExporterBuilder; +import io.opentelemetry.sdk.common.InternalTelemetryVersion; import io.opentelemetry.sdk.common.export.ProxyOptions; import io.opentelemetry.sdk.common.export.RetryPolicy; import io.opentelemetry.sdk.trace.data.SpanData; @@ -126,6 +128,20 @@ public TelemetryExporterBuilder setExecutorService(ExecutorService exe return this; } + @Override + public TelemetryExporterBuilder setMeterProvider( + Supplier meterProviderSupplier) { + builder.setMeterProvider(meterProviderSupplier); + return this; + } + + @Override + public TelemetryExporterBuilder setInternalTelemetryVersion( + InternalTelemetryVersion schemaVersion) { + builder.setInternalTelemetryVersion(schemaVersion); + return this; + } + @Override public TelemetryExporter build() { return TelemetryExporter.wrap(builder.build()); diff --git a/exporters/otlp/testing-internal/src/main/java/io/opentelemetry/exporter/otlp/testing/internal/HttpLogRecordExporterBuilderWrapper.java b/exporters/otlp/testing-internal/src/main/java/io/opentelemetry/exporter/otlp/testing/internal/HttpLogRecordExporterBuilderWrapper.java index 0060bde1d16..cfd9655843d 100644 --- a/exporters/otlp/testing-internal/src/main/java/io/opentelemetry/exporter/otlp/testing/internal/HttpLogRecordExporterBuilderWrapper.java +++ b/exporters/otlp/testing-internal/src/main/java/io/opentelemetry/exporter/otlp/testing/internal/HttpLogRecordExporterBuilderWrapper.java @@ -5,7 +5,9 @@ package io.opentelemetry.exporter.otlp.testing.internal; +import io.opentelemetry.api.metrics.MeterProvider; import io.opentelemetry.exporter.otlp.http.logs.OtlpHttpLogRecordExporterBuilder; +import io.opentelemetry.sdk.common.InternalTelemetryVersion; import io.opentelemetry.sdk.common.export.ProxyOptions; import io.opentelemetry.sdk.common.export.RetryPolicy; import io.opentelemetry.sdk.logs.data.LogRecordData; @@ -127,6 +129,20 @@ public TelemetryExporterBuilder setExecutorService( return this; } + @Override + public TelemetryExporterBuilder setMeterProvider( + Supplier meterProviderSupplier) { + builder.setMeterProvider(meterProviderSupplier); + return this; + } + + @Override + public TelemetryExporterBuilder setInternalTelemetryVersion( + InternalTelemetryVersion schemaVersion) { + builder.setInternalTelemetryVersion(schemaVersion); + return this; + } + @Override public TelemetryExporter build() { return TelemetryExporter.wrap(builder.build()); diff --git a/exporters/otlp/testing-internal/src/main/java/io/opentelemetry/exporter/otlp/testing/internal/HttpMetricExporterBuilderWrapper.java b/exporters/otlp/testing-internal/src/main/java/io/opentelemetry/exporter/otlp/testing/internal/HttpMetricExporterBuilderWrapper.java index f9a9f87c7ae..f30c1068d4c 100644 --- a/exporters/otlp/testing-internal/src/main/java/io/opentelemetry/exporter/otlp/testing/internal/HttpMetricExporterBuilderWrapper.java +++ b/exporters/otlp/testing-internal/src/main/java/io/opentelemetry/exporter/otlp/testing/internal/HttpMetricExporterBuilderWrapper.java @@ -5,7 +5,9 @@ package io.opentelemetry.exporter.otlp.testing.internal; +import io.opentelemetry.api.metrics.MeterProvider; import io.opentelemetry.exporter.otlp.http.metrics.OtlpHttpMetricExporterBuilder; +import io.opentelemetry.sdk.common.InternalTelemetryVersion; import io.opentelemetry.sdk.common.export.ProxyOptions; import io.opentelemetry.sdk.common.export.RetryPolicy; import io.opentelemetry.sdk.metrics.data.MetricData; @@ -125,6 +127,20 @@ public TelemetryExporterBuilder setExecutorService(ExecutorService e return this; } + @Override + public TelemetryExporterBuilder setMeterProvider( + Supplier meterProviderSupplier) { + builder.setMeterProvider(meterProviderSupplier); + return this; + } + + @Override + public TelemetryExporterBuilder setInternalTelemetryVersion( + InternalTelemetryVersion schemaVersion) { + builder.setInternalTelemetryVersion(schemaVersion); + return this; + } + @Override public TelemetryExporter build() { return TelemetryExporter.wrap(builder.build()); diff --git a/exporters/otlp/testing-internal/src/main/java/io/opentelemetry/exporter/otlp/testing/internal/HttpSpanExporterBuilderWrapper.java b/exporters/otlp/testing-internal/src/main/java/io/opentelemetry/exporter/otlp/testing/internal/HttpSpanExporterBuilderWrapper.java index 7bdca0f20e8..82c64f9451f 100644 --- a/exporters/otlp/testing-internal/src/main/java/io/opentelemetry/exporter/otlp/testing/internal/HttpSpanExporterBuilderWrapper.java +++ b/exporters/otlp/testing-internal/src/main/java/io/opentelemetry/exporter/otlp/testing/internal/HttpSpanExporterBuilderWrapper.java @@ -5,7 +5,9 @@ package io.opentelemetry.exporter.otlp.testing.internal; +import io.opentelemetry.api.metrics.MeterProvider; import io.opentelemetry.exporter.otlp.http.trace.OtlpHttpSpanExporterBuilder; +import io.opentelemetry.sdk.common.InternalTelemetryVersion; import io.opentelemetry.sdk.common.export.ProxyOptions; import io.opentelemetry.sdk.common.export.RetryPolicy; import io.opentelemetry.sdk.trace.data.SpanData; @@ -124,6 +126,20 @@ public TelemetryExporterBuilder setExecutorService(ExecutorService exe return this; } + @Override + public TelemetryExporterBuilder setMeterProvider( + Supplier meterProviderSupplier) { + builder.setMeterProvider(meterProviderSupplier); + return this; + } + + @Override + public TelemetryExporterBuilder setInternalTelemetryVersion( + InternalTelemetryVersion schemaVersion) { + builder.setInternalTelemetryVersion(schemaVersion); + return this; + } + @Override public TelemetryExporter build() { return TelemetryExporter.wrap(builder.build()); diff --git a/exporters/otlp/testing-internal/src/main/java/io/opentelemetry/exporter/otlp/testing/internal/ManagedChannelTelemetryExporterBuilder.java b/exporters/otlp/testing-internal/src/main/java/io/opentelemetry/exporter/otlp/testing/internal/ManagedChannelTelemetryExporterBuilder.java index 8b080cfbfda..5e37fa7df2a 100644 --- a/exporters/otlp/testing-internal/src/main/java/io/opentelemetry/exporter/otlp/testing/internal/ManagedChannelTelemetryExporterBuilder.java +++ b/exporters/otlp/testing-internal/src/main/java/io/opentelemetry/exporter/otlp/testing/internal/ManagedChannelTelemetryExporterBuilder.java @@ -12,10 +12,12 @@ import io.grpc.netty.GrpcSslContexts; import io.grpc.netty.NettyChannelBuilder; import io.netty.handler.ssl.SslContext; +import io.opentelemetry.api.metrics.MeterProvider; import io.opentelemetry.exporter.internal.TlsConfigHelper; import io.opentelemetry.exporter.internal.grpc.ManagedChannelUtil; import io.opentelemetry.exporter.otlp.internal.OtlpUserAgent; import io.opentelemetry.sdk.common.CompletableResultCode; +import io.opentelemetry.sdk.common.InternalTelemetryVersion; import io.opentelemetry.sdk.common.export.ProxyOptions; import io.opentelemetry.sdk.common.export.RetryPolicy; import java.net.URI; @@ -172,6 +174,20 @@ public TelemetryExporterBuilder setExecutorService(ExecutorService executorSe return this; } + @Override + public TelemetryExporterBuilder setMeterProvider( + Supplier meterProviderSupplier) { + delegate.setMeterProvider(meterProviderSupplier); + return this; + } + + @Override + public TelemetryExporterBuilder setInternalTelemetryVersion( + InternalTelemetryVersion schemaVersion) { + delegate.setInternalTelemetryVersion(schemaVersion); + return this; + } + @Override public TelemetryExporter build() { Runnable shutdownCallback; diff --git a/exporters/otlp/testing-internal/src/main/java/io/opentelemetry/exporter/otlp/testing/internal/TelemetryExporterBuilder.java b/exporters/otlp/testing-internal/src/main/java/io/opentelemetry/exporter/otlp/testing/internal/TelemetryExporterBuilder.java index 4283f361226..c00886e9127 100644 --- a/exporters/otlp/testing-internal/src/main/java/io/opentelemetry/exporter/otlp/testing/internal/TelemetryExporterBuilder.java +++ b/exporters/otlp/testing-internal/src/main/java/io/opentelemetry/exporter/otlp/testing/internal/TelemetryExporterBuilder.java @@ -5,11 +5,13 @@ package io.opentelemetry.exporter.otlp.testing.internal; +import io.opentelemetry.api.metrics.MeterProvider; import io.opentelemetry.exporter.otlp.logs.OtlpGrpcLogRecordExporterBuilder; import io.opentelemetry.exporter.otlp.metrics.OtlpGrpcMetricExporterBuilder; import io.opentelemetry.exporter.otlp.profiles.OtlpGrpcProfilesExporterBuilder; import io.opentelemetry.exporter.otlp.profiles.ProfileData; import io.opentelemetry.exporter.otlp.trace.OtlpGrpcSpanExporterBuilder; +import io.opentelemetry.sdk.common.InternalTelemetryVersion; import io.opentelemetry.sdk.common.export.ProxyOptions; import io.opentelemetry.sdk.common.export.RetryPolicy; import io.opentelemetry.sdk.logs.data.LogRecordData; @@ -74,5 +76,9 @@ static TelemetryExporterBuilder wrap(OtlpGrpcProfilesExporterBuilde TelemetryExporterBuilder setExecutorService(ExecutorService executorService); + TelemetryExporterBuilder setMeterProvider(Supplier meterProviderSupplier); + + TelemetryExporterBuilder setInternalTelemetryVersion(InternalTelemetryVersion schemaVersion); + TelemetryExporter build(); } diff --git a/exporters/zipkin/src/main/java/io/opentelemetry/exporter/zipkin/ZipkinSpanExporter.java b/exporters/zipkin/src/main/java/io/opentelemetry/exporter/zipkin/ZipkinSpanExporter.java index a76d3177fd4..afd7bce4286 100644 --- a/exporters/zipkin/src/main/java/io/opentelemetry/exporter/zipkin/ZipkinSpanExporter.java +++ b/exporters/zipkin/src/main/java/io/opentelemetry/exporter/zipkin/ZipkinSpanExporter.java @@ -7,8 +7,11 @@ import io.opentelemetry.api.internal.InstrumentationUtil; import io.opentelemetry.api.metrics.MeterProvider; -import io.opentelemetry.exporter.internal.ExporterMetrics; +import io.opentelemetry.exporter.internal.metrics.ExporterInstrumentation; import io.opentelemetry.sdk.common.CompletableResultCode; +import io.opentelemetry.sdk.common.InternalTelemetryVersion; +import io.opentelemetry.sdk.internal.ComponentId; +import io.opentelemetry.sdk.internal.StandardComponentId; import io.opentelemetry.sdk.internal.ThrottlingLogger; import io.opentelemetry.sdk.trace.data.SpanData; import io.opentelemetry.sdk.trace.export.SpanExporter; @@ -41,7 +44,7 @@ public final class ZipkinSpanExporter implements SpanExporter { private final ZipkinSpanExporterBuilder builder; private final BytesEncoder encoder; private final BytesMessageSender sender; - private final ExporterMetrics exporterMetrics; + private final ExporterInstrumentation exporterMetrics; private final OtelToZipkinSpanTransformer transformer; @@ -50,15 +53,26 @@ public final class ZipkinSpanExporter implements SpanExporter { BytesEncoder encoder, BytesMessageSender sender, Supplier meterProviderSupplier, + InternalTelemetryVersion internalTelemetryVersion, + String endpoint, OtelToZipkinSpanTransformer transformer) { this.builder = builder; this.encoder = encoder; this.sender = sender; - this.exporterMetrics = - sender.encoding() == Encoding.JSON - ? ExporterMetrics.createHttpJson("zipkin", "span", meterProviderSupplier) - : ExporterMetrics.createHttpProtobuf("zipkin", "span", meterProviderSupplier); this.transformer = transformer; + + StandardComponentId.ExporterType exporterType; + if (sender.encoding() == Encoding.JSON) { + exporterType = StandardComponentId.ExporterType.ZIPKIN_HTTP_JSON_SPAN_EXPORTER; + } else { + exporterType = StandardComponentId.ExporterType.ZIPKIN_HTTP_SPAN_EXPORTER; + } + this.exporterMetrics = + new ExporterInstrumentation( + internalTelemetryVersion, + meterProviderSupplier, + ComponentId.generateLazy(exporterType), + endpoint); } @Override @@ -68,7 +82,8 @@ public CompletableResultCode export(Collection spanDataList) { } int numItems = spanDataList.size(); - exporterMetrics.addSeen(numItems); + ExporterInstrumentation.Recording metricRecording = + exporterMetrics.startRecordingExport(numItems); List encodedSpans = new ArrayList<>(numItems); for (SpanData spanData : spanDataList) { @@ -81,10 +96,10 @@ public CompletableResultCode export(Collection spanDataList) { () -> { try { sender.send(encodedSpans); - exporterMetrics.addSuccess(numItems); + metricRecording.finishSuccessful(); resultCode.succeed(); } catch (IOException | RuntimeException e) { - exporterMetrics.addFailed(numItems); + metricRecording.finishFailed(e); logger.log(Level.WARNING, "Failed to export spans", e); resultCode.fail(); } diff --git a/exporters/zipkin/src/main/java/io/opentelemetry/exporter/zipkin/ZipkinSpanExporterBuilder.java b/exporters/zipkin/src/main/java/io/opentelemetry/exporter/zipkin/ZipkinSpanExporterBuilder.java index 0f1fae95b9a..c0773de041a 100644 --- a/exporters/zipkin/src/main/java/io/opentelemetry/exporter/zipkin/ZipkinSpanExporterBuilder.java +++ b/exporters/zipkin/src/main/java/io/opentelemetry/exporter/zipkin/ZipkinSpanExporterBuilder.java @@ -10,6 +10,7 @@ import io.opentelemetry.api.GlobalOpenTelemetry; import io.opentelemetry.api.metrics.MeterProvider; +import io.opentelemetry.sdk.common.InternalTelemetryVersion; import java.net.InetAddress; import java.time.Duration; import java.util.StringJoiner; @@ -33,6 +34,7 @@ public final class ZipkinSpanExporterBuilder { private boolean compressionEnabled = true; private int readTimeoutMillis = (int) TimeUnit.SECONDS.toMillis(10); private Supplier meterProviderSupplier = GlobalOpenTelemetry::getMeterProvider; + private InternalTelemetryVersion internalTelemetryVersion = InternalTelemetryVersion.LEGACY; /** * Sets the Zipkin sender. Implements the client side of the span transport. An {@link @@ -186,6 +188,16 @@ public ZipkinSpanExporterBuilder setMeterProvider(MeterProvider meterProvider) { return this; } + /** + * Sets the {@link InternalTelemetryVersion} defining which self-monitoring metrics this exporter + * collects. + */ + public ZipkinSpanExporterBuilder setInternalTelemetryVersion(InternalTelemetryVersion level) { + requireNonNull(level, "level"); + this.internalTelemetryVersion = level; + return this; + } + String toString(boolean includePrefixAndSuffix) { StringJoiner joiner = includePrefixAndSuffix @@ -194,6 +206,7 @@ String toString(boolean includePrefixAndSuffix) { joiner.add("endpoint=" + endpoint); joiner.add("compressionEnabled=" + compressionEnabled); joiner.add("readTimeoutMillis=" + readTimeoutMillis); + joiner.add("internalTelemetrySchemaVersion=" + internalTelemetryVersion); // Note: omit sender because we can't log the configuration in any readable way // Note: omit encoder because we can't log the configuration in any readable way // Note: omit localIpAddressSupplier because we can't log the configuration in any readable way @@ -218,6 +231,13 @@ public ZipkinSpanExporter build() { } OtelToZipkinSpanTransformer transformer = OtelToZipkinSpanTransformer.create(localIpAddressSupplier); - return new ZipkinSpanExporter(this, encoder, sender, meterProviderSupplier, transformer); + return new ZipkinSpanExporter( + this, + encoder, + sender, + meterProviderSupplier, + internalTelemetryVersion, + endpoint, + transformer); } } diff --git a/exporters/zipkin/src/test/java/io/opentelemetry/exporter/zipkin/ZipkinSpanExporterTest.java b/exporters/zipkin/src/test/java/io/opentelemetry/exporter/zipkin/ZipkinSpanExporterTest.java index b89c273bbbb..759cfae782b 100644 --- a/exporters/zipkin/src/test/java/io/opentelemetry/exporter/zipkin/ZipkinSpanExporterTest.java +++ b/exporters/zipkin/src/test/java/io/opentelemetry/exporter/zipkin/ZipkinSpanExporterTest.java @@ -20,6 +20,7 @@ import io.opentelemetry.context.Context; import io.opentelemetry.internal.testing.slf4j.SuppressLogger; import io.opentelemetry.sdk.common.CompletableResultCode; +import io.opentelemetry.sdk.common.InternalTelemetryVersion; import io.opentelemetry.sdk.testing.trace.TestSpanData; import java.io.IOException; import java.net.InetAddress; @@ -60,6 +61,8 @@ void testExport() throws IOException { mockEncoder, mockSender, MeterProvider::noop, + InternalTelemetryVersion.LATEST, + "http://testing:1234", mockTransformer); byte[] someBytes = new byte[0]; @@ -89,6 +92,8 @@ void testExport_failed() throws IOException { mockEncoder, mockSender, MeterProvider::noop, + InternalTelemetryVersion.LATEST, + "http://testing:1234", mockTransformer); byte[] someBytes = new byte[0]; @@ -251,7 +256,7 @@ void stringRepresentation() { try (ZipkinSpanExporter exporter = ZipkinSpanExporter.builder().build()) { assertThat(exporter.toString()) .isEqualTo( - "ZipkinSpanExporter{endpoint=http://localhost:9411/api/v2/spans, compressionEnabled=true, readTimeoutMillis=10000}"); + "ZipkinSpanExporter{endpoint=http://localhost:9411/api/v2/spans, compressionEnabled=true, readTimeoutMillis=10000, internalTelemetrySchemaVersion=LEGACY}"); } try (ZipkinSpanExporter exporter = ZipkinSpanExporter.builder() @@ -261,7 +266,7 @@ void stringRepresentation() { .build()) { assertThat(exporter.toString()) .isEqualTo( - "ZipkinSpanExporter{endpoint=http://zipkin:9411/api/v2/spans, compressionEnabled=false, readTimeoutMillis=15000}"); + "ZipkinSpanExporter{endpoint=http://zipkin:9411/api/v2/spans, compressionEnabled=false, readTimeoutMillis=15000, internalTelemetrySchemaVersion=LEGACY}"); } } @@ -276,6 +281,8 @@ void suppressInstrumentation() { mockEncoder, suppressCatchingSender, MeterProvider::noop, + InternalTelemetryVersion.LATEST, + "http://testing:1234", mockTransformer); byte[] someBytes = new byte[0]; diff --git a/sdk/common/build.gradle.kts b/sdk/common/build.gradle.kts index f369ae1e84f..8f6f3095354 100644 --- a/sdk/common/build.gradle.kts +++ b/sdk/common/build.gradle.kts @@ -20,6 +20,7 @@ dependencies { testImplementation(project(":sdk:testing")) testImplementation("com.google.guava:guava-testlib") + testImplementation("io.opentelemetry.semconv:opentelemetry-semconv-incubating") } tasks { diff --git a/sdk/common/src/main/java/io/opentelemetry/sdk/common/InternalTelemetryVersion.java b/sdk/common/src/main/java/io/opentelemetry/sdk/common/InternalTelemetryVersion.java new file mode 100644 index 00000000000..f50016b8e3b --- /dev/null +++ b/sdk/common/src/main/java/io/opentelemetry/sdk/common/InternalTelemetryVersion.java @@ -0,0 +1,20 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.sdk.common; + +/** Defines the self-monitoring telemetry SDK components should capture. */ +public enum InternalTelemetryVersion { + /** + * Record self-monitoring metrics defined in the SDK prior the standardization in semantic + * conventions. + */ + LEGACY, + /** + * Record self-monitoring metrics defined in the latest semantic conventions version supported by + * this SDK version. + */ + LATEST +} diff --git a/sdk/common/src/main/java/io/opentelemetry/sdk/internal/ComponentId.java b/sdk/common/src/main/java/io/opentelemetry/sdk/internal/ComponentId.java new file mode 100644 index 00000000000..13621ffc5d0 --- /dev/null +++ b/sdk/common/src/main/java/io/opentelemetry/sdk/internal/ComponentId.java @@ -0,0 +1,68 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.sdk.internal; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicInteger; +import javax.annotation.Nullable; + +/** + * The component id used for SDK health metrics. This corresponds to the otel.component.name and + * otel.component.id semconv attributes. + * + *

This class is internal and is hence not for public use. Its APIs are unstable and can change + * at any time. + */ +public abstract class ComponentId { + + private ComponentId() {} + + public abstract String getTypeName(); + + public abstract String getComponentName(); + + static class Lazy extends ComponentId { + + private static final Map nextIdCounters = new ConcurrentHashMap<>(); + + private final String componentType; + @Nullable private volatile String componentName = null; + + Lazy(String componentType) { + this.componentType = componentType; + } + + @Override + public String getTypeName() { + return componentType; + } + + @Override + public String getComponentName() { + if (componentName == null) { + synchronized (this) { + if (componentName == null) { + int id = + nextIdCounters + .computeIfAbsent(componentType, k -> new AtomicInteger(0)) + .getAndIncrement(); + componentName = componentType + "/" + id; + } + } + } + return componentName; + } + } + + public static ComponentId generateLazy(String componentType) { + return new Lazy(componentType); + } + + public static StandardComponentId generateLazy(StandardComponentId.ExporterType exporterType) { + return new StandardComponentId(exporterType); + } +} diff --git a/sdk/common/src/main/java/io/opentelemetry/sdk/internal/SemConvAttributes.java b/sdk/common/src/main/java/io/opentelemetry/sdk/internal/SemConvAttributes.java new file mode 100644 index 00000000000..1075d537604 --- /dev/null +++ b/sdk/common/src/main/java/io/opentelemetry/sdk/internal/SemConvAttributes.java @@ -0,0 +1,36 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.sdk.internal; + +import io.opentelemetry.api.common.AttributeKey; + +/** + * Provides access to semantic convention attributes used within the SDK implementation. This avoids + * having to pull in semantic conventions as a dependency, which would easily collide and conflict + * with user-provided dependencies. + * + *

This class is internal and is hence not for public use. Its APIs are unstable and can change + * at any time. + */ +public class SemConvAttributes { + + private SemConvAttributes() {} + + public static final AttributeKey OTEL_COMPONENT_TYPE = + AttributeKey.stringKey("otel.component.type"); + public static final AttributeKey OTEL_COMPONENT_NAME = + AttributeKey.stringKey("otel.component.name"); + public static final AttributeKey ERROR_TYPE = AttributeKey.stringKey("error.type"); + + public static final AttributeKey SERVER_ADDRESS = + AttributeKey.stringKey("server.address"); + public static final AttributeKey SERVER_PORT = AttributeKey.longKey("server.port"); + + public static final AttributeKey RPC_GRPC_STATUS_CODE = + AttributeKey.longKey("rpc.grpc.status_code"); + public static final AttributeKey HTTP_RESPONSE_STATUS_CODE = + AttributeKey.longKey("http.response.status_code"); +} diff --git a/sdk/common/src/main/java/io/opentelemetry/sdk/internal/Signal.java b/sdk/common/src/main/java/io/opentelemetry/sdk/internal/Signal.java new file mode 100644 index 00000000000..0a677ee3cd2 --- /dev/null +++ b/sdk/common/src/main/java/io/opentelemetry/sdk/internal/Signal.java @@ -0,0 +1,39 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.sdk.internal; + +import java.util.Locale; + +/** + * This class is internal and is hence not for public use. Its APIs are unstable and can change at + * any time. + */ +public enum Signal { + SPAN("otel.sdk.exporter.span", "span"), + METRIC("otel.sdk.exporter.metric_data_point", "data_point"), + LOG("otel.sdk.exporter.log", "log_record"), + PROFILE("TBD", "TBD"); + + private final String exporterMetricNamespace; + private final String metricUnit; + + Signal(String exporterMetricNamespace, String metricUnit) { + this.exporterMetricNamespace = exporterMetricNamespace; + this.metricUnit = metricUnit; + } + + public String logFriendlyName() { + return name().toLowerCase(Locale.ENGLISH); + } + + public String getExporterMetricNamespace() { + return exporterMetricNamespace; + } + + public String getMetricUnit() { + return metricUnit; + } +} diff --git a/sdk/common/src/main/java/io/opentelemetry/sdk/internal/StandardComponentId.java b/sdk/common/src/main/java/io/opentelemetry/sdk/internal/StandardComponentId.java new file mode 100644 index 00000000000..d7396d82879 --- /dev/null +++ b/sdk/common/src/main/java/io/opentelemetry/sdk/internal/StandardComponentId.java @@ -0,0 +1,62 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.sdk.internal; + +/** + * A {@link ComponentId} where the component type is one of {@link ExporterType}. + * + *

This class is internal and is hence not for public use. Its APIs are unstable and can change + * at any time. + */ +public class StandardComponentId extends ComponentId.Lazy { + + /** + * This class is internal and is hence not for public use. Its APIs are unstable and can change at + * any time. + */ + public enum ExporterType { + OTLP_GRPC_SPAN_EXPORTER("otlp_grpc_span_exporter", Signal.SPAN), + OTLP_HTTP_SPAN_EXPORTER("otlp_http_span_exporter", Signal.SPAN), + OTLP_HTTP_JSON_SPAN_EXPORTER("otlp_http_json_span_exporter", Signal.SPAN), + OTLP_GRPC_LOG_EXPORTER("otlp_grpc_log_exporter", Signal.LOG), + OTLP_HTTP_LOG_EXPORTER("otlp_http_log_exporter", Signal.LOG), + OTLP_HTTP_JSON_LOG_EXPORTER("otlp_http_json_log_exporter", Signal.LOG), + OTLP_GRPC_METRIC_EXPORTER("otlp_grpc_metric_exporter", Signal.METRIC), + OTLP_HTTP_METRIC_EXPORTER("otlp_http_metric_exporter", Signal.METRIC), + OTLP_HTTP_JSON_METRIC_EXPORTER("otlp_http_json_metric_exporter", Signal.METRIC), + ZIPKIN_HTTP_SPAN_EXPORTER("zipkin_http_span_exporter", Signal.SPAN), + /** + * Has the same semconv attribute value as ZIPKIN_HTTP_SPAN_EXPORTER, but we still use a + * different enum value for now because they produce separate legacy metrics. + */ + ZIPKIN_HTTP_JSON_SPAN_EXPORTER("zipkin_http_span_exporter", Signal.SPAN), + + OTLP_GRPC_PROFILES_EXPORTER("TBD", Signal.PROFILE); // TODO: not yet standardized in semconv + + final String value; + private final Signal signal; + + ExporterType(String value, Signal signal) { + this.value = value; + this.signal = signal; + } + + public Signal signal() { + return signal; + } + } + + private final ExporterType standardType; + + StandardComponentId(ExporterType standardType) { + super(standardType.value); + this.standardType = standardType; + } + + public ExporterType getStandardType() { + return standardType; + } +} diff --git a/sdk/common/src/test/java/io/opentelemetry/sdk/internal/ComponentIdTest.java b/sdk/common/src/test/java/io/opentelemetry/sdk/internal/ComponentIdTest.java new file mode 100644 index 00000000000..27b06463040 --- /dev/null +++ b/sdk/common/src/test/java/io/opentelemetry/sdk/internal/ComponentIdTest.java @@ -0,0 +1,50 @@ +/* + * 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.semconv.incubating.OtelIncubatingAttributes; +import org.junit.jupiter.api.Test; + +class ComponentIdTest { + + @Test + void testStandardTypesUpToDate() { + assertThat(StandardComponentId.ExporterType.OTLP_GRPC_SPAN_EXPORTER.value) + .isEqualTo( + OtelIncubatingAttributes.OtelComponentTypeIncubatingValues.OTLP_GRPC_SPAN_EXPORTER); + assertThat(StandardComponentId.ExporterType.OTLP_HTTP_SPAN_EXPORTER.value) + .isEqualTo( + OtelIncubatingAttributes.OtelComponentTypeIncubatingValues.OTLP_HTTP_SPAN_EXPORTER); + assertThat(StandardComponentId.ExporterType.OTLP_HTTP_JSON_SPAN_EXPORTER.value) + .isEqualTo( + OtelIncubatingAttributes.OtelComponentTypeIncubatingValues + .OTLP_HTTP_JSON_SPAN_EXPORTER); + assertThat(StandardComponentId.ExporterType.OTLP_GRPC_LOG_EXPORTER.value) + .isEqualTo( + OtelIncubatingAttributes.OtelComponentTypeIncubatingValues.OTLP_GRPC_LOG_EXPORTER); + assertThat(StandardComponentId.ExporterType.OTLP_HTTP_LOG_EXPORTER.value) + .isEqualTo( + OtelIncubatingAttributes.OtelComponentTypeIncubatingValues.OTLP_HTTP_LOG_EXPORTER); + assertThat(StandardComponentId.ExporterType.OTLP_HTTP_JSON_LOG_EXPORTER.value) + .isEqualTo( + OtelIncubatingAttributes.OtelComponentTypeIncubatingValues.OTLP_HTTP_JSON_LOG_EXPORTER); + // TODO: uncomment as soon as available in semconv release + // assertThat(ComponentId.StandardType.OTLP_GRPC_METRIC_EXPORTER.value) + // + // .isEqualTo(OtelIncubatingAttributes.OtelComponentTypeIncubatingValues.OTLP_GRPC_METRIC_EXPORTER); + // assertThat(ComponentId.StandardType.OTLP_HTTP_METRIC_EXPORTER.value) + // + // .isEqualTo(OtelIncubatingAttributes.OtelComponentTypeIncubatingValues.OTLP_HTTP_METRIC_EXPORTER); + // assertThat(ComponentId.StandardType.OTLP_HTTP_JSON_METRIC_EXPORTER.value) + // + // .isEqualTo(OtelIncubatingAttributes.OtelComponentTypeIncubatingValues.OTLP_HTTP_JSON_METRIC_EXPORTER); + // assertThat(ComponentId.StandardType.ZIPKIN_HTTP_SPAN_EXPORTER.value) + // + // .isEqualTo(OtelIncubatingAttributes.OtelComponentTypeIncubatingValues.ZIPKIN_HTTP_SPAN_EXPORTER); + } +} diff --git a/sdk/common/src/test/java/io/opentelemetry/sdk/internal/SemConvAttributesTest.java b/sdk/common/src/test/java/io/opentelemetry/sdk/internal/SemConvAttributesTest.java new file mode 100644 index 00000000000..d4a5d5de1f5 --- /dev/null +++ b/sdk/common/src/test/java/io/opentelemetry/sdk/internal/SemConvAttributesTest.java @@ -0,0 +1,35 @@ +/* + * 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.semconv.ErrorAttributes; +import io.opentelemetry.semconv.HttpAttributes; +import io.opentelemetry.semconv.ServerAttributes; +import io.opentelemetry.semconv.incubating.OtelIncubatingAttributes; +import io.opentelemetry.semconv.incubating.RpcIncubatingAttributes; +import org.junit.jupiter.api.Test; + +class SemConvAttributesTest { + + @Test + void testAttributeKeys() { + assertThat(SemConvAttributes.OTEL_COMPONENT_NAME) + .isEqualTo(OtelIncubatingAttributes.OTEL_COMPONENT_NAME); + assertThat(SemConvAttributes.OTEL_COMPONENT_TYPE) + .isEqualTo(OtelIncubatingAttributes.OTEL_COMPONENT_TYPE); + + assertThat(SemConvAttributes.ERROR_TYPE).isEqualTo(ErrorAttributes.ERROR_TYPE); + + assertThat(SemConvAttributes.SERVER_ADDRESS).isEqualTo(ServerAttributes.SERVER_ADDRESS); + assertThat(SemConvAttributes.SERVER_PORT).isEqualTo(ServerAttributes.SERVER_PORT); + assertThat(SemConvAttributes.RPC_GRPC_STATUS_CODE) + .isEqualTo(RpcIncubatingAttributes.RPC_GRPC_STATUS_CODE); + assertThat(SemConvAttributes.HTTP_RESPONSE_STATUS_CODE) + .isEqualTo(HttpAttributes.HTTP_RESPONSE_STATUS_CODE); + } +} From d239b7017dfc7374e9f98d195dbdb59d4cb24666 Mon Sep 17 00:00:00 2001 From: Onur Kayabasi Date: Wed, 4 Jun 2025 00:06:21 +0200 Subject: [PATCH 28/42] Prometheus label conversion refactored to align with spec (#7291) Co-authored-by: Jack Berg --- .../prometheus/Otel2PrometheusConverter.java | 78 ++++- .../Otel2PrometheusConverterTest.java | 288 +++++++++++------- 2 files changed, 248 insertions(+), 118 deletions(-) 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 e1cb84d9766..664ad1bd4c2 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 @@ -10,6 +10,7 @@ import static java.util.Objects.requireNonNull; import io.opentelemetry.api.common.AttributeKey; +import io.opentelemetry.api.common.AttributeType; import io.opentelemetry.api.common.Attributes; import io.opentelemetry.api.trace.SpanContext; import io.opentelemetry.sdk.common.InstrumentationScopeInfo; @@ -60,6 +61,7 @@ import java.util.Locale; import java.util.Map; import java.util.Set; +import java.util.StringJoiner; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.TimeUnit; import java.util.function.Predicate; @@ -472,7 +474,9 @@ private Labels convertAttributes( Map labelNameToValue = new HashMap<>(); attributes.forEach( - (key, value) -> labelNameToValue.put(sanitizeLabelName(key.getKey()), value.toString())); + (key, value) -> + labelNameToValue.put( + sanitizeLabelName(key.getKey()), toLabelValue(key.getType(), value))); for (int i = 0; i < additionalAttributes.length; i += 2) { labelNameToValue.putIfAbsent( @@ -642,4 +646,76 @@ private static String typeString(MetricSnapshot snapshot) { // Simple helper for a log message. return snapshot.getClass().getSimpleName().replace("Snapshot", "").toLowerCase(Locale.ENGLISH); } + + private static String toLabelValue(AttributeType type, Object attributeValue) { + switch (type) { + case STRING: + case BOOLEAN: + case LONG: + case DOUBLE: + return attributeValue.toString(); + case BOOLEAN_ARRAY: + case LONG_ARRAY: + case DOUBLE_ARRAY: + case STRING_ARRAY: + if (attributeValue instanceof List) { + return toJsonStr((List) attributeValue); + } else { + throw new IllegalStateException( + String.format( + "Unexpected label value of %s for %s", + attributeValue.getClass().getName(), type.name())); + } + } + throw new IllegalStateException("Unrecognized AttributeType: " + type); + } + + public static String toJsonStr(List attributeValue) { + StringJoiner joiner = new StringJoiner(",", "[", "]"); + for (int i = 0; i < attributeValue.size(); i++) { + Object value = attributeValue.get(i); + joiner.add(value instanceof String ? toJsonValidStr((String) value) : String.valueOf(value)); + } + return joiner.toString(); + } + + public static String toJsonValidStr(String str) { + StringBuilder sb = new StringBuilder(); + sb.append('"'); + for (int i = 0; i < str.length(); i++) { + char c = str.charAt(i); + + switch (c) { + case '"': + sb.append("\\\""); + break; + case '\\': + sb.append("\\\\"); + break; + case '\b': + sb.append("\\b"); + break; + case '\f': + sb.append("\\f"); + break; + case '\n': + sb.append("\\n"); + break; + case '\r': + sb.append("\\r"); + break; + case '\t': + sb.append("\\t"); + break; + default: + if (c <= 0x1F) { + sb.append(String.format(Locale.ROOT, "\\u%04X", (int) c)); + } else { + sb.append(c); + } + } + } + sb.append('"'); + return sb.toString(); + } } 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 5b8dd270548..6395d60c75e 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 @@ -5,10 +5,20 @@ package io.opentelemetry.exporter.prometheus; +import static io.opentelemetry.api.common.AttributeKey.booleanArrayKey; +import static io.opentelemetry.api.common.AttributeKey.booleanKey; +import static io.opentelemetry.api.common.AttributeKey.doubleArrayKey; +import static io.opentelemetry.api.common.AttributeKey.doubleKey; +import static io.opentelemetry.api.common.AttributeKey.longArrayKey; +import static io.opentelemetry.api.common.AttributeKey.longKey; +import static io.opentelemetry.api.common.AttributeKey.stringArrayKey; import static io.opentelemetry.api.common.AttributeKey.stringKey; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatCode; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import io.opentelemetry.api.common.AttributeType; import io.opentelemetry.api.common.Attributes; import io.opentelemetry.sdk.common.InstrumentationScopeInfo; import io.opentelemetry.sdk.metrics.data.AggregationTemporality; @@ -28,6 +38,7 @@ 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.Labels; import io.prometheus.metrics.model.snapshots.MetricSnapshots; import java.io.ByteArrayOutputStream; import java.io.IOException; @@ -53,6 +64,7 @@ class Otel2PrometheusConverterTest { private static final Pattern PATTERN = Pattern.compile( "# HELP (?.*)\n# TYPE (?.*)\n(?.*)\\{otel_scope_name=\"scope\"}(.|\\n)*"); + private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); private final Otel2PrometheusConverter converter = new Otel2PrometheusConverter(true, /* allowedResourceAttributesFilter= */ null); @@ -79,6 +91,101 @@ void metricMetadata( assertThat(matcher.group("metricName")).isEqualTo(expectedMetricName); } + private static Stream metricMetadataArgs() { + return Stream.of( + // the unity unit "1" is translated to "ratio" + Arguments.of( + createSampleMetricData("sample", "1", MetricDataType.LONG_GAUGE), + "sample_ratio gauge", + "sample_ratio description", + "sample_ratio"), + // unit is appended to metric name + Arguments.of( + createSampleMetricData("sample", "unit", MetricDataType.LONG_GAUGE), + "sample_unit gauge", + "sample_unit description", + "sample_unit"), + // units in curly braces are dropped + Arguments.of( + createSampleMetricData("sample", "1{dropped}", MetricDataType.LONG_GAUGE), + "sample_ratio gauge", + "sample_ratio description", + "sample_ratio"), + // monotonic sums always include _total suffix + Arguments.of( + createSampleMetricData("sample", "unit", MetricDataType.LONG_SUM), + "sample_unit_total counter", + "sample_unit_total description", + "sample_unit_total"), + Arguments.of( + createSampleMetricData("sample", "1", MetricDataType.LONG_SUM), + "sample_ratio_total counter", + "sample_ratio_total description", + "sample_ratio_total"), + // units expressed as numbers other than 1 are retained + Arguments.of( + createSampleMetricData("sample", "2", MetricDataType.LONG_SUM), + "sample_2_total counter", + "sample_2_total description", + "sample_2_total"), + Arguments.of( + createSampleMetricData("metric_name", "2", MetricDataType.SUMMARY), + "metric_name_2 summary", + "metric_name_2 description", + "metric_name_2_count"), + // unsupported characters are translated to "_", repeated "_" are dropped + Arguments.of( + createSampleMetricData("s%%ple", "%/min", MetricDataType.SUMMARY), + "s_ple_percent_per_minute summary", + "s_ple_percent_per_minute description", + "s_ple_percent_per_minute_count"), + // metric unit is not appended if the name already contains the unit + Arguments.of( + createSampleMetricData("metric_name_total", "total", MetricDataType.LONG_SUM), + "metric_name_total counter", + "metric_name_total description", + "metric_name_total"), + // total suffix is stripped because total is a reserved suffixed for monotonic sums + Arguments.of( + createSampleMetricData("metric_name_total", "total", MetricDataType.SUMMARY), + "metric_name summary", + "metric_name description", + "metric_name_count"), + // if metric name ends with unit the unit is omitted + Arguments.of( + createSampleMetricData("metric_name_ratio", "1", MetricDataType.LONG_GAUGE), + "metric_name_ratio gauge", + "metric_name_ratio description", + "metric_name_ratio"), + Arguments.of( + createSampleMetricData("metric_name_ratio", "1", MetricDataType.SUMMARY), + "metric_name_ratio summary", + "metric_name_ratio description", + "metric_name_ratio_count"), + Arguments.of( + createSampleMetricData("metric_hertz", "hertz", MetricDataType.LONG_GAUGE), + "metric_hertz gauge", + "metric_hertz description", + "metric_hertz"), + Arguments.of( + createSampleMetricData("metric_hertz", "hertz", MetricDataType.LONG_SUM), + "metric_hertz_total counter", + "metric_hertz_total description", + "metric_hertz_total"), + // if metric name ends with unit the unit is omitted - order matters + Arguments.of( + createSampleMetricData("metric_total_hertz", "hertz_total", MetricDataType.LONG_SUM), + "metric_total_hertz_total counter", + "metric_total_hertz_total description", + "metric_total_hertz_total"), + // metric name cannot start with a number + Arguments.of( + createSampleMetricData("2_metric_name", "By", MetricDataType.SUMMARY), + "_metric_name_bytes summary", + "_metric_name_bytes description", + "_metric_name_bytes_count")); + } + @ParameterizedTest @MethodSource("resourceAttributesAdditionArgs") void resourceAttributesAddition( @@ -109,34 +216,6 @@ void resourceAttributesAddition( assertThat(metricLabels).isEqualTo(expectedMetricLabels); } - @Test - void prometheusNameCollisionTest_Issue6277() { - // NOTE: Metrics with the same resolved prometheus name should merge. However, - // Otel2PrometheusConverter is not responsible for merging individual series, so the merge will - // fail if the two different metrics contain overlapping series. Users should deal with this by - // adding a view that renames one of the two metrics such that the conflict does not occur. - MetricData dotName = - createSampleMetricData( - "my.metric", - "units", - MetricDataType.LONG_SUM, - Attributes.builder().put("key", "a").build(), - Resource.create(Attributes.empty())); - MetricData underscoreName = - createSampleMetricData( - "my_metric", - "units", - MetricDataType.LONG_SUM, - Attributes.builder().put("key", "b").build(), - Resource.create(Attributes.empty())); - - List metricData = new ArrayList<>(); - metricData.add(dotName); - metricData.add(underscoreName); - - assertThatCode(() -> converter.convert(metricData)).doesNotThrowAnyException(); - } - private static Stream resourceAttributesAdditionArgs() { List arguments = new ArrayList<>(); @@ -199,99 +278,74 @@ private static Stream resourceAttributesAdditionArgs() { return arguments.stream(); } - private static Stream metricMetadataArgs() { + @Test + void prometheusNameCollisionTest_Issue6277() { + // NOTE: Metrics with the same resolved prometheus name should merge. However, + // Otel2PrometheusConverter is not responsible for merging individual series, so the merge will + // fail if the two different metrics contain overlapping series. Users should deal with this by + // adding a view that renames one of the two metrics such that the conflict does not occur. + MetricData dotName = + createSampleMetricData( + "my.metric", + "units", + MetricDataType.LONG_SUM, + Attributes.builder().put("key", "a").build(), + Resource.create(Attributes.empty())); + MetricData underscoreName = + createSampleMetricData( + "my_metric", + "units", + MetricDataType.LONG_SUM, + Attributes.builder().put("key", "b").build(), + Resource.create(Attributes.empty())); + + List metricData = new ArrayList<>(); + metricData.add(dotName); + metricData.add(underscoreName); + + assertThatCode(() -> converter.convert(metricData)).doesNotThrowAnyException(); + } + + @ParameterizedTest + @MethodSource("labelValueSerializationArgs") + void labelValueSerialization(Attributes attributes) { + MetricData metricData = + createSampleMetricData("sample", "1", MetricDataType.LONG_SUM, attributes, null); + + MetricSnapshots snapshots = converter.convert(Collections.singletonList(metricData)); + + Labels labels = snapshots.get(0).getDataPoints().get(0).getLabels(); + attributes.forEach( + (key, value) -> { + String labelValue = labels.get(key.getKey()); + try { + String expectedValue = + key.getType() == AttributeType.STRING + ? (String) value + : OBJECT_MAPPER.writeValueAsString(value); + assertThat(labelValue).isEqualTo(expectedValue); + } catch (JsonProcessingException e) { + throw new RuntimeException(e); + } + }); + } + + private static Stream labelValueSerializationArgs() { return Stream.of( - // the unity unit "1" is translated to "ratio" + Arguments.of(Attributes.of(stringKey("key"), "stringValue")), + Arguments.of(Attributes.of(booleanKey("key"), true)), + Arguments.of(Attributes.of(longKey("key"), Long.MAX_VALUE)), + Arguments.of(Attributes.of(doubleKey("key"), 0.12345)), Arguments.of( - createSampleMetricData("sample", "1", MetricDataType.LONG_GAUGE), - "sample_ratio gauge", - "sample_ratio description", - "sample_ratio"), - // unit is appended to metric name + Attributes.of( + stringArrayKey("key"), + Arrays.asList("stringValue1", "\"+\\\\\\+\b+\f+\n+\r+\t+" + (char) 0))), + Arguments.of(Attributes.of(booleanArrayKey("key"), Arrays.asList(true, false))), Arguments.of( - createSampleMetricData("sample", "unit", MetricDataType.LONG_GAUGE), - "sample_unit gauge", - "sample_unit description", - "sample_unit"), - // units in curly braces are dropped + Attributes.of(longArrayKey("key"), Arrays.asList(Long.MIN_VALUE, Long.MAX_VALUE))), Arguments.of( - createSampleMetricData("sample", "1{dropped}", MetricDataType.LONG_GAUGE), - "sample_ratio gauge", - "sample_ratio description", - "sample_ratio"), - // monotonic sums always include _total suffix - Arguments.of( - createSampleMetricData("sample", "unit", MetricDataType.LONG_SUM), - "sample_unit_total counter", - "sample_unit_total description", - "sample_unit_total"), - Arguments.of( - createSampleMetricData("sample", "1", MetricDataType.LONG_SUM), - "sample_ratio_total counter", - "sample_ratio_total description", - "sample_ratio_total"), - // units expressed as numbers other than 1 are retained - Arguments.of( - createSampleMetricData("sample", "2", MetricDataType.LONG_SUM), - "sample_2_total counter", - "sample_2_total description", - "sample_2_total"), - Arguments.of( - createSampleMetricData("metric_name", "2", MetricDataType.SUMMARY), - "metric_name_2 summary", - "metric_name_2 description", - "metric_name_2_count"), - // unsupported characters are translated to "_", repeated "_" are dropped - Arguments.of( - createSampleMetricData("s%%ple", "%/min", MetricDataType.SUMMARY), - "s_ple_percent_per_minute summary", - "s_ple_percent_per_minute description", - "s_ple_percent_per_minute_count"), - // metric unit is not appended if the name already contains the unit - Arguments.of( - createSampleMetricData("metric_name_total", "total", MetricDataType.LONG_SUM), - "metric_name_total counter", - "metric_name_total description", - "metric_name_total"), - // total suffix is stripped because total is a reserved suffixed for monotonic sums - Arguments.of( - createSampleMetricData("metric_name_total", "total", MetricDataType.SUMMARY), - "metric_name summary", - "metric_name description", - "metric_name_count"), - // if metric name ends with unit the unit is omitted - Arguments.of( - createSampleMetricData("metric_name_ratio", "1", MetricDataType.LONG_GAUGE), - "metric_name_ratio gauge", - "metric_name_ratio description", - "metric_name_ratio"), - Arguments.of( - createSampleMetricData("metric_name_ratio", "1", MetricDataType.SUMMARY), - "metric_name_ratio summary", - "metric_name_ratio description", - "metric_name_ratio_count"), - Arguments.of( - createSampleMetricData("metric_hertz", "hertz", MetricDataType.LONG_GAUGE), - "metric_hertz gauge", - "metric_hertz description", - "metric_hertz"), - Arguments.of( - createSampleMetricData("metric_hertz", "hertz", MetricDataType.LONG_SUM), - "metric_hertz_total counter", - "metric_hertz_total description", - "metric_hertz_total"), - // if metric name ends with unit the unit is omitted - order matters - Arguments.of( - createSampleMetricData("metric_total_hertz", "hertz_total", MetricDataType.LONG_SUM), - "metric_total_hertz_total counter", - "metric_total_hertz_total description", - "metric_total_hertz_total"), - // metric name cannot start with a number - Arguments.of( - createSampleMetricData("2_metric_name", "By", MetricDataType.SUMMARY), - "_metric_name_bytes summary", - "_metric_name_bytes description", - "_metric_name_bytes_count")); + Attributes.of( + doubleArrayKey("key"), Arrays.asList(Double.MIN_VALUE, Double.MAX_VALUE)))); } static MetricData createSampleMetricData( From 3b596aae1bd661293e60f913cf9a7985c1b142c2 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 3 Jun 2025 17:15:35 -0500 Subject: [PATCH 29/42] fix(deps): update dependency io.zipkin.brave:brave-bom to v6.3.0 (#7385) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- dependencyManagement/build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dependencyManagement/build.gradle.kts b/dependencyManagement/build.gradle.kts index 629fc47a0f1..8ae40843eae 100644 --- a/dependencyManagement/build.gradle.kts +++ b/dependencyManagement/build.gradle.kts @@ -20,7 +20,7 @@ val DEPENDENCY_BOMS = listOf( "com.squareup.okio:okio-bom:3.12.0", // applies to transitive dependencies of okhttp "io.grpc:grpc-bom:1.73.0", "io.netty:netty-bom:4.2.1.Final", - "io.zipkin.brave:brave-bom:6.2.0", + "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.1", From a756317511741fa06ad343c12d3599c1b7618a4d Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 3 Jun 2025 17:15:48 -0500 Subject: [PATCH 30/42] fix(deps): update dependency com.google.api.grpc:proto-google-common-protos to v2.58.0 (#7384) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- dependencyManagement/build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dependencyManagement/build.gradle.kts b/dependencyManagement/build.gradle.kts index 8ae40843eae..c376972528b 100644 --- a/dependencyManagement/build.gradle.kts +++ b/dependencyManagement/build.gradle.kts @@ -73,7 +73,7 @@ val DEPENDENCIES = listOf( "io.prometheus:simpleclient_httpserver:${prometheusClientVersion}", "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.57.0", + "com.google.api.grpc:proto-google-common-protos:2.58.0", "com.google.code.findbugs:jsr305:3.0.2", "com.google.guava:guava-beta-checker:1.0", "com.sun.net.httpserver:http:20070405", From b11316b0bd2638860a1945b2f1e354e051cf6fee Mon Sep 17 00:00:00 2001 From: jack-berg <34418638+jack-berg@users.noreply.github.com> Date: Thu, 5 Jun 2025 11:20:14 -0500 Subject: [PATCH 31/42] Configurable exception.* attribute resolution (#7266) --- .../sdk/internal/AttributeUtil.java | 32 --------- .../sdk/internal/AttributesMap.java | 14 +++- .../DefaultExceptionAttributeResolver.java | 50 ++++++++++++++ .../internal/ExceptionAttributeResolver.java | 37 +++++++++++ .../sdk/internal/ExtendedAttributesMap.java | 17 +++-- .../sdk/logs/ExtendedSdkLogRecordBuilder.java | 8 ++- .../sdk/logs/LoggerSharedState.java | 10 ++- .../sdk/logs/SdkLoggerProvider.java | 7 +- .../sdk/logs/SdkLoggerProviderBuilder.java | 26 +++++++- .../logs/internal/SdkLoggerProviderUtil.java | 17 +++++ .../sdk/logs/LoggerSharedStateTest.java | 7 +- .../sdk/logs/ExtendedLoggerBuilderTest.java | 33 +++++++++- .../io/opentelemetry/sdk/trace/SdkSpan.java | 11 +++- .../sdk/trace/SdkSpanBuilder.java | 1 + .../sdk/trace/SdkTracerProvider.java | 12 +++- .../sdk/trace/SdkTracerProviderBuilder.java | 22 ++++++- .../sdk/trace/TracerSharedState.java | 11 +++- .../trace/internal/SdkTracerProviderUtil.java | 17 +++++ .../opentelemetry/sdk/trace/SdkSpanTest.java | 66 +++++++++++++++++-- .../sdk/trace/SdkTracerProviderTest.java | 24 +++++++ 20 files changed, 364 insertions(+), 58 deletions(-) create mode 100644 sdk/common/src/main/java/io/opentelemetry/sdk/internal/DefaultExceptionAttributeResolver.java create mode 100644 sdk/common/src/main/java/io/opentelemetry/sdk/internal/ExceptionAttributeResolver.java diff --git a/sdk/common/src/main/java/io/opentelemetry/sdk/internal/AttributeUtil.java b/sdk/common/src/main/java/io/opentelemetry/sdk/internal/AttributeUtil.java index e0fc9b56b90..de7fac88eba 100644 --- a/sdk/common/src/main/java/io/opentelemetry/sdk/internal/AttributeUtil.java +++ b/sdk/common/src/main/java/io/opentelemetry/sdk/internal/AttributeUtil.java @@ -8,12 +8,9 @@ import io.opentelemetry.api.common.AttributeKey; import io.opentelemetry.api.common.Attributes; import io.opentelemetry.api.common.AttributesBuilder; -import java.io.PrintWriter; -import java.io.StringWriter; import java.util.ArrayList; import java.util.List; import java.util.Map; -import java.util.function.BiConsumer; import java.util.function.Predicate; /** @@ -22,13 +19,6 @@ */ public final class AttributeUtil { - private static final AttributeKey EXCEPTION_TYPE = - AttributeKey.stringKey("exception.type"); - private static final AttributeKey EXCEPTION_MESSAGE = - AttributeKey.stringKey("exception.message"); - private static final AttributeKey EXCEPTION_STACKTRACE = - AttributeKey.stringKey("exception.stacktrace"); - private AttributeUtil() {} /** @@ -105,26 +95,4 @@ public static Object applyAttributeLengthLimit(Object value, int lengthLimit) { } return value; } - - public static void addExceptionAttributes( - Throwable exception, BiConsumer, String> attributeConsumer) { - String exceptionType = exception.getClass().getCanonicalName(); - if (exceptionType != null) { - attributeConsumer.accept(EXCEPTION_TYPE, exceptionType); - } - - String exceptionMessage = exception.getMessage(); - if (exceptionMessage != null) { - attributeConsumer.accept(EXCEPTION_MESSAGE, exceptionMessage); - } - - StringWriter stringWriter = new StringWriter(); - try (PrintWriter printWriter = new PrintWriter(stringWriter)) { - exception.printStackTrace(printWriter); - } - String stackTrace = stringWriter.toString(); - if (stackTrace != null) { - attributeConsumer.accept(EXCEPTION_STACKTRACE, stackTrace); - } - } } diff --git a/sdk/common/src/main/java/io/opentelemetry/sdk/internal/AttributesMap.java b/sdk/common/src/main/java/io/opentelemetry/sdk/internal/AttributesMap.java index 7a09093a315..2e481723c9c 100644 --- a/sdk/common/src/main/java/io/opentelemetry/sdk/internal/AttributesMap.java +++ b/sdk/common/src/main/java/io/opentelemetry/sdk/internal/AttributesMap.java @@ -21,8 +21,8 @@ *

WARNING: In order to reduce memory allocation, this class extends {@link HashMap} when it * would be more appropriate to delegate. The problem with extending is that we don't enforce that * all {@link HashMap} methods for reading / writing data conform to the configured attribute - * limits. Therefore, it's easy to accidentally call something like {@link Map#putAll(Map)} or - * {@link Map#put(Object, Object)} and bypass the restrictions (see #7135). Callers MUST * take care to only call methods from {@link AttributesMap}, and not {@link HashMap}. * @@ -58,7 +58,10 @@ public static AttributesMap create(long capacity, int lengthLimit) { */ @Override @Nullable - public Object put(AttributeKey key, Object value) { + public Object put(AttributeKey key, @Nullable Object value) { + if (value == null) { + return null; + } totalAddedValues++; if (size() >= capacity && !containsKey(key)) { return null; @@ -66,6 +69,11 @@ public Object put(AttributeKey key, Object value) { return super.put(key, AttributeUtil.applyAttributeLengthLimit(value, lengthLimit)); } + /** Generic overload of {@link #put(AttributeKey, Object)}. */ + public void putIfCapacity(AttributeKey key, @Nullable T value) { + put(key, value); + } + /** Get the total number of attributes added, including those dropped for capacity limits. */ public int getTotalAddedValues() { return totalAddedValues; 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 new file mode 100644 index 00000000000..44061c1644f --- /dev/null +++ b/sdk/common/src/main/java/io/opentelemetry/sdk/internal/DefaultExceptionAttributeResolver.java @@ -0,0 +1,50 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.sdk.internal; + +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 + * APIs (or a version of them) may be promoted to the public stable API in the future, but no + * guarantees are made. + */ +public final class DefaultExceptionAttributeResolver implements ExceptionAttributeResolver { + + private static final DefaultExceptionAttributeResolver INSTANCE = + new DefaultExceptionAttributeResolver(); + + private DefaultExceptionAttributeResolver() {} + + public static ExceptionAttributeResolver getInstance() { + return INSTANCE; + } + + @Override + public void setExceptionAttributes( + AttributeSetter attributeSetter, Throwable throwable, int maxAttributeLength) { + String exceptionType = throwable.getClass().getCanonicalName(); + if (exceptionType != null) { + attributeSetter.setAttribute(ExceptionAttributeResolver.EXCEPTION_TYPE, exceptionType); + } + + String exceptionMessage = throwable.getMessage(); + if (exceptionMessage != null) { + attributeSetter.setAttribute(ExceptionAttributeResolver.EXCEPTION_MESSAGE, exceptionMessage); + } + + 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); + } + } +} 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 new file mode 100644 index 00000000000..4b9d7a8dc9d --- /dev/null +++ b/sdk/common/src/main/java/io/opentelemetry/sdk/internal/ExceptionAttributeResolver.java @@ -0,0 +1,37 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.sdk.internal; + +import io.opentelemetry.api.common.AttributeKey; +import javax.annotation.Nullable; + +/** + * Implementations resolve {@code exception.*} attributes attached to span events, logs, etc. + * + *

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. + */ +public interface ExceptionAttributeResolver { + + AttributeKey EXCEPTION_TYPE = AttributeKey.stringKey("exception.type"); + AttributeKey EXCEPTION_MESSAGE = AttributeKey.stringKey("exception.message"); + AttributeKey EXCEPTION_STACKTRACE = AttributeKey.stringKey("exception.stacktrace"); + + void setExceptionAttributes( + AttributeSetter attributeSetter, Throwable throwable, int maxAttributeLength); + + /** + * 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. + */ + // TODO(jack-berg): Consider promoting to opentelemetry and extending with Span, LogRecordBuilder, + // AttributeBuilder, AttributesMap etc. + interface AttributeSetter { + void setAttribute(AttributeKey key, @Nullable T value); + } +} diff --git a/sdk/common/src/main/java/io/opentelemetry/sdk/internal/ExtendedAttributesMap.java b/sdk/common/src/main/java/io/opentelemetry/sdk/internal/ExtendedAttributesMap.java index 440e472666e..52959b1b617 100644 --- a/sdk/common/src/main/java/io/opentelemetry/sdk/internal/ExtendedAttributesMap.java +++ b/sdk/common/src/main/java/io/opentelemetry/sdk/internal/ExtendedAttributesMap.java @@ -49,14 +49,23 @@ public static ExtendedAttributesMap create(long capacity, int lengthLimit) { } /** Add the attribute key value pair, applying capacity and length limits. */ - public void put(ExtendedAttributeKey key, T value) { + @Override + @Nullable + public Object put(ExtendedAttributeKey key, @Nullable Object value) { + if (value == null) { + return null; + } totalAddedValues++; - // TODO(jack-berg): apply capcity to nested entries + // TODO(jack-berg): apply capacity to nested entries if (size() >= capacity && !containsKey(key)) { - return; + return null; } // TODO(jack-berg): apply limits to nested entries - super.put(key, AttributeUtil.applyAttributeLengthLimit(value, lengthLimit)); + return super.put(key, AttributeUtil.applyAttributeLengthLimit(value, lengthLimit)); + } + + public void putIfCapacity(ExtendedAttributeKey key, @Nullable T value) { + put(key, value); } /** diff --git a/sdk/logs/src/main/java/io/opentelemetry/sdk/logs/ExtendedSdkLogRecordBuilder.java b/sdk/logs/src/main/java/io/opentelemetry/sdk/logs/ExtendedSdkLogRecordBuilder.java index 9fabc98b9e8..41596afa3da 100644 --- a/sdk/logs/src/main/java/io/opentelemetry/sdk/logs/ExtendedSdkLogRecordBuilder.java +++ b/sdk/logs/src/main/java/io/opentelemetry/sdk/logs/ExtendedSdkLogRecordBuilder.java @@ -13,7 +13,6 @@ import io.opentelemetry.api.trace.Span; import io.opentelemetry.context.Context; import io.opentelemetry.sdk.common.InstrumentationScopeInfo; -import io.opentelemetry.sdk.internal.AttributeUtil; import io.opentelemetry.sdk.internal.ExtendedAttributesMap; import java.time.Instant; import java.util.concurrent.TimeUnit; @@ -42,7 +41,12 @@ public ExtendedSdkLogRecordBuilder setException(Throwable throwable) { return this; } - AttributeUtil.addExceptionAttributes(throwable, this::setAttribute); + loggerSharedState + .getExceptionAttributeResolver() + .setExceptionAttributes( + this::setAttribute, + throwable, + loggerSharedState.getLogLimits().getMaxAttributeValueLength()); return this; } diff --git a/sdk/logs/src/main/java/io/opentelemetry/sdk/logs/LoggerSharedState.java b/sdk/logs/src/main/java/io/opentelemetry/sdk/logs/LoggerSharedState.java index 768871e1e57..5b40f897a32 100644 --- a/sdk/logs/src/main/java/io/opentelemetry/sdk/logs/LoggerSharedState.java +++ b/sdk/logs/src/main/java/io/opentelemetry/sdk/logs/LoggerSharedState.java @@ -7,6 +7,7 @@ import io.opentelemetry.sdk.common.Clock; import io.opentelemetry.sdk.common.CompletableResultCode; +import io.opentelemetry.sdk.internal.ExceptionAttributeResolver; import io.opentelemetry.sdk.resources.Resource; import java.util.function.Supplier; import javax.annotation.Nullable; @@ -21,17 +22,20 @@ final class LoggerSharedState { private final Supplier logLimitsSupplier; private final LogRecordProcessor logRecordProcessor; private final Clock clock; + private final ExceptionAttributeResolver exceptionAttributeResolver; @Nullable private volatile CompletableResultCode shutdownResult = null; LoggerSharedState( Resource resource, Supplier logLimitsSupplier, LogRecordProcessor logRecordProcessor, - Clock clock) { + Clock clock, + ExceptionAttributeResolver exceptionAttributeResolver) { this.resource = resource; this.logLimitsSupplier = logLimitsSupplier; this.logRecordProcessor = logRecordProcessor; this.clock = clock; + this.exceptionAttributeResolver = exceptionAttributeResolver; } Resource getResource() { @@ -50,6 +54,10 @@ Clock getClock() { return clock; } + ExceptionAttributeResolver getExceptionAttributeResolver() { + return exceptionAttributeResolver; + } + boolean hasBeenShutdown() { return shutdownResult != null; } diff --git a/sdk/logs/src/main/java/io/opentelemetry/sdk/logs/SdkLoggerProvider.java b/sdk/logs/src/main/java/io/opentelemetry/sdk/logs/SdkLoggerProvider.java index 12f7baa201e..54825caf21e 100644 --- a/sdk/logs/src/main/java/io/opentelemetry/sdk/logs/SdkLoggerProvider.java +++ b/sdk/logs/src/main/java/io/opentelemetry/sdk/logs/SdkLoggerProvider.java @@ -13,6 +13,7 @@ import io.opentelemetry.sdk.common.CompletableResultCode; import io.opentelemetry.sdk.common.InstrumentationScopeInfo; import io.opentelemetry.sdk.internal.ComponentRegistry; +import io.opentelemetry.sdk.internal.ExceptionAttributeResolver; import io.opentelemetry.sdk.internal.ScopeConfigurator; import io.opentelemetry.sdk.logs.internal.LoggerConfig; import io.opentelemetry.sdk.resources.Resource; @@ -56,10 +57,12 @@ public static SdkLoggerProviderBuilder builder() { Supplier logLimitsSupplier, List processors, Clock clock, - ScopeConfigurator loggerConfigurator) { + ScopeConfigurator loggerConfigurator, + ExceptionAttributeResolver exceptionAttributeResolver) { LogRecordProcessor logRecordProcessor = LogRecordProcessor.composite(processors); this.sharedState = - new LoggerSharedState(resource, logLimitsSupplier, logRecordProcessor, clock); + new LoggerSharedState( + resource, logLimitsSupplier, logRecordProcessor, clock, exceptionAttributeResolver); this.loggerComponentRegistry = new ComponentRegistry<>( instrumentationScopeInfo -> 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 14d6d664f9c..25fdeaa4454 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,6 +12,8 @@ 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; import io.opentelemetry.sdk.logs.data.LogRecordData; @@ -37,6 +39,8 @@ public final class SdkLoggerProviderBuilder { private Clock clock = Clock.getDefault(); private ScopeConfiguratorBuilder loggerConfiguratorBuilder = LoggerConfig.configuratorBuilder(); + private ExceptionAttributeResolver exceptionAttributeResolver = + DefaultExceptionAttributeResolver.getInstance(); SdkLoggerProviderBuilder() {} @@ -168,6 +172,21 @@ SdkLoggerProviderBuilder addLoggerConfiguratorCondition( return this; } + /** + * Set the exception attribute resolver, which resolves {@code exception.*} attributes an + * exception is set on a log. + * + *

This method is experimental so not public. You may reflectively call it using {@link + * SdkLoggerProviderUtil#setExceptionAttributeResolver(SdkLoggerProviderBuilder, + * ExceptionAttributeResolver)}. + */ + SdkLoggerProviderBuilder setExceptionAttributeResolver( + ExceptionAttributeResolver exceptionAttributeResolver) { + requireNonNull(exceptionAttributeResolver, "exceptionAttributeResolver"); + this.exceptionAttributeResolver = exceptionAttributeResolver; + return this; + } + /** * Create a {@link SdkLoggerProvider} instance. * @@ -175,6 +194,11 @@ SdkLoggerProviderBuilder addLoggerConfiguratorCondition( */ public SdkLoggerProvider build() { return new SdkLoggerProvider( - resource, logLimitsSupplier, logRecordProcessors, clock, loggerConfiguratorBuilder.build()); + resource, + logLimitsSupplier, + logRecordProcessors, + clock, + loggerConfiguratorBuilder.build(), + exceptionAttributeResolver); } } diff --git a/sdk/logs/src/main/java/io/opentelemetry/sdk/logs/internal/SdkLoggerProviderUtil.java b/sdk/logs/src/main/java/io/opentelemetry/sdk/logs/internal/SdkLoggerProviderUtil.java index fa659a747ec..417601bbad4 100644 --- a/sdk/logs/src/main/java/io/opentelemetry/sdk/logs/internal/SdkLoggerProviderUtil.java +++ b/sdk/logs/src/main/java/io/opentelemetry/sdk/logs/internal/SdkLoggerProviderUtil.java @@ -6,6 +6,7 @@ package io.opentelemetry.sdk.logs.internal; import io.opentelemetry.sdk.common.InstrumentationScopeInfo; +import io.opentelemetry.sdk.internal.ExceptionAttributeResolver; import io.opentelemetry.sdk.internal.ScopeConfigurator; import io.opentelemetry.sdk.logs.SdkLoggerProvider; import io.opentelemetry.sdk.logs.SdkLoggerProviderBuilder; @@ -73,4 +74,20 @@ public static SdkLoggerProviderBuilder addLoggerConfiguratorCondition( } return sdkLoggerProviderBuilder; } + + /** Reflectively set exception attribute resolver to the {@link SdkLoggerProviderBuilder}. */ + public static void setExceptionAttributeResolver( + SdkLoggerProviderBuilder sdkLoggerProviderBuilder, + ExceptionAttributeResolver exceptionAttributeResolver) { + try { + Method method = + SdkLoggerProviderBuilder.class.getDeclaredMethod( + "setExceptionAttributeResolver", ExceptionAttributeResolver.class); + method.setAccessible(true); + method.invoke(sdkLoggerProviderBuilder, exceptionAttributeResolver); + } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { + throw new IllegalStateException( + "Error calling setExceptionAttributeResolver on SdkLoggerProviderBuilder", e); + } + } } 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 29a3a846a56..31b46c65206 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,6 +12,7 @@ import io.opentelemetry.sdk.common.Clock; import io.opentelemetry.sdk.common.CompletableResultCode; +import io.opentelemetry.sdk.internal.DefaultExceptionAttributeResolver; import io.opentelemetry.sdk.resources.Resource; import org.junit.jupiter.api.Test; @@ -24,7 +25,11 @@ void shutdown() { when(logRecordProcessor.shutdown()).thenReturn(code); LoggerSharedState state = new LoggerSharedState( - Resource.empty(), LogLimits::getDefault, logRecordProcessor, Clock.getDefault()); + Resource.empty(), + LogLimits::getDefault, + logRecordProcessor, + Clock.getDefault(), + DefaultExceptionAttributeResolver.getInstance()); state.shutdown(); state.shutdown(); verify(logRecordProcessor, times(1)).shutdown(); diff --git a/sdk/logs/src/testIncubating/java/io/opentelemetry/sdk/logs/ExtendedLoggerBuilderTest.java b/sdk/logs/src/testIncubating/java/io/opentelemetry/sdk/logs/ExtendedLoggerBuilderTest.java index 3b166f8a163..17e1eb0ddff 100644 --- a/sdk/logs/src/testIncubating/java/io/opentelemetry/sdk/logs/ExtendedLoggerBuilderTest.java +++ b/sdk/logs/src/testIncubating/java/io/opentelemetry/sdk/logs/ExtendedLoggerBuilderTest.java @@ -14,7 +14,9 @@ import io.opentelemetry.api.incubator.logs.ExtendedLogRecordBuilder; import io.opentelemetry.api.logs.Logger; +import io.opentelemetry.sdk.internal.ExceptionAttributeResolver; import io.opentelemetry.sdk.logs.export.SimpleLogRecordProcessor; +import io.opentelemetry.sdk.logs.internal.SdkLoggerProviderUtil; import io.opentelemetry.sdk.testing.exporter.InMemoryLogRecordExporter; import org.junit.jupiter.api.Test; @@ -25,7 +27,7 @@ class ExtendedLoggerBuilderTest { SdkLoggerProvider.builder().addLogRecordProcessor(SimpleLogRecordProcessor.create(exporter)); @Test - void setException() { + void setException_DefaultResolver() { Logger logger = loggerProviderBuilder.build().get("logger"); ((ExtendedLogRecordBuilder) logger.logRecordBuilder()) @@ -45,4 +47,33 @@ void setException() { stacktrace.startsWith( "java.lang.Exception: error" + System.lineSeparator())))); } + + @Test + void setException_CustomResolver() { + SdkLoggerProviderUtil.setExceptionAttributeResolver( + loggerProviderBuilder, + new ExceptionAttributeResolver() { + @Override + public void setExceptionAttributes( + AttributeSetter attributeSetter, Throwable throwable, int maxAttributeLength) { + attributeSetter.setAttribute(ExceptionAttributeResolver.EXCEPTION_TYPE, "type"); + attributeSetter.setAttribute( + ExceptionAttributeResolver.EXCEPTION_STACKTRACE, "stacktrace"); + } + }); + + Logger logger = loggerProviderBuilder.build().get("logger"); + + ((ExtendedLogRecordBuilder) logger.logRecordBuilder()) + .setException(new Exception("error")) + .emit(); + + assertThat(exporter.getFinishedLogRecordItems()) + .satisfiesExactly( + logRecord -> + assertThat(logRecord) + .hasAttributesSatisfyingExactly( + equalTo(EXCEPTION_TYPE, "type"), + equalTo(EXCEPTION_STACKTRACE, "stacktrace"))); + } } diff --git a/sdk/trace/src/main/java/io/opentelemetry/sdk/trace/SdkSpan.java b/sdk/trace/src/main/java/io/opentelemetry/sdk/trace/SdkSpan.java index 352ccf3135f..37deab7ffc8 100644 --- a/sdk/trace/src/main/java/io/opentelemetry/sdk/trace/SdkSpan.java +++ b/sdk/trace/src/main/java/io/opentelemetry/sdk/trace/SdkSpan.java @@ -17,6 +17,7 @@ import io.opentelemetry.sdk.common.InstrumentationScopeInfo; import io.opentelemetry.sdk.internal.AttributeUtil; import io.opentelemetry.sdk.internal.AttributesMap; +import io.opentelemetry.sdk.internal.ExceptionAttributeResolver; import io.opentelemetry.sdk.internal.InstrumentationScopeUtil; import io.opentelemetry.sdk.resources.Resource; import io.opentelemetry.sdk.trace.data.EventData; @@ -48,6 +49,8 @@ final class SdkSpan implements ReadWriteSpan { private final SpanContext parentSpanContext; // Handler called when the span starts and ends. private final SpanProcessor spanProcessor; + // Resolves exception.* when an recordException is called + private final ExceptionAttributeResolver exceptionAttributeResolver; // The kind of the span. private final SpanKind kind; // The clock used to get the time. @@ -123,6 +126,7 @@ private SdkSpan( SpanContext parentSpanContext, SpanLimits spanLimits, SpanProcessor spanProcessor, + ExceptionAttributeResolver exceptionAttributeResolver, AnchoredClock clock, Resource resource, @Nullable AttributesMap attributes, @@ -137,6 +141,7 @@ private SdkSpan( this.name = name; this.kind = kind; this.spanProcessor = spanProcessor; + this.exceptionAttributeResolver = exceptionAttributeResolver; this.resource = resource; this.hasEnded = EndState.NOT_ENDED; this.clock = clock; @@ -169,6 +174,7 @@ static SdkSpan startSpan( Context parentContext, SpanLimits spanLimits, SpanProcessor spanProcessor, + ExceptionAttributeResolver exceptionAttributeResolver, Clock tracerClock, Resource resource, @Nullable AttributesMap attributes, @@ -207,6 +213,7 @@ static SdkSpan startSpan( parentSpan.getSpanContext(), spanLimits, spanProcessor, + exceptionAttributeResolver, clock, resource, attributes, @@ -465,11 +472,13 @@ public ReadWriteSpan recordException(Throwable exception, Attributes additionalA additionalAttributes = Attributes.empty(); } + int maxAttributeLength = spanLimits.getMaxAttributeValueLength(); AttributesMap attributes = AttributesMap.create( spanLimits.getMaxNumberOfAttributes(), spanLimits.getMaxAttributeValueLength()); - AttributeUtil.addExceptionAttributes(exception, attributes::put); + exceptionAttributeResolver.setExceptionAttributes( + attributes::putIfCapacity, exception, maxAttributeLength); additionalAttributes.forEach(attributes::put); diff --git a/sdk/trace/src/main/java/io/opentelemetry/sdk/trace/SdkSpanBuilder.java b/sdk/trace/src/main/java/io/opentelemetry/sdk/trace/SdkSpanBuilder.java index b12107c7d43..c0f872265ec 100644 --- a/sdk/trace/src/main/java/io/opentelemetry/sdk/trace/SdkSpanBuilder.java +++ b/sdk/trace/src/main/java/io/opentelemetry/sdk/trace/SdkSpanBuilder.java @@ -226,6 +226,7 @@ public Span startSpan() { parentContext, spanLimits, tracerSharedState.getActiveSpanProcessor(), + tracerSharedState.getExceptionAttributesResolver(), tracerSharedState.getClock(), tracerSharedState.getResource(), recordedAttributes, diff --git a/sdk/trace/src/main/java/io/opentelemetry/sdk/trace/SdkTracerProvider.java b/sdk/trace/src/main/java/io/opentelemetry/sdk/trace/SdkTracerProvider.java index 8959c59a115..f39ce565731 100644 --- a/sdk/trace/src/main/java/io/opentelemetry/sdk/trace/SdkTracerProvider.java +++ b/sdk/trace/src/main/java/io/opentelemetry/sdk/trace/SdkTracerProvider.java @@ -12,6 +12,7 @@ import io.opentelemetry.sdk.common.CompletableResultCode; import io.opentelemetry.sdk.common.InstrumentationScopeInfo; import io.opentelemetry.sdk.internal.ComponentRegistry; +import io.opentelemetry.sdk.internal.ExceptionAttributeResolver; import io.opentelemetry.sdk.internal.ScopeConfigurator; import io.opentelemetry.sdk.resources.Resource; import io.opentelemetry.sdk.trace.internal.SdkTracerProviderUtil; @@ -52,10 +53,17 @@ public static SdkTracerProviderBuilder builder() { Supplier spanLimitsSupplier, Sampler sampler, List spanProcessors, - ScopeConfigurator tracerConfigurator) { + ScopeConfigurator tracerConfigurator, + ExceptionAttributeResolver exceptionAttributeResolver) { this.sharedState = new TracerSharedState( - clock, idsGenerator, resource, spanLimitsSupplier, sampler, spanProcessors); + clock, + idsGenerator, + resource, + spanLimitsSupplier, + sampler, + spanProcessors, + exceptionAttributeResolver); this.tracerSdkComponentRegistry = new ComponentRegistry<>( instrumentationScopeInfo -> 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 c9f604f76fc..194aa67bc17 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,6 +10,8 @@ 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; import io.opentelemetry.sdk.resources.Resource; @@ -35,6 +37,8 @@ public final class SdkTracerProviderBuilder { private Sampler sampler = DEFAULT_SAMPLER; private ScopeConfiguratorBuilder tracerConfiguratorBuilder = TracerConfig.configuratorBuilder(); + private ExceptionAttributeResolver exceptionAttributeResolver = + DefaultExceptionAttributeResolver.getInstance(); /** * Assign a {@link Clock}. {@link Clock} will be used each time a {@link Span} is started, ended @@ -214,6 +218,21 @@ SdkTracerProviderBuilder addTracerConfiguratorCondition( return this; } + /** + * Set the exception attribute resolver, which resolves {@code exception.*} attributes when {@link + * Span#recordException(Throwable)} + * + *

This method is experimental so not public. You may reflectively call it using {@link + * SdkTracerProviderUtil#setExceptionAttributeResolver(SdkTracerProviderBuilder, + * ExceptionAttributeResolver)}. + */ + SdkTracerProviderBuilder setExceptionAttributeResolver( + ExceptionAttributeResolver exceptionAttributeResolver) { + requireNonNull(exceptionAttributeResolver, "exceptionAttributeResolver"); + this.exceptionAttributeResolver = exceptionAttributeResolver; + return this; + } + /** * Create a new {@link SdkTracerProvider} instance with the configuration. * @@ -227,7 +246,8 @@ public SdkTracerProvider build() { spanLimitsSupplier, sampler, spanProcessors, - tracerConfiguratorBuilder.build()); + tracerConfiguratorBuilder.build(), + exceptionAttributeResolver); } SdkTracerProviderBuilder() {} diff --git a/sdk/trace/src/main/java/io/opentelemetry/sdk/trace/TracerSharedState.java b/sdk/trace/src/main/java/io/opentelemetry/sdk/trace/TracerSharedState.java index 37bb6675c06..74d43076b97 100644 --- a/sdk/trace/src/main/java/io/opentelemetry/sdk/trace/TracerSharedState.java +++ b/sdk/trace/src/main/java/io/opentelemetry/sdk/trace/TracerSharedState.java @@ -7,6 +7,7 @@ import io.opentelemetry.sdk.common.Clock; import io.opentelemetry.sdk.common.CompletableResultCode; +import io.opentelemetry.sdk.internal.ExceptionAttributeResolver; import io.opentelemetry.sdk.resources.Resource; import io.opentelemetry.sdk.trace.samplers.Sampler; import java.util.List; @@ -26,6 +27,7 @@ final class TracerSharedState { private final Supplier spanLimitsSupplier; private final Sampler sampler; private final SpanProcessor activeSpanProcessor; + private final ExceptionAttributeResolver exceptionAttributeResolver; @Nullable private volatile CompletableResultCode shutdownResult = null; @@ -35,7 +37,8 @@ final class TracerSharedState { Resource resource, Supplier spanLimitsSupplier, Sampler sampler, - List spanProcessors) { + List spanProcessors, + ExceptionAttributeResolver exceptionAttributeResolver) { this.clock = clock; this.idGenerator = idGenerator; this.idGeneratorSafeToSkipIdValidation = idGenerator instanceof RandomIdGenerator; @@ -43,6 +46,7 @@ final class TracerSharedState { this.spanLimitsSupplier = spanLimitsSupplier; this.sampler = sampler; this.activeSpanProcessor = SpanProcessor.composite(spanProcessors); + this.exceptionAttributeResolver = exceptionAttributeResolver; } Clock getClock() { @@ -89,6 +93,11 @@ boolean hasBeenShutdown() { return shutdownResult != null; } + /** Return the {@link ExceptionAttributeResolver}. */ + ExceptionAttributeResolver getExceptionAttributesResolver() { + return exceptionAttributeResolver; + } + /** * Stops tracing, including shutting down processors and set to {@code true} {@link * #hasBeenShutdown()}. diff --git a/sdk/trace/src/main/java/io/opentelemetry/sdk/trace/internal/SdkTracerProviderUtil.java b/sdk/trace/src/main/java/io/opentelemetry/sdk/trace/internal/SdkTracerProviderUtil.java index 753a312fa1f..f9ca8fafc56 100644 --- a/sdk/trace/src/main/java/io/opentelemetry/sdk/trace/internal/SdkTracerProviderUtil.java +++ b/sdk/trace/src/main/java/io/opentelemetry/sdk/trace/internal/SdkTracerProviderUtil.java @@ -6,6 +6,7 @@ package io.opentelemetry.sdk.trace.internal; import io.opentelemetry.sdk.common.InstrumentationScopeInfo; +import io.opentelemetry.sdk.internal.ExceptionAttributeResolver; import io.opentelemetry.sdk.internal.ScopeConfigurator; import io.opentelemetry.sdk.trace.SdkTracerProvider; import io.opentelemetry.sdk.trace.SdkTracerProviderBuilder; @@ -72,4 +73,20 @@ public static void addTracerConfiguratorCondition( "Error calling addTracerConfiguratorCondition on SdkTracerProviderBuilder", e); } } + + /** Reflectively set exception attribute resolver to the {@link SdkTracerProviderBuilder}. */ + public static void setExceptionAttributeResolver( + SdkTracerProviderBuilder sdkTracerProviderBuilder, + ExceptionAttributeResolver exceptionAttributeResolver) { + try { + Method method = + SdkTracerProviderBuilder.class.getDeclaredMethod( + "setExceptionAttributeResolver", ExceptionAttributeResolver.class); + method.setAccessible(true); + method.invoke(sdkTracerProviderBuilder, exceptionAttributeResolver); + } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { + throw new IllegalStateException( + "Error calling setExceptionAttributeResolver on SdkTracerProviderBuilder", e); + } + } } 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 625313dbcb5..fcfec65d47f 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 @@ -14,6 +14,7 @@ import static io.opentelemetry.api.common.AttributeKey.stringArrayKey; import static io.opentelemetry.api.common.AttributeKey.stringKey; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat; +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo; import static java.util.stream.Collectors.joining; import static org.assertj.core.api.Assertions.assertThatCode; import static org.assertj.core.api.Assertions.assertThatThrownBy; @@ -37,6 +38,8 @@ 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; import io.opentelemetry.sdk.testing.time.TestClock; @@ -942,7 +945,8 @@ void addLink() { .build(), parentSpanId, null, - null); + null, + DefaultExceptionAttributeResolver.getInstance()); try { Span span1 = createTestSpan(SpanKind.INTERNAL); Span span2 = createTestSpan(SpanKind.INTERNAL); @@ -1032,6 +1036,7 @@ void addLink_FaultIn() { Context.root(), SpanLimits.getDefault(), spanProcessor, + DefaultExceptionAttributeResolver.getInstance(), testClock, resource, null, @@ -1292,6 +1297,39 @@ void recordException_SpanLimits() { assertThat(event.getTotalAttributeCount() - event.getAttributes().size()).isPositive(); } + @Test + void recordException_CustomResolver() { + ExceptionAttributeResolver exceptionAttributeResolver = + new ExceptionAttributeResolver() { + @Override + public void setExceptionAttributes( + AttributeSetter attributeSetter, Throwable throwable, int maxAttributeLength) { + attributeSetter.setAttribute(ExceptionAttributeResolver.EXCEPTION_TYPE, "type"); + attributeSetter.setAttribute( + ExceptionAttributeResolver.EXCEPTION_STACKTRACE, "stacktrace"); + } + }; + + SdkSpan span = + createTestSpan( + SpanKind.INTERNAL, + SpanLimits.getDefault(), + parentSpanId, + null, + Collections.singletonList(link), + exceptionAttributeResolver); + + span.recordException(new IllegalStateException("error")); + + List events = span.toSpanData().getEvents(); + assertThat(events.size()).isEqualTo(1); + EventData event = events.get(0); + assertThat(event) + .hasAttributesSatisfyingExactly( + equalTo(ExceptionAttributeResolver.EXCEPTION_TYPE, "type"), + equalTo(ExceptionAttributeResolver.EXCEPTION_STACKTRACE, "stacktrace")); + } + @Test void badArgsIgnored() { SdkSpan span = createTestRootSpan(); @@ -1343,6 +1381,7 @@ void onStartOnEndNotRequired() { Context.root(), spanLimits, spanProcessor, + DefaultExceptionAttributeResolver.getInstance(), testClock, resource, AttributesMap.create( @@ -1424,7 +1463,8 @@ private SdkSpan createTestSpanWithAttributes(Map attribute SpanLimits.getDefault(), null, attributesMap, - Collections.singletonList(link)); + Collections.singletonList(link), + DefaultExceptionAttributeResolver.getInstance()); } private SdkSpan createTestRootSpan() { @@ -1433,17 +1473,28 @@ private SdkSpan createTestRootSpan() { SpanLimits.getDefault(), SpanId.getInvalid(), null, - Collections.singletonList(link)); + Collections.singletonList(link), + DefaultExceptionAttributeResolver.getInstance()); } private SdkSpan createTestSpan(SpanKind kind) { return createTestSpan( - kind, SpanLimits.getDefault(), parentSpanId, null, Collections.singletonList(link)); + kind, + SpanLimits.getDefault(), + parentSpanId, + null, + Collections.singletonList(link), + DefaultExceptionAttributeResolver.getInstance()); } private SdkSpan createTestSpan(SpanLimits config) { return createTestSpan( - SpanKind.INTERNAL, config, parentSpanId, null, Collections.singletonList(link)); + SpanKind.INTERNAL, + config, + parentSpanId, + null, + Collections.singletonList(link), + DefaultExceptionAttributeResolver.getInstance()); } private SdkSpan createTestSpan( @@ -1451,7 +1502,8 @@ private SdkSpan createTestSpan( SpanLimits config, @Nullable String parentSpanId, @Nullable AttributesMap attributes, - @Nullable List links) { + @Nullable List links, + ExceptionAttributeResolver exceptionAttributeResolver) { List linksCopy = links == null ? new ArrayList<>() : new ArrayList<>(links); SdkSpan span = @@ -1468,6 +1520,7 @@ private SdkSpan createTestSpan( Context.root(), config, spanProcessor, + exceptionAttributeResolver, testClock, resource, attributes, @@ -1555,6 +1608,7 @@ void testAsSpanData() { Context.root(), spanLimits, spanProcessor, + DefaultExceptionAttributeResolver.getInstance(), 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 578fd9099ee..345f97d52c0 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 @@ -8,7 +8,11 @@ import static io.opentelemetry.api.common.AttributeKey.stringKey; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import io.opentelemetry.api.common.Attributes; @@ -18,6 +22,8 @@ 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; import io.opentelemetry.sdk.trace.internal.SdkTracerProviderUtil; @@ -282,4 +288,22 @@ void suppliesDefaultTracerForEmptyName() { assertThat(tracer.getInstrumentationScopeInfo().getName()) .isEqualTo(SdkTracerProvider.DEFAULT_TRACER_NAME); } + + @Test + void exceptionAttributeResolver() { + int maxAttributeLength = 5; + SdkTracerProviderBuilder builder = + SdkTracerProvider.builder() + .addSpanProcessor(spanProcessor) + .setSpanLimits( + SpanLimits.builder().setMaxAttributeValueLength(maxAttributeLength).build()); + ExceptionAttributeResolver exceptionAttributeResolver = + spy(DefaultExceptionAttributeResolver.getInstance()); + SdkTracerProviderUtil.setExceptionAttributeResolver(builder, exceptionAttributeResolver); + + Exception exception = new Exception("error"); + builder.build().get("tracer").spanBuilder("span").startSpan().recordException(exception).end(); + + verify(exceptionAttributeResolver).setExceptionAttributes(any(), any(), eq(maxAttributeLength)); + } } From 93d9e7278f402303f0ac6b4384bcb502540e2e83 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 5 Jun 2025 11:20:40 -0500 Subject: [PATCH 32/42] fix(deps): update dependency io.opentelemetry.proto:opentelemetry-proto to v1.7.0-alpha (#7362) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Jack Berg --- dependencyManagement/build.gradle.kts | 2 +- .../internal/data/ImmutableProfileData.java | 26 +--- .../data/ImmutableProfileDictionaryData.java | 55 ++++++++ .../exporter/otlp/profiles/ProfileData.java | 32 +---- .../otlp/profiles/ProfileDictionaryData.java | 45 +++++++ .../profiles/ProfileDictionaryMarshaler.java | 120 ++++++++++++++++++ .../otlp/profiles/ProfileMarshaler.java | 78 +----------- .../profiles/ProfilesRequestMarshaler.java | 50 +++++++- .../otlp/profiles/FakeTelemetryUtil.java | 19 ++- .../ProfilesRequestMarshalerTest.java | 16 ++- 10 files changed, 299 insertions(+), 144 deletions(-) create mode 100644 exporters/otlp/profiles/src/main/java/io/opentelemetry/exporter/otlp/internal/data/ImmutableProfileDictionaryData.java create mode 100644 exporters/otlp/profiles/src/main/java/io/opentelemetry/exporter/otlp/profiles/ProfileDictionaryData.java create mode 100644 exporters/otlp/profiles/src/main/java/io/opentelemetry/exporter/otlp/profiles/ProfileDictionaryMarshaler.java diff --git a/dependencyManagement/build.gradle.kts b/dependencyManagement/build.gradle.kts index c376972528b..a6fd0936a41 100644 --- a/dependencyManagement/build.gradle.kts +++ b/dependencyManagement/build.gradle.kts @@ -85,7 +85,7 @@ val DEPENDENCIES = listOf( "io.jaegertracing:jaeger-client:1.8.1", "io.opentelemetry.contrib:opentelemetry-aws-xray-propagator:1.46.0-alpha", "io.opentelemetry.semconv:opentelemetry-semconv-incubating:1.32.0-alpha", - "io.opentelemetry.proto:opentelemetry-proto:1.5.0-alpha", + "io.opentelemetry.proto:opentelemetry-proto:1.7.0-alpha", "io.opentracing:opentracing-api:0.33.0", "io.opentracing:opentracing-noop:0.33.0", "junit:junit:4.13.2", diff --git a/exporters/otlp/profiles/src/main/java/io/opentelemetry/exporter/otlp/internal/data/ImmutableProfileData.java b/exporters/otlp/profiles/src/main/java/io/opentelemetry/exporter/otlp/internal/data/ImmutableProfileData.java index 520139b2452..ae67fde5908 100644 --- a/exporters/otlp/profiles/src/main/java/io/opentelemetry/exporter/otlp/internal/data/ImmutableProfileData.java +++ b/exporters/otlp/profiles/src/main/java/io/opentelemetry/exporter/otlp/internal/data/ImmutableProfileData.java @@ -6,13 +6,8 @@ package io.opentelemetry.exporter.otlp.internal.data; import com.google.auto.value.AutoValue; -import io.opentelemetry.exporter.internal.otlp.AttributeKeyValue; -import io.opentelemetry.exporter.otlp.profiles.AttributeUnitData; -import io.opentelemetry.exporter.otlp.profiles.FunctionData; -import io.opentelemetry.exporter.otlp.profiles.LinkData; -import io.opentelemetry.exporter.otlp.profiles.LocationData; -import io.opentelemetry.exporter.otlp.profiles.MappingData; import io.opentelemetry.exporter.otlp.profiles.ProfileData; +import io.opentelemetry.exporter.otlp.profiles.ProfileDictionaryData; import io.opentelemetry.exporter.otlp.profiles.SampleData; import io.opentelemetry.exporter.otlp.profiles.ValueTypeData; import io.opentelemetry.sdk.common.InstrumentationScopeInfo; @@ -23,8 +18,7 @@ /** * Auto value implementation of {@link ProfileData}, which represents a complete profile, including - * sample types, samples, mappings to binaries, locations, functions, string table, and additional - * metadata. + * sample types, samples, mappings to binaries, locations, and additional metadata. * *

This class is internal and is hence not for public use. Its APIs are unstable and can change * at any time. @@ -42,16 +36,10 @@ public abstract class ImmutableProfileData implements ProfileData { public static ProfileData create( Resource resource, InstrumentationScopeInfo instrumentationScopeInfo, + ProfileDictionaryData profileDictionaryData, List sampleTypes, List samples, - List mappingTable, - List locationTable, List locationIndices, - List functionTable, - List> attributeTable, - List attributeUnits, - List linkTable, - List stringTable, long timeNanos, long durationNanos, ValueTypeData periodType, @@ -66,16 +54,10 @@ public static ProfileData create( return new AutoValue_ImmutableProfileData( resource, instrumentationScopeInfo, + profileDictionaryData, sampleTypes, samples, - mappingTable, - locationTable, locationIndices, - functionTable, - attributeTable, - attributeUnits, - linkTable, - stringTable, timeNanos, durationNanos, periodType, diff --git a/exporters/otlp/profiles/src/main/java/io/opentelemetry/exporter/otlp/internal/data/ImmutableProfileDictionaryData.java b/exporters/otlp/profiles/src/main/java/io/opentelemetry/exporter/otlp/internal/data/ImmutableProfileDictionaryData.java new file mode 100644 index 00000000000..4ee54e7d9ea --- /dev/null +++ b/exporters/otlp/profiles/src/main/java/io/opentelemetry/exporter/otlp/internal/data/ImmutableProfileDictionaryData.java @@ -0,0 +1,55 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.exporter.otlp.internal.data; + +import com.google.auto.value.AutoValue; +import io.opentelemetry.exporter.internal.otlp.AttributeKeyValue; +import io.opentelemetry.exporter.otlp.profiles.AttributeUnitData; +import io.opentelemetry.exporter.otlp.profiles.FunctionData; +import io.opentelemetry.exporter.otlp.profiles.LinkData; +import io.opentelemetry.exporter.otlp.profiles.LocationData; +import io.opentelemetry.exporter.otlp.profiles.MappingData; +import io.opentelemetry.exporter.otlp.profiles.ProfileDictionaryData; +import java.util.List; +import javax.annotation.concurrent.Immutable; + +/** + * Auto value implementation of {@link ProfileDictionaryData}, which represents profiles data shared + * across the entire message being sent. + * + *

This class is internal and is hence not for public use. Its APIs are unstable and can change + * at any time. + */ +@Immutable +@AutoValue +public abstract class ImmutableProfileDictionaryData implements ProfileDictionaryData { + + /** + * Returns a new ProfileData representing the given data. + * + * @return a new ProfileData representing the given data. + */ + @SuppressWarnings("TooManyParameters") + public static ProfileDictionaryData create( + List mappingTable, + List locationTable, + List functionTable, + List> attributeTable, + List attributeUnits, + List linkTable, + List stringTable) { + return new AutoValue_ImmutableProfileDictionaryData( + mappingTable, + locationTable, + functionTable, + attributeTable, + attributeUnits, + linkTable, + stringTable); + } + + ImmutableProfileDictionaryData() {} +} diff --git a/exporters/otlp/profiles/src/main/java/io/opentelemetry/exporter/otlp/profiles/ProfileData.java b/exporters/otlp/profiles/src/main/java/io/opentelemetry/exporter/otlp/profiles/ProfileData.java index 0f6ef8e6ea3..54f5e1743ac 100644 --- a/exporters/otlp/profiles/src/main/java/io/opentelemetry/exporter/otlp/profiles/ProfileData.java +++ b/exporters/otlp/profiles/src/main/java/io/opentelemetry/exporter/otlp/profiles/ProfileData.java @@ -6,7 +6,6 @@ package io.opentelemetry.exporter.otlp.profiles; import io.opentelemetry.api.internal.OtelEncodingUtils; -import io.opentelemetry.exporter.internal.otlp.AttributeKeyValue; import io.opentelemetry.sdk.common.InstrumentationScopeInfo; import io.opentelemetry.sdk.resources.Resource; import java.nio.ByteBuffer; @@ -16,7 +15,7 @@ /** * Represents a complete profile, including sample types, samples, mappings to binaries, locations, - * functions, string table, and additional metadata. + * and additional metadata. * * @see "profiles.proto::Profile" */ @@ -29,41 +28,18 @@ public interface ProfileData { /** Returns the instrumentation scope that generated this profile. */ InstrumentationScopeInfo getInstrumentationScopeInfo(); + /** Returns the dictionary data of this profile. */ + ProfileDictionaryData getProfileDictionaryData(); + /** A description of the samples associated with each Sample.value. */ List getSampleTypes(); /** The set of samples recorded in this profile. */ List getSamples(); - /** - * Mapping from address ranges to the image/binary/library mapped into that address range. - * mapping[0] will be the main binary. - */ - List getMappingTable(); - - /** Locations referenced by samples via location_indices. */ - List getLocationTable(); - /** Array of locations referenced by samples. */ List getLocationIndices(); - /** Functions referenced by locations. */ - List getFunctionTable(); - - /** Lookup table for attributes. */ - List> getAttributeTable(); - - /** Represents a mapping between Attribute Keys and Units. */ - List getAttributeUnits(); - - /** Lookup table for links. */ - List getLinkTable(); - - /** - * A common table for strings referenced by various messages. string_table[0] must always be "". - */ - List getStringTable(); - /** Time of collection (UTC) represented as nanoseconds past the epoch. */ long getTimeNanos(); diff --git a/exporters/otlp/profiles/src/main/java/io/opentelemetry/exporter/otlp/profiles/ProfileDictionaryData.java b/exporters/otlp/profiles/src/main/java/io/opentelemetry/exporter/otlp/profiles/ProfileDictionaryData.java new file mode 100644 index 00000000000..e67b27d8e75 --- /dev/null +++ b/exporters/otlp/profiles/src/main/java/io/opentelemetry/exporter/otlp/profiles/ProfileDictionaryData.java @@ -0,0 +1,45 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.exporter.otlp.profiles; + +import io.opentelemetry.exporter.internal.otlp.AttributeKeyValue; +import java.util.List; +import javax.annotation.concurrent.Immutable; + +/** + * Represents profiles data shared across the entire message being sent. + * + * @see "profiles.proto::ProfilesDictionary" + */ +@Immutable +public interface ProfileDictionaryData { + + /** + * Mapping from address ranges to the image/binary/library mapped into that address range. + * mapping[0] will be the main binary. + */ + List getMappingTable(); + + /** Locations referenced by samples via location_indices. */ + List getLocationTable(); + + /** Functions referenced by locations. */ + List getFunctionTable(); + + /** Lookup table for attributes. */ + List> getAttributeTable(); + + /** Represents a mapping between Attribute Keys and Units. */ + List getAttributeUnits(); + + /** Lookup table for links. */ + List getLinkTable(); + + /** + * A common table for strings referenced by various messages. string_table[0] must always be "". + */ + List getStringTable(); +} diff --git a/exporters/otlp/profiles/src/main/java/io/opentelemetry/exporter/otlp/profiles/ProfileDictionaryMarshaler.java b/exporters/otlp/profiles/src/main/java/io/opentelemetry/exporter/otlp/profiles/ProfileDictionaryMarshaler.java new file mode 100644 index 00000000000..234040a7d84 --- /dev/null +++ b/exporters/otlp/profiles/src/main/java/io/opentelemetry/exporter/otlp/profiles/ProfileDictionaryMarshaler.java @@ -0,0 +1,120 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.exporter.otlp.profiles; + +import io.opentelemetry.exporter.internal.marshal.MarshalerUtil; +import io.opentelemetry.exporter.internal.marshal.MarshalerWithSize; +import io.opentelemetry.exporter.internal.marshal.Serializer; +import io.opentelemetry.exporter.internal.otlp.KeyValueMarshaler; +import io.opentelemetry.proto.profiles.v1development.internal.ProfilesDictionary; +import java.io.IOException; +import java.nio.charset.StandardCharsets; + +final class ProfileDictionaryMarshaler extends MarshalerWithSize { + + private final MappingMarshaler[] mappingTableMarshalers; + private final LocationMarshaler[] locationTableMarshalers; + private final FunctionMarshaler[] functionTableMarshalers; + private final KeyValueMarshaler[] attributeTableMarshalers; + private final AttributeUnitMarshaler[] attributeUnitMarshalers; + private final LinkMarshaler[] linkTableMarshalers; + private final byte[][] stringTable; + + static ProfileDictionaryMarshaler create(ProfileDictionaryData profileDictionaryData) { + + MappingMarshaler[] mappingMarshalers = + MappingMarshaler.createRepeated(profileDictionaryData.getMappingTable()); + LocationMarshaler[] locationMarshalers = + LocationMarshaler.createRepeated(profileDictionaryData.getLocationTable()); + FunctionMarshaler[] functionMarshalers = + FunctionMarshaler.createRepeated(profileDictionaryData.getFunctionTable()); + KeyValueMarshaler[] attributeTableMarshalers = + KeyValueMarshaler.createRepeated(profileDictionaryData.getAttributeTable()); + AttributeUnitMarshaler[] attributeUnitsMarshalers = + AttributeUnitMarshaler.createRepeated(profileDictionaryData.getAttributeUnits()); + LinkMarshaler[] linkMarshalers = + LinkMarshaler.createRepeated(profileDictionaryData.getLinkTable()); + + byte[][] convertedStrings = new byte[profileDictionaryData.getStringTable().size()][]; + for (int i = 0; i < profileDictionaryData.getStringTable().size(); i++) { + convertedStrings[i] = + profileDictionaryData.getStringTable().get(i).getBytes(StandardCharsets.UTF_8); + } + + return new ProfileDictionaryMarshaler( + mappingMarshalers, + locationMarshalers, + functionMarshalers, + attributeTableMarshalers, + attributeUnitsMarshalers, + linkMarshalers, + convertedStrings); + } + + private ProfileDictionaryMarshaler( + MappingMarshaler[] mappingTableMarshalers, + LocationMarshaler[] locationTableMarshalers, + FunctionMarshaler[] functionTableMarshalers, + KeyValueMarshaler[] attributeTableMarshalers, + AttributeUnitMarshaler[] attributeUnitMarshalers, + LinkMarshaler[] linkTableMarshalers, + byte[][] stringTableUtf8) { + super( + calculateSize( + mappingTableMarshalers, + locationTableMarshalers, + functionTableMarshalers, + attributeTableMarshalers, + attributeUnitMarshalers, + linkTableMarshalers, + stringTableUtf8)); + this.mappingTableMarshalers = mappingTableMarshalers; + this.locationTableMarshalers = locationTableMarshalers; + this.functionTableMarshalers = functionTableMarshalers; + this.attributeTableMarshalers = attributeTableMarshalers; + this.attributeUnitMarshalers = attributeUnitMarshalers; + this.linkTableMarshalers = linkTableMarshalers; + this.stringTable = stringTableUtf8; + } + + @Override + protected void writeTo(Serializer output) throws IOException { + output.serializeRepeatedMessage(ProfilesDictionary.MAPPING_TABLE, mappingTableMarshalers); + output.serializeRepeatedMessage(ProfilesDictionary.LOCATION_TABLE, locationTableMarshalers); + output.serializeRepeatedMessage(ProfilesDictionary.FUNCTION_TABLE, functionTableMarshalers); + output.serializeRepeatedMessage(ProfilesDictionary.ATTRIBUTE_TABLE, attributeTableMarshalers); + output.serializeRepeatedMessage(ProfilesDictionary.ATTRIBUTE_UNITS, attributeUnitMarshalers); + output.serializeRepeatedMessage(ProfilesDictionary.LINK_TABLE, linkTableMarshalers); + output.serializeRepeatedString(ProfilesDictionary.STRING_TABLE, stringTable); + } + + private static int calculateSize( + MappingMarshaler[] mappingMarshalers, + LocationMarshaler[] locationMarshalers, + FunctionMarshaler[] functionMarshalers, + KeyValueMarshaler[] attributeTableMarshalers, + AttributeUnitMarshaler[] attributeUnitMarshalers, + LinkMarshaler[] linkMarshalers, + byte[][] stringTable) { + int size; + size = 0; + size += MarshalerUtil.sizeRepeatedMessage(ProfilesDictionary.MAPPING_TABLE, mappingMarshalers); + size += + MarshalerUtil.sizeRepeatedMessage(ProfilesDictionary.LOCATION_TABLE, locationMarshalers); + size += + MarshalerUtil.sizeRepeatedMessage(ProfilesDictionary.FUNCTION_TABLE, functionMarshalers); + size += + MarshalerUtil.sizeRepeatedMessage( + ProfilesDictionary.ATTRIBUTE_TABLE, attributeTableMarshalers); + size += + MarshalerUtil.sizeRepeatedMessage( + ProfilesDictionary.ATTRIBUTE_UNITS, attributeUnitMarshalers); + size += MarshalerUtil.sizeRepeatedMessage(ProfilesDictionary.LINK_TABLE, linkMarshalers); + size += MarshalerUtil.sizeRepeatedString(ProfilesDictionary.STRING_TABLE, stringTable); + + return size; + } +} diff --git a/exporters/otlp/profiles/src/main/java/io/opentelemetry/exporter/otlp/profiles/ProfileMarshaler.java b/exporters/otlp/profiles/src/main/java/io/opentelemetry/exporter/otlp/profiles/ProfileMarshaler.java index 8d04bbbf94f..07b66848489 100644 --- a/exporters/otlp/profiles/src/main/java/io/opentelemetry/exporter/otlp/profiles/ProfileMarshaler.java +++ b/exporters/otlp/profiles/src/main/java/io/opentelemetry/exporter/otlp/profiles/ProfileMarshaler.java @@ -8,25 +8,16 @@ import io.opentelemetry.exporter.internal.marshal.MarshalerUtil; import io.opentelemetry.exporter.internal.marshal.MarshalerWithSize; import io.opentelemetry.exporter.internal.marshal.Serializer; -import io.opentelemetry.exporter.internal.otlp.KeyValueMarshaler; import io.opentelemetry.proto.profiles.v1development.internal.Profile; import java.io.IOException; import java.nio.ByteBuffer; -import java.nio.charset.StandardCharsets; import java.util.List; final class ProfileMarshaler extends MarshalerWithSize { private final ValueTypeMarshaler[] sampleTypeMarshalers; private final SampleMarshaler[] sampleMarshalers; - private final MappingMarshaler[] mappingTableMarshalers; - private final LocationMarshaler[] locationTableMarshalers; private final List locationIndices; - private final FunctionMarshaler[] functionTableMarshalers; - private final KeyValueMarshaler[] attributeTableMarshalers; - private final AttributeUnitMarshaler[] attributeUnitMarshalers; - private final LinkMarshaler[] linkTableMarshalers; - private final byte[][] stringTable; private final long timeNanos; private final long durationNanos; private final ValueTypeMarshaler periodTypeMarshaler; @@ -44,38 +35,15 @@ static ProfileMarshaler create(ProfileData profileData) { ValueTypeMarshaler[] sampleTypeMarshalers = ValueTypeMarshaler.createRepeated(profileData.getSampleTypes()); SampleMarshaler[] sampleMarshalers = SampleMarshaler.createRepeated(profileData.getSamples()); - MappingMarshaler[] mappingMarshalers = - MappingMarshaler.createRepeated(profileData.getMappingTable()); - LocationMarshaler[] locationMarshalers = - LocationMarshaler.createRepeated(profileData.getLocationTable()); - FunctionMarshaler[] functionMarshalers = - FunctionMarshaler.createRepeated(profileData.getFunctionTable()); - KeyValueMarshaler[] attributeTableMarshalers = - KeyValueMarshaler.createRepeated(profileData.getAttributeTable()); - AttributeUnitMarshaler[] attributeUnitsMarshalers = - AttributeUnitMarshaler.createRepeated(profileData.getAttributeUnits()); - LinkMarshaler[] linkMarshalers = LinkMarshaler.createRepeated(profileData.getLinkTable()); ValueTypeMarshaler periodTypeMarshaler = ValueTypeMarshaler.create(profileData.getPeriodType()); - byte[][] convertedStrings = new byte[profileData.getStringTable().size()][]; - for (int i = 0; i < profileData.getStringTable().size(); i++) { - convertedStrings[i] = profileData.getStringTable().get(i).getBytes(StandardCharsets.UTF_8); - } - int droppedAttributesCount = profileData.getTotalAttributeCount() - profileData.getAttributeIndices().size(); return new ProfileMarshaler( sampleTypeMarshalers, sampleMarshalers, - mappingMarshalers, - locationMarshalers, profileData.getLocationIndices(), - functionMarshalers, - attributeTableMarshalers, - attributeUnitsMarshalers, - linkMarshalers, - convertedStrings, profileData.getTimeNanos(), profileData.getDurationNanos(), periodTypeMarshaler, @@ -92,14 +60,7 @@ static ProfileMarshaler create(ProfileData profileData) { private ProfileMarshaler( ValueTypeMarshaler[] sampleTypeMarshalers, SampleMarshaler[] sampleMarshalers, - MappingMarshaler[] mappingTableMarshalers, - LocationMarshaler[] locationTableMarshalers, List locationIndices, - FunctionMarshaler[] functionTableMarshalers, - KeyValueMarshaler[] attributeTableMarshalers, - AttributeUnitMarshaler[] attributeUnitMarshalers, - LinkMarshaler[] linkTableMarshalers, - byte[][] stringTableUtf8, long timeNanos, long durationNanos, ValueTypeMarshaler periodTypeMarshaler, @@ -115,14 +76,7 @@ private ProfileMarshaler( calculateSize( sampleTypeMarshalers, sampleMarshalers, - mappingTableMarshalers, - locationTableMarshalers, locationIndices, - functionTableMarshalers, - attributeTableMarshalers, - attributeUnitMarshalers, - linkTableMarshalers, - stringTableUtf8, timeNanos, durationNanos, periodTypeMarshaler, @@ -136,14 +90,7 @@ private ProfileMarshaler( originalPayload)); this.sampleTypeMarshalers = sampleTypeMarshalers; this.sampleMarshalers = sampleMarshalers; - this.mappingTableMarshalers = mappingTableMarshalers; - this.locationTableMarshalers = locationTableMarshalers; this.locationIndices = locationIndices; - this.functionTableMarshalers = functionTableMarshalers; - this.attributeTableMarshalers = attributeTableMarshalers; - this.attributeUnitMarshalers = attributeUnitMarshalers; - this.linkTableMarshalers = linkTableMarshalers; - this.stringTable = stringTableUtf8; this.timeNanos = timeNanos; this.durationNanos = durationNanos; this.periodTypeMarshaler = periodTypeMarshaler; @@ -161,20 +108,13 @@ private ProfileMarshaler( protected void writeTo(Serializer output) throws IOException { output.serializeRepeatedMessage(Profile.SAMPLE_TYPE, sampleTypeMarshalers); output.serializeRepeatedMessage(Profile.SAMPLE, sampleMarshalers); - output.serializeRepeatedMessage(Profile.MAPPING_TABLE, mappingTableMarshalers); - output.serializeRepeatedMessage(Profile.LOCATION_TABLE, locationTableMarshalers); output.serializeRepeatedInt32(Profile.LOCATION_INDICES, locationIndices); - output.serializeRepeatedMessage(Profile.FUNCTION_TABLE, functionTableMarshalers); - output.serializeRepeatedMessage(Profile.ATTRIBUTE_TABLE, attributeTableMarshalers); - output.serializeRepeatedMessage(Profile.ATTRIBUTE_UNITS, attributeUnitMarshalers); - output.serializeRepeatedMessage(Profile.LINK_TABLE, linkTableMarshalers); - output.serializeRepeatedString(Profile.STRING_TABLE, stringTable); output.serializeInt64(Profile.TIME_NANOS, timeNanos); output.serializeInt64(Profile.DURATION_NANOS, durationNanos); output.serializeMessage(Profile.PERIOD_TYPE, periodTypeMarshaler); output.serializeInt64(Profile.PERIOD, period); output.serializeRepeatedInt32(Profile.COMMENT_STRINDICES, comment); - output.serializeInt32(Profile.DEFAULT_SAMPLE_TYPE_STRINDEX, defaultSampleType); + output.serializeInt32(Profile.DEFAULT_SAMPLE_TYPE_INDEX, defaultSampleType); output.serializeBytes(Profile.PROFILE_ID, profileId); output.serializeRepeatedInt32(Profile.ATTRIBUTE_INDICES, attributeIndices); @@ -186,14 +126,7 @@ protected void writeTo(Serializer output) throws IOException { private static int calculateSize( ValueTypeMarshaler[] sampleTypeMarshalers, SampleMarshaler[] sampleMarshalers, - MappingMarshaler[] mappingMarshalers, - LocationMarshaler[] locationMarshalers, List locationIndices, - FunctionMarshaler[] functionMarshalers, - KeyValueMarshaler[] attributeTableMarshalers, - AttributeUnitMarshaler[] attributeUnitMarshalers, - LinkMarshaler[] linkMarshalers, - byte[][] stringTable, long timeNanos, long durationNanos, ValueTypeMarshaler periodTypeMarshaler, @@ -209,20 +142,13 @@ private static int calculateSize( size = 0; size += MarshalerUtil.sizeRepeatedMessage(Profile.SAMPLE_TYPE, sampleTypeMarshalers); size += MarshalerUtil.sizeRepeatedMessage(Profile.SAMPLE, sampleMarshalers); - size += MarshalerUtil.sizeRepeatedMessage(Profile.MAPPING_TABLE, mappingMarshalers); - size += MarshalerUtil.sizeRepeatedMessage(Profile.LOCATION_TABLE, locationMarshalers); size += MarshalerUtil.sizeRepeatedInt32(Profile.LOCATION_INDICES, locationIndices); - size += MarshalerUtil.sizeRepeatedMessage(Profile.FUNCTION_TABLE, functionMarshalers); - size += MarshalerUtil.sizeRepeatedMessage(Profile.ATTRIBUTE_TABLE, attributeTableMarshalers); - size += MarshalerUtil.sizeRepeatedMessage(Profile.ATTRIBUTE_UNITS, attributeUnitMarshalers); - size += MarshalerUtil.sizeRepeatedMessage(Profile.LINK_TABLE, linkMarshalers); - size += MarshalerUtil.sizeRepeatedString(Profile.STRING_TABLE, stringTable); size += MarshalerUtil.sizeInt64(Profile.TIME_NANOS, timeNanos); size += MarshalerUtil.sizeInt64(Profile.DURATION_NANOS, durationNanos); size += MarshalerUtil.sizeMessage(Profile.PERIOD_TYPE, periodTypeMarshaler); size += MarshalerUtil.sizeInt64(Profile.PERIOD, period); size += MarshalerUtil.sizeRepeatedInt32(Profile.COMMENT_STRINDICES, comment); - size += MarshalerUtil.sizeInt64(Profile.DEFAULT_SAMPLE_TYPE_STRINDEX, defaultSampleType); + size += MarshalerUtil.sizeInt64(Profile.DEFAULT_SAMPLE_TYPE_INDEX, defaultSampleType); size += MarshalerUtil.sizeBytes(Profile.PROFILE_ID, profileId); size += MarshalerUtil.sizeRepeatedInt32(Profile.ATTRIBUTE_INDICES, attributeIndices); diff --git a/exporters/otlp/profiles/src/main/java/io/opentelemetry/exporter/otlp/profiles/ProfilesRequestMarshaler.java b/exporters/otlp/profiles/src/main/java/io/opentelemetry/exporter/otlp/profiles/ProfilesRequestMarshaler.java index 3d57825899a..4c7b953352d 100644 --- a/exporters/otlp/profiles/src/main/java/io/opentelemetry/exporter/otlp/profiles/ProfilesRequestMarshaler.java +++ b/exporters/otlp/profiles/src/main/java/io/opentelemetry/exporter/otlp/profiles/ProfilesRequestMarshaler.java @@ -5,14 +5,18 @@ package io.opentelemetry.exporter.otlp.profiles; +import static io.opentelemetry.proto.collector.profiles.v1development.internal.ExportProfilesServiceRequest.DICTIONARY; + import io.opentelemetry.exporter.internal.marshal.Marshaler; import io.opentelemetry.exporter.internal.marshal.MarshalerUtil; import io.opentelemetry.exporter.internal.marshal.MarshalerWithSize; import io.opentelemetry.exporter.internal.marshal.ProtoFieldInfo; import io.opentelemetry.exporter.internal.marshal.Serializer; +import io.opentelemetry.exporter.otlp.internal.data.ImmutableProfileDictionaryData; import io.opentelemetry.proto.collector.profiles.v1development.internal.ExportProfilesServiceRequest; import java.io.IOException; import java.util.Collection; +import java.util.Collections; /** * {@link Marshaler} to convert SDK {@link ProfileData} to OTLP ExportProfilesServiceRequest. @@ -22,26 +26,66 @@ */ public final class ProfilesRequestMarshaler extends MarshalerWithSize { + private static final ProfileDictionaryData EMPTY_DICTIONARY_DATA = + ImmutableProfileDictionaryData.create( + Collections.emptyList(), + Collections.emptyList(), + Collections.emptyList(), + Collections.emptyList(), + Collections.emptyList(), + Collections.emptyList(), + Collections.emptyList()); + private static final ProtoFieldInfo RESOURCE_PROFILES = ExportProfilesServiceRequest.RESOURCE_PROFILES; private final ResourceProfilesMarshaler[] resourceProfilesMarshalers; + private final ProfileDictionaryMarshaler profileDictionaryMarshaler; /** * Returns a {@link ProfilesRequestMarshaler} that can be used to convert the provided {@link * ProfileData} into a serialized OTLP ExportProfilesServiceRequest. */ public static ProfilesRequestMarshaler create(Collection profileList) { - return new ProfilesRequestMarshaler(ResourceProfilesMarshaler.create(profileList)); + // Verify all profiles in batch have identical dictionary + ProfileDictionaryData profileDictionaryData = null; + for (ProfileData profileData : profileList) { + if (profileDictionaryData == null) { + profileDictionaryData = profileData.getProfileDictionaryData(); + } else if (profileDictionaryData != profileData.getProfileDictionaryData()) { + throw new IllegalArgumentException( + "All profiles in batch must have identical ProfileDictionaryData"); + } + } + + ProfileDictionaryMarshaler profileDictionaryMarshaler = + ProfileDictionaryMarshaler.create( + profileDictionaryData == null ? EMPTY_DICTIONARY_DATA : profileDictionaryData); + + return new ProfilesRequestMarshaler( + ResourceProfilesMarshaler.create(profileList), profileDictionaryMarshaler); } - private ProfilesRequestMarshaler(ResourceProfilesMarshaler[] resourceProfilesMarshalers) { - super(MarshalerUtil.sizeRepeatedMessage(RESOURCE_PROFILES, resourceProfilesMarshalers)); + private ProfilesRequestMarshaler( + ResourceProfilesMarshaler[] resourceProfilesMarshalers, + ProfileDictionaryMarshaler profileDictionaryMarshaler) { + super(calculateSize(resourceProfilesMarshalers, profileDictionaryMarshaler)); this.resourceProfilesMarshalers = resourceProfilesMarshalers; + this.profileDictionaryMarshaler = profileDictionaryMarshaler; } @Override public void writeTo(Serializer output) throws IOException { output.serializeRepeatedMessage(RESOURCE_PROFILES, resourceProfilesMarshalers); + output.serializeMessage(DICTIONARY, profileDictionaryMarshaler); + } + + private static int calculateSize( + ResourceProfilesMarshaler[] resourceProfilesMarshalers, + ProfileDictionaryMarshaler profileDictionaryMarshaler) { + int size = 0; + size += MarshalerUtil.sizeRepeatedMessage(RESOURCE_PROFILES, resourceProfilesMarshalers); + size += MarshalerUtil.sizeMessage(DICTIONARY, profileDictionaryMarshaler); + return size; } } diff --git a/exporters/otlp/profiles/src/test/java/io/opentelemetry/exporter/otlp/profiles/FakeTelemetryUtil.java b/exporters/otlp/profiles/src/test/java/io/opentelemetry/exporter/otlp/profiles/FakeTelemetryUtil.java index 2c511bac6bf..eb997485fcf 100644 --- a/exporters/otlp/profiles/src/test/java/io/opentelemetry/exporter/otlp/profiles/FakeTelemetryUtil.java +++ b/exporters/otlp/profiles/src/test/java/io/opentelemetry/exporter/otlp/profiles/FakeTelemetryUtil.java @@ -7,6 +7,7 @@ import io.opentelemetry.api.common.Attributes; import io.opentelemetry.exporter.otlp.internal.data.ImmutableProfileData; +import io.opentelemetry.exporter.otlp.internal.data.ImmutableProfileDictionaryData; import io.opentelemetry.exporter.otlp.internal.data.ImmutableValueTypeData; import io.opentelemetry.sdk.common.InstrumentationScopeInfo; import io.opentelemetry.sdk.resources.Resource; @@ -16,6 +17,16 @@ // TODO eventually merge with io.opentelemetry.exporter.otlp.testing.internal.FakeTelemetryUtil class FakeTelemetryUtil { + private static final ProfileDictionaryData EMPTY_PROFILE_DICTIONARY_DATA = + ImmutableProfileDictionaryData.create( + Collections.emptyList(), + Collections.emptyList(), + Collections.emptyList(), + Collections.emptyList(), + Collections.emptyList(), + Collections.emptyList(), + Collections.emptyList()); + private FakeTelemetryUtil() {} private static final InstrumentationScopeInfo SCOPE_INFO = @@ -30,13 +41,7 @@ static ProfileData generateFakeProfileData() { return ImmutableProfileData.create( Resource.create(Attributes.empty()), SCOPE_INFO, - Collections.emptyList(), - Collections.emptyList(), - Collections.emptyList(), - Collections.emptyList(), - Collections.emptyList(), - Collections.emptyList(), - Collections.emptyList(), + EMPTY_PROFILE_DICTIONARY_DATA, Collections.emptyList(), Collections.emptyList(), Collections.emptyList(), diff --git a/exporters/otlp/profiles/src/test/java/io/opentelemetry/exporter/otlp/profiles/ProfilesRequestMarshalerTest.java b/exporters/otlp/profiles/src/test/java/io/opentelemetry/exporter/otlp/profiles/ProfilesRequestMarshalerTest.java index 3ed1d9159d2..33ddc1c0f47 100644 --- a/exporters/otlp/profiles/src/test/java/io/opentelemetry/exporter/otlp/profiles/ProfilesRequestMarshalerTest.java +++ b/exporters/otlp/profiles/src/test/java/io/opentelemetry/exporter/otlp/profiles/ProfilesRequestMarshalerTest.java @@ -20,6 +20,7 @@ import io.opentelemetry.exporter.otlp.internal.data.ImmutableLocationData; import io.opentelemetry.exporter.otlp.internal.data.ImmutableMappingData; import io.opentelemetry.exporter.otlp.internal.data.ImmutableProfileData; +import io.opentelemetry.exporter.otlp.internal.data.ImmutableProfileDictionaryData; import io.opentelemetry.exporter.otlp.internal.data.ImmutableSampleData; import io.opentelemetry.exporter.otlp.internal.data.ImmutableValueTypeData; import io.opentelemetry.proto.common.v1.InstrumentationScope; @@ -145,16 +146,17 @@ void compareResourceProfilesMarshaling() { ImmutableProfileData.create( Resource.create(Attributes.empty()), InstrumentationScopeInfo.create("testscope"), - Collections.emptyList(), - Collections.emptyList(), + ImmutableProfileDictionaryData.create( + Collections.emptyList(), + Collections.emptyList(), + Collections.emptyList(), + Collections.emptyList(), + Collections.emptyList(), + Collections.emptyList(), + Collections.emptyList()), Collections.emptyList(), Collections.emptyList(), listOf(1, 2), - Collections.emptyList(), - Collections.emptyList(), - Collections.emptyList(), - Collections.emptyList(), - Collections.emptyList(), 5L, 6L, ImmutableValueTypeData.create(1, 2, AggregationTemporality.CUMULATIVE), From 9e76c90a045f04211903c5b3c559e27fcaa6c80b Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 5 Jun 2025 16:40:43 -0500 Subject: [PATCH 33/42] fix(deps): update dependency io.netty:netty-bom to v4.2.2.final (#7389) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- dependencyManagement/build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dependencyManagement/build.gradle.kts b/dependencyManagement/build.gradle.kts index a6fd0936a41..c7d1999a323 100644 --- a/dependencyManagement/build.gradle.kts +++ b/dependencyManagement/build.gradle.kts @@ -19,7 +19,7 @@ val DEPENDENCY_BOMS = listOf( "com.squareup.okhttp3:okhttp-bom:4.12.0", "com.squareup.okio:okio-bom:3.12.0", // applies to transitive dependencies of okhttp "io.grpc:grpc-bom:1.73.0", - "io.netty:netty-bom:4.2.1.Final", + "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", From d41c099e62257800bb11bd5fd5420888f8de87ce Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 5 Jun 2025 16:57:05 -0500 Subject: [PATCH 34/42] chore(deps): update weekly update (#7352) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/benchmark-tags.yml | 2 +- .github/workflows/benchmark.yml | 2 +- .github/workflows/build-tracecontext-testsuite.yml | 2 +- .github/workflows/build.yml | 4 ++-- .github/workflows/codeql.yml | 6 +++--- .github/workflows/gradle-wrapper-validation.yml | 2 +- .github/workflows/javadoc-crawler.yml | 2 +- .github/workflows/ossf-scorecard.yml | 4 ++-- .github/workflows/owasp-dependency-check-daily.yml | 2 +- .github/workflows/release.yml | 2 +- integration-tests/tracecontext/docker/Dockerfile | 4 ++-- 11 files changed, 16 insertions(+), 16 deletions(-) diff --git a/.github/workflows/benchmark-tags.yml b/.github/workflows/benchmark-tags.yml index 05a27337147..9c0f1702ccc 100644 --- a/.github/workflows/benchmark-tags.yml +++ b/.github/workflows/benchmark-tags.yml @@ -56,7 +56,7 @@ jobs: java-version: 17 - name: Set up gradle - uses: gradle/actions/setup-gradle@06832c7b30a0129d7fb559bcc6e43d26f6374244 # v4.3.1 + uses: gradle/actions/setup-gradle@8379f6a1328ee0e06e2bb424dadb7b159856a326 # v4.4.0 - name: Run jmh run: ./gradlew jmhJar diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml index 2e80577ef74..27a43c2c949 100644 --- a/.github/workflows/benchmark.yml +++ b/.github/workflows/benchmark.yml @@ -26,7 +26,7 @@ jobs: java-version: 17 - name: Set up gradle - uses: gradle/actions/setup-gradle@06832c7b30a0129d7fb559bcc6e43d26f6374244 # v4.3.1 + uses: gradle/actions/setup-gradle@8379f6a1328ee0e06e2bb424dadb7b159856a326 # v4.4.0 - name: Run jmh run: ./gradlew jmhJar diff --git a/.github/workflows/build-tracecontext-testsuite.yml b/.github/workflows/build-tracecontext-testsuite.yml index 0cffca0cfd6..e73592737e3 100644 --- a/.github/workflows/build-tracecontext-testsuite.yml +++ b/.github/workflows/build-tracecontext-testsuite.yml @@ -29,7 +29,7 @@ jobs: password: ${{ secrets.GITHUB_TOKEN }} - name: Build and push - uses: docker/build-push-action@1dc73863535b631f98b2378be8619f83b136f4a0 # v6.17.0 + uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6.18.0 with: context: integration-tests/tracecontext/docker push: true diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 66cf614c2de..047e602ac8b 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -69,7 +69,7 @@ jobs: java-version: 17 - name: Set up gradle - uses: gradle/actions/setup-gradle@06832c7b30a0129d7fb559bcc6e43d26f6374244 # v4.3.1 + uses: gradle/actions/setup-gradle@8379f6a1328ee0e06e2bb424dadb7b159856a326 # v4.4.0 - name: Build run: > ./gradlew build @@ -145,7 +145,7 @@ jobs: java-version: 17 - name: Set up gradle - uses: gradle/actions/setup-gradle@06832c7b30a0129d7fb559bcc6e43d26f6374244 # v4.3.1 + uses: gradle/actions/setup-gradle@8379f6a1328ee0e06e2bb424dadb7b159856a326 # v4.4.0 # skipping release branches because the versions in those branches are not snapshots # (also this skips pull requests) if: ${{ github.ref_name == 'main' && github.repository == 'open-telemetry/opentelemetry-java' }} diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index efa61755815..eadfaa7abc6 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -34,10 +34,10 @@ jobs: java-version: 17 - name: Set up gradle - uses: gradle/actions/setup-gradle@06832c7b30a0129d7fb559bcc6e43d26f6374244 # v4.3.1 + uses: gradle/actions/setup-gradle@8379f6a1328ee0e06e2bb424dadb7b159856a326 # v4.4.0 - name: Initialize CodeQL - uses: github/codeql-action/init@60168efe1c415ce0f5521ea06d5c2062adbeed1b # v3.28.17 + uses: github/codeql-action/init@fca7ace96b7d713c7035871441bd52efbe39e27e # v3.28.19 with: languages: java, actions # using "latest" helps to keep up with the latest Kotlin support @@ -51,4 +51,4 @@ jobs: run: ./gradlew assemble --no-build-cache --no-daemon - name: Perform CodeQL analysis - uses: github/codeql-action/analyze@60168efe1c415ce0f5521ea06d5c2062adbeed1b # v3.28.17 + uses: github/codeql-action/analyze@fca7ace96b7d713c7035871441bd52efbe39e27e # v3.28.19 diff --git a/.github/workflows/gradle-wrapper-validation.yml b/.github/workflows/gradle-wrapper-validation.yml index 365d5b84039..a9e18e8aa4c 100644 --- a/.github/workflows/gradle-wrapper-validation.yml +++ b/.github/workflows/gradle-wrapper-validation.yml @@ -13,4 +13,4 @@ jobs: steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - - uses: gradle/actions/wrapper-validation@06832c7b30a0129d7fb559bcc6e43d26f6374244 # v4.3.1 + - uses: gradle/actions/wrapper-validation@8379f6a1328ee0e06e2bb424dadb7b159856a326 # v4.4.0 diff --git a/.github/workflows/javadoc-crawler.yml b/.github/workflows/javadoc-crawler.yml index 9275265dd79..cd87b005f79 100644 --- a/.github/workflows/javadoc-crawler.yml +++ b/.github/workflows/javadoc-crawler.yml @@ -20,7 +20,7 @@ jobs: java-version: 17 - name: Set up gradle - uses: gradle/actions/setup-gradle@06832c7b30a0129d7fb559bcc6e43d26f6374244 # v4.3.1 + uses: gradle/actions/setup-gradle@8379f6a1328ee0e06e2bb424dadb7b159856a326 # v4.4.0 - name: Run crawler run: ./gradlew :javadoc-crawler:crawl diff --git a/.github/workflows/ossf-scorecard.yml b/.github/workflows/ossf-scorecard.yml index c969bba721a..352cf133f0d 100644 --- a/.github/workflows/ossf-scorecard.yml +++ b/.github/workflows/ossf-scorecard.yml @@ -23,7 +23,7 @@ jobs: with: persist-credentials: false - - uses: ossf/scorecard-action@f49aabe0b5af0936a0987cfb85d86b75731b0186 # v2.4.1 + - uses: ossf/scorecard-action@05b42c624433fc40578a4040d5cf5e36ddca8cde # v2.4.2 with: results_file: results.sarif results_format: sarif @@ -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@60168efe1c415ce0f5521ea06d5c2062adbeed1b # v3.28.17 + uses: github/codeql-action/upload-sarif@fca7ace96b7d713c7035871441bd52efbe39e27e # v3.28.19 with: sarif_file: results.sarif diff --git a/.github/workflows/owasp-dependency-check-daily.yml b/.github/workflows/owasp-dependency-check-daily.yml index 50439106b44..8d795c6e7fb 100644 --- a/.github/workflows/owasp-dependency-check-daily.yml +++ b/.github/workflows/owasp-dependency-check-daily.yml @@ -22,7 +22,7 @@ jobs: java-version: 17 - name: Set up gradle - uses: gradle/actions/setup-gradle@06832c7b30a0129d7fb559bcc6e43d26f6374244 # v4.3.1 + uses: gradle/actions/setup-gradle@8379f6a1328ee0e06e2bb424dadb7b159856a326 # v4.4.0 - name: Check dependencies run: ./gradlew dependencyCheckAnalyze diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 50de2dc3194..ada71742b2b 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -28,7 +28,7 @@ jobs: java-version: 17 - name: Set up gradle - uses: gradle/actions/setup-gradle@06832c7b30a0129d7fb559bcc6e43d26f6374244 # v4.3.1 + uses: gradle/actions/setup-gradle@8379f6a1328ee0e06e2bb424dadb7b159856a326 # v4.4.0 - name: Build and publish artifacts run: ./gradlew assemble publishToSonatype closeAndReleaseSonatypeStagingRepository diff --git a/integration-tests/tracecontext/docker/Dockerfile b/integration-tests/tracecontext/docker/Dockerfile index 96cdb43fac6..39d52f3b977 100644 --- a/integration-tests/tracecontext/docker/Dockerfile +++ b/integration-tests/tracecontext/docker/Dockerfile @@ -1,4 +1,4 @@ -FROM python:3.13.3@sha256:653b0cf8fc50366277a21657209ddd54edd125499d20f3520c6b277eb8c828d3 AS build +FROM python:3.13.4@sha256:eb120d016adcbc8bac194e15826bbb4f1d1569d298d8817bb5049ed5e59f41d9 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.3-slim@sha256:914bf5c12ea40a97a78b2bff97fbdb766cc36ec903bfb4358faf2b74d73b555b +FROM python:3.13.4-slim@sha256:d97b595c5f4ac718102e5a5a91adaf04b22e852961a698411637c718d45867c8 RUN pip install aiohttp From c874197d64b0de371db462f53f12691e4fa0c35c Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 5 Jun 2025 16:57:54 -0500 Subject: [PATCH 35/42] chore(config): migrate renovate config (#7351) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Trask Stalnaker --- .github/renovate.json5 | 83 ++++++++++++++++++++++++++---------------- 1 file changed, 51 insertions(+), 32 deletions(-) diff --git a/.github/renovate.json5 b/.github/renovate.json5 index cc9dbd40a2b..70e3507d22b 100644 --- a/.github/renovate.json5 +++ b/.github/renovate.json5 @@ -1,59 +1,78 @@ { - "$schema": "https://docs.renovatebot.com/renovate-schema.json", - "extends": [ - "config:best-practices", - "helpers:pinGitHubActionDigestsToSemver" + $schema: 'https://docs.renovatebot.com/renovate-schema.json', + extends: [ + 'config:best-practices', + 'helpers:pinGitHubActionDigestsToSemver', ], - "packageRules": [ + packageRules: [ { // this is to reduce the number of renovate PRs - "matchManagers": [ - "github-actions", - "dockerfile" + matchManagers: [ + 'github-actions', + 'dockerfile', ], - "extends": ["schedule:weekly"], - "groupName": "weekly update" + extends: [ + 'schedule:weekly', + ], + groupName: 'weekly update', }, { - "matchPackageNames": [ - "io.opentelemetry.contrib:opentelemetry-aws-xray-propagator", - "io.opentelemetry.proto:opentelemetry-proto", - "io.opentelemetry.semconv:opentelemetry-semconv-incubating" + matchPackageNames: [ + 'io.opentelemetry.contrib:opentelemetry-aws-xray-propagator', + 'io.opentelemetry.proto:opentelemetry-proto', + 'io.opentelemetry.semconv:opentelemetry-semconv-incubating', ], // Renovate's default behavior is only to update from unstable -> unstable if it's for the // major.minor.patch, under the assumption that you would want to update to the stable version // of that release instead of the unstable version for a future release // (TODO remove once the artifacts above release stable versions) - "ignoreUnstable": false, - "allowedVersions": "!/\\-SNAPSHOT$/" + ignoreUnstable: false, + allowedVersions: '!/\\-SNAPSHOT$/', }, { // junit-pioneer 2+ requires Java 11+ - "matchPackageNames": ["org.junit-pioneer:junit-pioneer"], - "matchUpdateTypes": ["major"], - "enabled": false + matchPackageNames: [ + 'org.junit-pioneer:junit-pioneer', + ], + matchUpdateTypes: [ + 'major', + ], + enabled: false, }, { // mockito 5+ requires Java 11+ - "matchPackagePrefixes": ["org.mockito:"], - "matchUpdateTypes": ["major"], - "enabled": false + matchUpdateTypes: [ + 'major', + ], + enabled: false, + matchPackageNames: [ + 'org.mockito:{/,}**', + ], }, { // jqf-fuzz version 1.8+ requires Java 11+ - "matchPackageNames": ["edu.berkeley.cs.jqf:jqf-fuzz"], - "matchUpdateTypes": ["major", "minor"], - "enabled": false + matchPackageNames: [ + 'edu.berkeley.cs.jqf:jqf-fuzz', + ], + matchUpdateTypes: [ + 'major', + 'minor', + ], + enabled: false, }, { // pinned version for compatibility - "matchPackageNames": ["org.jetbrains.kotlinx:kotlinx-coroutines-core"], - "matchCurrentVersion": "1.5.2", - "enabled": false + matchPackageNames: [ + 'org.jetbrains.kotlinx:kotlinx-coroutines-core', + ], + matchCurrentVersion: '1.5.2', + enabled: false, }, { - "matchPackagePrefixes": ["com.diffplug.spotless"], - "groupName": "spotless packages" - } - ] + groupName: 'spotless packages', + matchPackageNames: [ + 'com.diffplug.spotless{/,}**', + ], + }, + ], } From 5fada737bf96a02c2a6d2754b6b6cf94007b0013 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 5 Jun 2025 17:05:28 -0500 Subject: [PATCH 36/42] fix(deps): update dependency com.squareup.wire:wire-bom to v5.3.3 (#7395) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- buildSrc/build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts index a342477939f..dbf637985ed 100644 --- a/buildSrc/build.gradle.kts +++ b/buildSrc/build.gradle.kts @@ -50,7 +50,7 @@ repositories { } dependencies { - implementation(enforcedPlatform("com.squareup.wire:wire-bom:5.3.2")) + implementation(enforcedPlatform("com.squareup.wire:wire-bom:5.3.3")) 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") From cb92d970d4dde8db8ce49c0291c839134b7f71d3 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 5 Jun 2025 17:08:36 -0500 Subject: [PATCH 37/42] chore(deps): update dependency gradle to v8.14.2 (#7394) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/wrapper/gradle-wrapper.properties | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 9128c7d428d..3735f265b95 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=845952a9d6afa783db70bb3b0effaae45ae5542ca2bb7929619e8af49cb634cf -distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.1-bin.zip +distributionSha256Sum=7197a12f450794931532469d4ff21a59ea2c1cd59a3ec3f89c035c3c420a6999 +distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.2-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME From 5e50aa7ee67d7a20b0b448df336a7dbb5625a22a Mon Sep 17 00:00:00 2001 From: Gregor Zeitlinger Date: Fri, 6 Jun 2025 00:12:45 +0200 Subject: [PATCH 38/42] prom exporter: exclude shaded protobuf (#7355) Co-authored-by: Jack Berg --- dependencyManagement/build.gradle.kts | 7 +------ exporters/prometheus/build.gradle.kts | 8 +++++--- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/dependencyManagement/build.gradle.kts b/dependencyManagement/build.gradle.kts index c7d1999a323..e8a54c60a28 100644 --- a/dependencyManagement/build.gradle.kts +++ b/dependencyManagement/build.gradle.kts @@ -34,7 +34,6 @@ val jmhVersion = "1.37" val mockitoVersion = "4.11.0" val slf4jVersion = "2.0.17" val opencensusVersion = "0.31.1" -val prometheusClientVersion = "0.16.0" val prometheusServerVersion = "1.3.7" val armeriaVersion = "1.32.5" val junitVersion = "5.12.2" @@ -65,12 +64,8 @@ val DEPENDENCIES = listOf( "org.mockito:mockito-junit-jupiter:${mockitoVersion}", "org.slf4j:slf4j-simple:${slf4jVersion}", "org.slf4j:jul-to-slf4j:${slf4jVersion}", - "io.prometheus:prometheus-metrics-shaded-protobuf:1.3.1", "io.prometheus:prometheus-metrics-exporter-httpserver:${prometheusServerVersion}", - "io.prometheus:prometheus-metrics-exposition-formats:${prometheusServerVersion}", - "io.prometheus:simpleclient:${prometheusClientVersion}", - "io.prometheus:simpleclient_common:${prometheusClientVersion}", - "io.prometheus:simpleclient_httpserver:${prometheusClientVersion}", + "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.0", diff --git a/exporters/prometheus/build.gradle.kts b/exporters/prometheus/build.gradle.kts index 30e52664419..16740f353dc 100644 --- a/exporters/prometheus/build.gradle.kts +++ b/exporters/prometheus/build.gradle.kts @@ -12,7 +12,10 @@ dependencies { compileOnly(project(":api:incubator")) implementation(project(":exporters:common")) implementation(project(":sdk-extensions:autoconfigure-spi")) - implementation("io.prometheus:prometheus-metrics-exporter-httpserver") + implementation("io.prometheus:prometheus-metrics-exporter-httpserver") { + exclude(group = "io.prometheus", module = "prometheus-metrics-exposition-formats") + } + implementation("io.prometheus:prometheus-metrics-exposition-textformats") compileOnly("com.google.auto.value:auto-value-annotations") @@ -20,8 +23,7 @@ dependencies { testImplementation(project(":sdk:testing")) testImplementation("io.opentelemetry.proto:opentelemetry-proto") - testImplementation("io.prometheus:prometheus-metrics-shaded-protobuf") - testImplementation("io.prometheus:prometheus-metrics-exposition-formats") + testImplementation("io.prometheus:prometheus-metrics-exposition-formats-no-protobuf") testImplementation("com.sun.net.httpserver:http") testImplementation("com.google.guava:guava") testImplementation("com.linecorp.armeria:armeria") From 8ed10f2ad7fb81ca5c49fb5c1d98800af0da91e3 Mon Sep 17 00:00:00 2001 From: jack-berg <34418638+jack-berg@users.noreply.github.com> Date: Fri, 6 Jun 2025 09:23:58 -0500 Subject: [PATCH 39/42] OTLP exporter should tolerate instances of LogRecordData when incubator is present (#7393) --- .../exporter/internal/otlp/IncubatingUtil.java | 17 +++++++++++++++++ .../internal/otlp/logs/LogMarshaler.java | 17 ++--------------- .../otlp/logs/LogStatelessMarshaler.java | 16 ++-------------- .../LogsRequestMarshalerIncubatingTest.java | 16 ++++++++++++++++ 4 files changed, 37 insertions(+), 29 deletions(-) diff --git a/exporters/otlp/common/src/main/java/io/opentelemetry/exporter/internal/otlp/IncubatingUtil.java b/exporters/otlp/common/src/main/java/io/opentelemetry/exporter/internal/otlp/IncubatingUtil.java index fb1e350188a..90660fd5a85 100644 --- a/exporters/otlp/common/src/main/java/io/opentelemetry/exporter/internal/otlp/IncubatingUtil.java +++ b/exporters/otlp/common/src/main/java/io/opentelemetry/exporter/internal/otlp/IncubatingUtil.java @@ -28,11 +28,28 @@ */ public class IncubatingUtil { + private static final boolean INCUBATOR_AVAILABLE; + + static { + boolean incubatorAvailable = false; + try { + Class.forName("io.opentelemetry.api.incubator.common.ExtendedAttributes"); + incubatorAvailable = true; + } catch (ClassNotFoundException e) { + // Not available + } + INCUBATOR_AVAILABLE = incubatorAvailable; + } + private static final byte[] EMPTY_BYTES = new byte[0]; private static final KeyValueMarshaler[] EMPTY_REPEATED = new KeyValueMarshaler[0]; private IncubatingUtil() {} + public static boolean isExtendedLogRecordData(LogRecordData logRecordData) { + return INCUBATOR_AVAILABLE && logRecordData instanceof ExtendedLogRecordData; + } + @SuppressWarnings("AvoidObjectArrays") public static KeyValueMarshaler[] createdExtendedAttributesMarhsalers( LogRecordData logRecordData) { diff --git a/exporters/otlp/common/src/main/java/io/opentelemetry/exporter/internal/otlp/logs/LogMarshaler.java b/exporters/otlp/common/src/main/java/io/opentelemetry/exporter/internal/otlp/logs/LogMarshaler.java index 713fba7ded0..a53a523c642 100644 --- a/exporters/otlp/common/src/main/java/io/opentelemetry/exporter/internal/otlp/logs/LogMarshaler.java +++ b/exporters/otlp/common/src/main/java/io/opentelemetry/exporter/internal/otlp/logs/LogMarshaler.java @@ -24,19 +24,6 @@ import javax.annotation.Nullable; final class LogMarshaler extends MarshalerWithSize { - private static final boolean INCUBATOR_AVAILABLE; - - static { - boolean incubatorAvailable = false; - try { - Class.forName("io.opentelemetry.api.incubator.common.ExtendedAttributes"); - incubatorAvailable = true; - } catch (ClassNotFoundException e) { - // Not available - } - INCUBATOR_AVAILABLE = incubatorAvailable; - } - private static final String INVALID_TRACE_ID = TraceId.getInvalid(); private static final String INVALID_SPAN_ID = SpanId.getInvalid(); @@ -54,12 +41,12 @@ final class LogMarshaler extends MarshalerWithSize { static LogMarshaler create(LogRecordData logRecordData) { KeyValueMarshaler[] attributeMarshalers = - INCUBATOR_AVAILABLE + IncubatingUtil.isExtendedLogRecordData(logRecordData) ? IncubatingUtil.createdExtendedAttributesMarhsalers(logRecordData) : KeyValueMarshaler.createForAttributes(logRecordData.getAttributes()); int attributeSize = - INCUBATOR_AVAILABLE + IncubatingUtil.isExtendedLogRecordData(logRecordData) ? IncubatingUtil.extendedAttributesSize(logRecordData) : logRecordData.getAttributes().size(); diff --git a/exporters/otlp/common/src/main/java/io/opentelemetry/exporter/internal/otlp/logs/LogStatelessMarshaler.java b/exporters/otlp/common/src/main/java/io/opentelemetry/exporter/internal/otlp/logs/LogStatelessMarshaler.java index e8b533e170a..505c42cbc47 100644 --- a/exporters/otlp/common/src/main/java/io/opentelemetry/exporter/internal/otlp/logs/LogStatelessMarshaler.java +++ b/exporters/otlp/common/src/main/java/io/opentelemetry/exporter/internal/otlp/logs/LogStatelessMarshaler.java @@ -27,18 +27,6 @@ final class LogStatelessMarshaler implements StatelessMarshaler { private static final String INVALID_TRACE_ID = TraceId.getInvalid(); private static final String INVALID_SPAN_ID = SpanId.getInvalid(); - private static final boolean INCUBATOR_AVAILABLE; - - static { - boolean incubatorAvailable = false; - try { - Class.forName("io.opentelemetry.api.incubator.common.ExtendedAttributes"); - incubatorAvailable = true; - } catch (ClassNotFoundException e) { - // Not available - } - INCUBATOR_AVAILABLE = incubatorAvailable; - } static final LogStatelessMarshaler INSTANCE = new LogStatelessMarshaler(); @@ -56,7 +44,7 @@ public void writeTo(Serializer output, LogRecordData log, MarshalerContext conte } int droppedAttributesCount; - if (INCUBATOR_AVAILABLE) { + if (IncubatingUtil.isExtendedLogRecordData(log)) { IncubatingUtil.serializeExtendedAttributes(output, log, context); droppedAttributesCount = log.getTotalAttributeCount() - IncubatingUtil.extendedAttributesSize(log); @@ -99,7 +87,7 @@ public int getBinarySerializedSize(LogRecordData log, MarshalerContext context) StatelessMarshalerUtil.sizeMessageWithContext( LogRecord.BODY, log.getBodyValue(), AnyValueStatelessMarshaler.INSTANCE, context); } - if (INCUBATOR_AVAILABLE) { + if (IncubatingUtil.isExtendedLogRecordData(log)) { size += IncubatingUtil.sizeExtendedAttributes(log, context); int droppedAttributesCount = diff --git a/exporters/otlp/common/src/testIncubating/java/io/opentelemetry/exporter/internal/otlp/logs/LogsRequestMarshalerIncubatingTest.java b/exporters/otlp/common/src/testIncubating/java/io/opentelemetry/exporter/internal/otlp/logs/LogsRequestMarshalerIncubatingTest.java index 8b4533e24ae..60d4c4fe799 100644 --- a/exporters/otlp/common/src/testIncubating/java/io/opentelemetry/exporter/internal/otlp/logs/LogsRequestMarshalerIncubatingTest.java +++ b/exporters/otlp/common/src/testIncubating/java/io/opentelemetry/exporter/internal/otlp/logs/LogsRequestMarshalerIncubatingTest.java @@ -6,6 +6,7 @@ package io.opentelemetry.exporter.internal.otlp.logs; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatCode; import com.google.protobuf.ByteString; import com.google.protobuf.InvalidProtocolBufferException; @@ -34,6 +35,7 @@ import io.opentelemetry.sdk.common.InstrumentationScopeInfo; import io.opentelemetry.sdk.logs.data.LogRecordData; import io.opentelemetry.sdk.resources.Resource; +import io.opentelemetry.sdk.testing.logs.TestLogRecordData; import io.opentelemetry.sdk.testing.logs.internal.TestExtendedLogRecordData; import java.io.ByteArrayOutputStream; import java.io.IOException; @@ -56,6 +58,20 @@ class LogsRequestMarshalerIncubatingTest { private static final String EVENT_NAME = "hello"; private static final String BODY = "Hello world from this log..."; + // Marshalers should not throw if the incubator is on the class path, but are called with + // LogRecordData instances which are not ExtendedLogRecordData. + // See: https://github.com/open-telemetry/opentelemetry-java/issues/7363 + @ParameterizedTest + @EnumSource(MarshalerSource.class) + void toProtoLogRecord_NotExtendedLogRecordData_DoesNotThrow(MarshalerSource marshalerSource) { + assertThatCode( + () -> + parse( + LogRecord.getDefaultInstance(), + marshalerSource.create(TestLogRecordData.builder().build()))) + .doesNotThrowAnyException(); + } + @ParameterizedTest @EnumSource(MarshalerSource.class) void toProtoLogRecord(MarshalerSource marshalerSource) { From f2f29f5976983de3d260acbddcfb419ff47b0d70 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 6 Jun 2025 09:29:48 -0500 Subject: [PATCH 40/42] fix(deps): update prometheusserverversion to v1.3.8 (#7367) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Jack Berg --- dependencyManagement/build.gradle.kts | 2 +- .../exporter/prometheus/PrometheusHttpServerTest.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/dependencyManagement/build.gradle.kts b/dependencyManagement/build.gradle.kts index e8a54c60a28..70711328566 100644 --- a/dependencyManagement/build.gradle.kts +++ b/dependencyManagement/build.gradle.kts @@ -34,7 +34,7 @@ val jmhVersion = "1.37" val mockitoVersion = "4.11.0" val slf4jVersion = "2.0.17" val opencensusVersion = "0.31.1" -val prometheusServerVersion = "1.3.7" +val prometheusServerVersion = "1.3.8" val armeriaVersion = "1.32.5" val junitVersion = "5.12.2" 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 4faca5428cd..81b85fb9487 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 @@ -46,7 +46,7 @@ import io.opentelemetry.sdk.resources.Resource; import io.prometheus.metrics.exporter.httpserver.HTTPServer; import io.prometheus.metrics.exporter.httpserver.MetricsHandler; -import io.prometheus.metrics.expositionformats.generated.com_google_protobuf_4_30_2.Metrics; +import io.prometheus.metrics.expositionformats.generated.com_google_protobuf_4_31_0.Metrics; import io.prometheus.metrics.model.registry.PrometheusRegistry; import java.io.ByteArrayInputStream; import java.io.IOException; From d034464ffd2f9ecca85292ad3459f156968f6441 Mon Sep 17 00:00:00 2001 From: jack-berg <34418638+jack-berg@users.noreply.github.com> Date: Fri, 6 Jun 2025 11:46:10 -0500 Subject: [PATCH 41/42] Prepare for 1.51.0 release (#7396) --- CHANGELOG.md | 32 +++++++++++++++++++ .../OtlpHttpLogRecordExporterBuilder.java | 2 ++ .../OtlpHttpMetricExporterBuilder.java | 2 ++ .../trace/OtlpHttpSpanExporterBuilder.java | 2 ++ .../OtlpGrpcLogRecordExporterBuilder.java | 2 ++ .../OtlpGrpcMetricExporterBuilder.java | 2 ++ .../trace/OtlpGrpcSpanExporterBuilder.java | 2 ++ .../zipkin/ZipkinSpanExporterBuilder.java | 2 ++ .../sdk/common/InternalTelemetryVersion.java | 6 +++- 9 files changed, 51 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c912fd9da9b..a342ab6bdce 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,38 @@ ## Unreleased +### API + +#### Context + +* Fix context storage provider property name in log message + ([#7342](https://github.com/open-telemetry/opentelemetry-java/pull/7342)) + +### SDK + +* Experimental configurable exception.* attribute resolution for SdkTracerProvider, + SdkLoggerProvider + ([#7266](https://github.com/open-telemetry/opentelemetry-java/pull/7266)) + +#### Exporters + +* All exporters: implement new SemConv exporter health metrics, with configuration API for selecting + schema version + ([#7265](https://github.com/open-telemetry/opentelemetry-java/pull/7265)) +* OTLP: Add gRPC export for profiles signal type. + ([#7301](https://github.com/open-telemetry/opentelemetry-java/pull/7301)) +* OTLP: Run JDK HTTP sender on non-daemon threads. + ([#7322](https://github.com/open-telemetry/opentelemetry-java/pull/7322)) +* Prometheus: fix serialization of arrays + ([#7291](https://github.com/open-telemetry/opentelemetry-java/pull/7291)) +* OTLP: exporter tolerates instances of LogRecordData when incubator is present + ([#7393](https://github.com/open-telemetry/opentelemetry-java/pull/7393)) + +#### Extensions + +* Declarative config: Handle instrumentation node changes in yaml config file format 0.4 + ([#7357](https://github.com/open-telemetry/opentelemetry-java/pull/7357)) + ## Version 1.50.0 (2025-05-09) ### API 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 c7426f1880b..7dfe6f74262 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 @@ -220,6 +220,8 @@ public OtlpHttpLogRecordExporterBuilder setMeterProvider( /** * Sets the {@link InternalTelemetryVersion} defining which self-monitoring metrics this exporter * collects. + * + * @since 1.51.0 */ public OtlpHttpLogRecordExporterBuilder setInternalTelemetryVersion( InternalTelemetryVersion schemaVersion) { 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 f553c6d6bcf..3d6e6b97e5b 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 @@ -249,6 +249,8 @@ public OtlpHttpMetricExporterBuilder setProxyOptions(ProxyOptions proxyOptions) /** * Sets the {@link InternalTelemetryVersion} defining which self-monitoring metrics this exporter * collects. + * + * @since 1.51.0 */ public OtlpHttpMetricExporterBuilder setInternalTelemetryVersion( InternalTelemetryVersion schemaVersion) { 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 8f8cd9ec6a7..4c2737845d1 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 @@ -221,6 +221,8 @@ public OtlpHttpSpanExporterBuilder setMeterProvider( /** * Sets the {@link InternalTelemetryVersion} defining which self-monitoring metrics this exporter * collects. + * + * @since 1.51.0 */ public OtlpHttpSpanExporterBuilder setInternalTelemetryVersion( InternalTelemetryVersion schemaVersion) { 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 88f07f98710..dbec072655e 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 @@ -249,6 +249,8 @@ public OtlpGrpcLogRecordExporterBuilder setMeterProvider( /** * Sets the {@link InternalTelemetryVersion} defining which self-monitoring metrics this exporter * collects. + * + * @since 1.51.0 */ public OtlpGrpcLogRecordExporterBuilder setInternalTelemetryVersion( InternalTelemetryVersion schemaVersion) { 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 ec969083515..8a56a8188a2 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 @@ -277,6 +277,8 @@ public OtlpGrpcMetricExporterBuilder setRetryPolicy(@Nullable RetryPolicy retryP /** * Sets the {@link InternalTelemetryVersion} defining which self-monitoring metrics this exporter * collects. + * + * @since 1.51.0 */ public OtlpGrpcMetricExporterBuilder setInternalTelemetryVersion( InternalTelemetryVersion schemaVersion) { 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 cbe12a97193..8b0c8c1f9f7 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 @@ -246,6 +246,8 @@ public OtlpGrpcSpanExporterBuilder setMeterProvider( /** * Sets the {@link InternalTelemetryVersion} defining which self-monitoring metrics this exporter * collects. + * + * @since 1.51.0 */ public OtlpGrpcSpanExporterBuilder setInternalTelemetryVersion( InternalTelemetryVersion schemaVersion) { diff --git a/exporters/zipkin/src/main/java/io/opentelemetry/exporter/zipkin/ZipkinSpanExporterBuilder.java b/exporters/zipkin/src/main/java/io/opentelemetry/exporter/zipkin/ZipkinSpanExporterBuilder.java index c0773de041a..ec25c9983a4 100644 --- a/exporters/zipkin/src/main/java/io/opentelemetry/exporter/zipkin/ZipkinSpanExporterBuilder.java +++ b/exporters/zipkin/src/main/java/io/opentelemetry/exporter/zipkin/ZipkinSpanExporterBuilder.java @@ -191,6 +191,8 @@ public ZipkinSpanExporterBuilder setMeterProvider(MeterProvider meterProvider) { /** * Sets the {@link InternalTelemetryVersion} defining which self-monitoring metrics this exporter * collects. + * + * @since 1.51.0 */ public ZipkinSpanExporterBuilder setInternalTelemetryVersion(InternalTelemetryVersion level) { requireNonNull(level, "level"); diff --git a/sdk/common/src/main/java/io/opentelemetry/sdk/common/InternalTelemetryVersion.java b/sdk/common/src/main/java/io/opentelemetry/sdk/common/InternalTelemetryVersion.java index f50016b8e3b..37aaf934b96 100644 --- a/sdk/common/src/main/java/io/opentelemetry/sdk/common/InternalTelemetryVersion.java +++ b/sdk/common/src/main/java/io/opentelemetry/sdk/common/InternalTelemetryVersion.java @@ -5,7 +5,11 @@ package io.opentelemetry.sdk.common; -/** Defines the self-monitoring telemetry SDK components should capture. */ +/** + * Defines the self-monitoring telemetry SDK components should capture. + * + * @since 1.51.0 + */ public enum InternalTelemetryVersion { /** * Record self-monitoring metrics defined in the SDK prior the standardization in semantic From c280308a056b0a434908e6c8b966f95ad1d58f64 Mon Sep 17 00:00:00 2001 From: "otelbot[bot]" <197425009+otelbot[bot]@users.noreply.github.com> Date: Fri, 6 Jun 2025 13:27:38 -0500 Subject: [PATCH 42/42] [release/v1.51.x] Prepare release 1.51.0 (#7400) Co-authored-by: otelbot <197425009+otelbot@users.noreply.github.com> --- CHANGELOG.md | 2 +- version.gradle.kts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a342ab6bdce..736c2681162 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -## Unreleased +## Version 1.51.0 (2025-06-06) ### API diff --git a/version.gradle.kts b/version.gradle.kts index 74272dc6b59..830cf16c085 100644 --- a/version.gradle.kts +++ b/version.gradle.kts @@ -1,4 +1,4 @@ -val snapshot = true +val snapshot = false allprojects { var ver = "1.51.0"