diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 8243beff8f7..210f70e2ff7 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -136,8 +136,9 @@ jobs: - uses: actions/checkout@v4 - uses: graalvm/setup-graalvm@v1 with: - version: 'latest' - java-version: '17' + # TODO(jack-berg): Which versions do we need to test? Should we use a matrix scheme? + java-version: '21' + distribution: 'graalvm' components: 'native-image' github-token: ${{ secrets.GITHUB_TOKEN }} - name: Running test @@ -145,7 +146,6 @@ jobs: echo "GRAALVM_HOME: $GRAALVM_HOME" echo "JAVA_HOME: $JAVA_HOME" java --version - gu --version native-image --version ./gradlew nativeTest diff --git a/CHANGELOG.md b/CHANGELOG.md index 09d69ff84ba..3fece48b3b4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,13 @@ ## Unreleased +## Version 1.34.1 (2024-01-11) + +* Fix prometheus exporter regressions + ([#6138](https://github.com/open-telemetry/opentelemetry-java/pull/6138)) +* Fix native image regression + ([#6134](https://github.com/open-telemetry/opentelemetry-java/pull/6134)) + ## Version 1.34.0 (2024-01-05) **NOTE:** This is the LAST release for `opentelemetry-exporter-jaeger` diff --git a/README.md b/README.md index c0425230e7c..f273a554485 100644 --- a/README.md +++ b/README.md @@ -265,8 +265,6 @@ dependency as follows, replacing `{{artifact-id}}` with the value from the "Arti | [OTLP Exporters](./exporters/otlp/all) | OTLP gRPC & HTTP exporters, including traces, metrics, and logs | `opentelemetry-exporter-otlp` | 1.34.0 | [![Javadocs](https://www.javadoc.io/badge/io.opentelemetry/opentelemetry-exporter-otlp.svg)](https://www.javadoc.io/doc/io.opentelemetry/opentelemetry-exporter-otlp) | | [OTLP Logging Exporters](./exporters/logging-otlp) | Logging exporters in OTLP JSON encoding, including traces, metrics, and logs | `opentelemetry-exporter-logging-otlp` | 1.34.0 | [![Javadocs](https://www.javadoc.io/badge/io.opentelemetry/opentelemetry-exporter-logging-otlp.svg)](https://www.javadoc.io/doc/io.opentelemetry/opentelemetry-exporter-logging-otlp) | | [OTLP Common](./exporters/otlp/common) | Shared OTLP components (internal) | `opentelemetry-exporter-otlp-common` | 1.34.0 | [![Javadocs](https://www.javadoc.io/badge/io.opentelemetry/opentelemetry-exporter-otlp-common.svg)](https://www.javadoc.io/doc/io.opentelemetry/opentelemetry-exporter-otlp-common) | -| [Jaeger gRPC Exporter](./exporters/jaeger) | Jaeger gRPC trace exporter (deprecated [1]) | `opentelemetry-exporter-jaeger` | 1.34.0 | [![Javadocs](https://www.javadoc.io/badge/io.opentelemetry/opentelemetry-exporter-jaeger.svg)](https://www.javadoc.io/doc/io.opentelemetry/opentelemetry-exporter-jaeger) | -| [Jaeger Thrift Exporter](./exporters/jaeger-thrift) | Jaeger thrift trace exporter (deprecated [1]) | `opentelemetry-exporter-jaeger-thift` | 1.34.0 | [![Javadocs](https://www.javadoc.io/badge/io.opentelemetry/opentelemetry-exporter-jaeger-thrift.svg)](https://www.javadoc.io/doc/io.opentelemetry/opentelemetry-exporter-jaeger-thrift) | | [Logging Exporter](./exporters/logging) | Logging exporters, including metrics, traces, and logs | `opentelemetry-exporter-logging` | 1.34.0 | [![Javadocs](https://www.javadoc.io/badge/io.opentelemetry/opentelemetry-exporter-logging.svg)](https://www.javadoc.io/doc/io.opentelemetry/opentelemetry-exporter-logging) | | [Zipkin Exporter](./exporters/zipkin) | Zipkin trace exporter | `opentelemetry-exporter-zipkin` | 1.34.0 | [![Javadocs](https://www.javadoc.io/badge/io.opentelemetry/opentelemetry-exporter-zipkin.svg)](https://www.javadoc.io/doc/io.opentelemetry/opentelemetry-exporter-zipkin) | | [Prometheus Exporter](./exporters/prometheus) | Prometheus metric exporter | `opentelemetry-exporter-prometheus` | 1.34.0-alpha | [![Javadocs](https://www.javadoc.io/badge/io.opentelemetry/opentelemetry-exporter-prometheus.svg)](https://www.javadoc.io/doc/io.opentelemetry/opentelemetry-exporter-prometheus) | @@ -275,13 +273,6 @@ dependency as follows, replacing `{{artifact-id}}` with the value from the "Arti | [JDK Sender](./exporters/sender/okhttp) | Java 11+ native HttpClient implementation of HttpSender (internal) | `opentelemetry-exporter-sender-jdk` | 1.34.0-alpha | [![Javadocs](https://www.javadoc.io/badge/io.opentelemetry/opentelemetry-exporter-sender-jdk.svg)](https://www.javadoc.io/doc/io.opentelemetry/opentelemetry-exporter-sender-jdk) | | | [gRPC ManagedChannel Sender](./exporters/sender/grpc-managed-channel) | gRPC ManagedChannel implementation of GrpcSender (internal) | `opentelemetry-exporter-sender-grpc-managed-channel` | 1.34.0 | [![Javadocs](https://www.javadoc.io/badge/io.opentelemetry/opentelemetry-exporter-sender-grpc-managed-channel.svg)](https://www.javadoc.io/doc/io.opentelemetry/opentelemetry-exporter-sender-grpc-managed-channel) | | -**[1]**: Jaeger now -has [native support for OTLP](https://opentelemetry.io/blog/2022/jaeger-native-otlp/) and jaeger -exporters are now deprecated. `opentelemetry-exporter-jaeger-thrift` will continue to be published -until 1.34.0 (January 2024) but no new PRs will be accepted except security related bugfixes. After -1.34.0, `io.opentelemetry:opentelemetry-bom` will reference the last published version, but no -additional versions will be published. - ### SDK Extensions | Component | Description | Artifact ID | Version | Javadoc | diff --git a/all/build.gradle.kts b/all/build.gradle.kts index aa302ed598b..874c93ae1c6 100644 --- a/all/build.gradle.kts +++ b/all/build.gradle.kts @@ -98,9 +98,6 @@ tasks.named("jacocoTestReport") { // Exclude mrjar (jacoco complains), shaded, and generated code !it.absolutePath.contains("META-INF/versions/") && !it.absolutePath.contains("/internal/shaded/") && - !it.absolutePath.contains("io/opentelemetry/proto/") && - !it.absolutePath.contains("io/opentelemetry/exporter/jaeger/proto/") && - !it.absolutePath.contains("io/opentelemetry/exporter/jaeger/internal/protobuf/") && !it.absolutePath.contains("io/opentelemetry/sdk/extension/trace/jaeger/proto/") && !it.absolutePath.contains("AutoValue_") }, diff --git a/api/all/src/main/resources/META-INF/native-image/io.opentelemetry/opentelemetry-api/reflect-config.json b/api/all/src/main/resources/META-INF/native-image/io.opentelemetry/opentelemetry-api/resource-config.json similarity index 100% rename from api/all/src/main/resources/META-INF/native-image/io.opentelemetry/opentelemetry-api/reflect-config.json rename to api/all/src/main/resources/META-INF/native-image/io.opentelemetry/opentelemetry-api/resource-config.json diff --git a/bom/build.gradle.kts b/bom/build.gradle.kts index ae50cabc349..3b977ff1263 100644 --- a/bom/build.gradle.kts +++ b/bom/build.gradle.kts @@ -14,3 +14,5 @@ otelBom.addFallback("opentelemetry-extension-annotations", "1.18.0") otelBom.addFallback("opentelemetry-sdk-extension-resources", "1.19.0") otelBom.addFallback("opentelemetry-sdk-extension-aws", "1.19.0") otelBom.addFallback("opentelemetry-extension-aws", "1.20.1") +// NOTE: opentelemetry-exporter-jaeger and opentelemetry-exporter-jaeger-thift are omitted because +// they contain dependencies on internal classes, which may have breaking API changes preventing compilation. diff --git a/dependencyManagement/build.gradle.kts b/dependencyManagement/build.gradle.kts index 05321ddaf8c..3baf6562328 100644 --- a/dependencyManagement/build.gradle.kts +++ b/dependencyManagement/build.gradle.kts @@ -17,7 +17,7 @@ val DEPENDENCY_BOMS = listOf( "io.grpc:grpc-bom:1.60.1", "io.netty:netty-bom:4.1.104.Final", "io.zipkin.brave:brave-bom:6.0.0", - "io.zipkin.reporter2:zipkin-reporter-bom:2.17.1", + "io.zipkin.reporter2:zipkin-reporter-bom:3.1.1", "org.assertj:assertj-bom:3.25.1", "org.junit:junit-bom:5.10.1", "org.testcontainers:testcontainers-bom:1.19.3", diff --git a/docs/apidiffs/current_vs_latest/opentelemetry-exporter-zipkin.txt b/docs/apidiffs/current_vs_latest/opentelemetry-exporter-zipkin.txt index df26146497b..73d401a04b9 100644 --- a/docs/apidiffs/current_vs_latest/opentelemetry-exporter-zipkin.txt +++ b/docs/apidiffs/current_vs_latest/opentelemetry-exporter-zipkin.txt @@ -1,2 +1,6 @@ Comparing source compatibility of against -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 + === UNCHANGED METHOD: PUBLIC io.opentelemetry.exporter.zipkin.ZipkinSpanExporterBuilder setEncoder(zipkin2.codec.BytesEncoder) + +++ NEW ANNOTATION: java.lang.Deprecated + +++ NEW METHOD: PUBLIC(+) io.opentelemetry.exporter.zipkin.ZipkinSpanExporterBuilder setEncoder(zipkin2.reporter.BytesEncoder) diff --git a/exporters/common/src/main/java/io/opentelemetry/exporter/internal/marshal/MarshalerUtil.java b/exporters/common/src/main/java/io/opentelemetry/exporter/internal/marshal/MarshalerUtil.java index e0f4c6f25f7..39f0584f4e4 100644 --- a/exporters/common/src/main/java/io/opentelemetry/exporter/internal/marshal/MarshalerUtil.java +++ b/exporters/common/src/main/java/io/opentelemetry/exporter/internal/marshal/MarshalerUtil.java @@ -8,6 +8,7 @@ import io.opentelemetry.api.trace.SpanId; import io.opentelemetry.api.trace.TraceId; import io.opentelemetry.sdk.common.InstrumentationScopeInfo; +import io.opentelemetry.sdk.internal.DynamicPrimitiveLongList; import io.opentelemetry.sdk.resources.Resource; import java.io.ByteArrayOutputStream; import java.io.IOException; @@ -128,6 +129,31 @@ public static int sizeRepeatedUInt64(ProtoFieldInfo field, long[] values) { return field.getTagSize() + CodedOutputStream.computeUInt32SizeNoTag(payloadSize) + payloadSize; } + /** + * Returns the size of a repeated uint64 field. + * + *

Packed repeated fields contain the tag, an integer representing the incoming payload size, + * and an actual payload of repeated varints. + * + *

NOTE: This method has the same logic as {@link #sizeRepeatedUInt64(ProtoFieldInfo, long[])} + * )} but instead of using a primitive array it uses {@link DynamicPrimitiveLongList} to avoid + * boxing/unboxing + */ + public static int sizeRepeatedUInt64(ProtoFieldInfo field, DynamicPrimitiveLongList values) { + if (values.isEmpty()) { + return 0; + } + + int payloadSize = 0; + for (int i = 0; i < values.size(); i++) { + long v = values.getLong(i); + payloadSize += CodedOutputStream.computeUInt64SizeNoTag(v); + } + + // tag size + payload indicator size + actual payload size + return field.getTagSize() + CodedOutputStream.computeUInt32SizeNoTag(payloadSize) + payloadSize; + } + /** Returns the size of a repeated double field. */ public static int sizeRepeatedDouble(ProtoFieldInfo field, List values) { // Same as fixed64. diff --git a/exporters/common/src/main/java/io/opentelemetry/exporter/internal/marshal/Serializer.java b/exporters/common/src/main/java/io/opentelemetry/exporter/internal/marshal/Serializer.java index 622201579a9..7382c2cfac6 100644 --- a/exporters/common/src/main/java/io/opentelemetry/exporter/internal/marshal/Serializer.java +++ b/exporters/common/src/main/java/io/opentelemetry/exporter/internal/marshal/Serializer.java @@ -5,6 +5,7 @@ package io.opentelemetry.exporter.internal.marshal; +import io.opentelemetry.sdk.internal.DynamicPrimitiveLongList; import java.io.IOException; import java.util.List; import javax.annotation.Nullable; @@ -191,6 +192,7 @@ public void serializeMessage(ProtoFieldInfo field, Marshaler message) throws IOE writeEndMessage(); } + @SuppressWarnings("SameParameterValue") protected abstract void writeStartRepeatedPrimitive( ProtoFieldInfo field, int protoSizePerElement, int numElements) throws IOException; @@ -243,6 +245,32 @@ public void serializeRepeatedUInt64(ProtoFieldInfo field, long[] values) throws writeEndRepeatedVarint(); } + /** + * Serializes a {@code repeated uint64} field. + * + *

NOTE: This is the same as {@link #serializeRepeatedUInt64(ProtoFieldInfo, long[])} but + * instead of taking a primitive array it takes a {@link DynamicPrimitiveLongList} as input. + */ + public void serializeRepeatedUInt64(ProtoFieldInfo field, DynamicPrimitiveLongList values) + throws IOException { + if (values.isEmpty()) { + return; + } + + int payloadSize = 0; + for (int i = 0; i < values.size(); i++) { + long v = values.getLong(i); + payloadSize += CodedOutputStream.computeUInt64SizeNoTag(v); + } + + writeStartRepeatedVarint(field, payloadSize); + for (int i = 0; i < values.size(); i++) { + long value = values.getLong(i); + writeUInt64Value(value); + } + writeEndRepeatedVarint(); + } + /** Serializes a {@code repeated double} field. */ public void serializeRepeatedDouble(ProtoFieldInfo field, List values) throws IOException { diff --git a/exporters/common/src/main/resources/META-INF/native-image/io.opentelemetry.opentelemetry-exporter-common/reflect-config.json b/exporters/common/src/main/resources/META-INF/native-image/io.opentelemetry.opentelemetry-exporter-common/reflect-config.json new file mode 100644 index 00000000000..033ccf44f0c --- /dev/null +++ b/exporters/common/src/main/resources/META-INF/native-image/io.opentelemetry.opentelemetry-exporter-common/reflect-config.json @@ -0,0 +1,10 @@ +[ + { + "name":"io.opentelemetry.sdk.common.export.AutoValue_RetryPolicy", + "queryAllDeclaredMethods":true + }, + { + "name":"io.opentelemetry.sdk.common.export.RetryPolicy", + "queryAllDeclaredMethods":true + } +] diff --git a/exporters/jaeger-proto/README.md b/exporters/jaeger-proto/README.md deleted file mode 100644 index f7e8a0497d8..00000000000 --- a/exporters/jaeger-proto/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# OpenTelemetry - Jaeger Proto (DEPRECATED) - -> **NOTICE**: External use of this artifact is deprecated. diff --git a/exporters/jaeger-proto/build.gradle.kts b/exporters/jaeger-proto/build.gradle.kts deleted file mode 100644 index 165686f909d..00000000000 --- a/exporters/jaeger-proto/build.gradle.kts +++ /dev/null @@ -1,25 +0,0 @@ -plugins { - id("otel.protobuf-conventions") - - id("otel.animalsniffer-conventions") -} - -description = "OpenTelemetry - Jaeger Exporter Proto (Internal Use Only)" -otelJava.moduleName.set("io.opentelemetry.exporter.jaeger.proto") - -dependencies { - api("com.google.protobuf:protobuf-java") - - compileOnly("io.grpc:grpc-api") - compileOnly("io.grpc:grpc-protobuf") - compileOnly("io.grpc:grpc-stub") -} - -tasks { - compileJava { - with(options) { - // Generated code so can't control serialization. - compilerArgs.add("-Xlint:-serial") - } - } -} diff --git a/exporters/jaeger-proto/src/main/proto/jaeger/api_v2/collector.proto b/exporters/jaeger-proto/src/main/proto/jaeger/api_v2/collector.proto deleted file mode 100644 index 03d6b6be8ca..00000000000 --- a/exporters/jaeger-proto/src/main/proto/jaeger/api_v2/collector.proto +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -syntax="proto3"; - -package jaeger.api_v2; - -import "jaeger/api_v2/model.proto"; - -option java_package = "io.opentelemetry.exporter.jaeger.proto.api_v2"; - -message PostSpansRequest { - Batch batch = 1; -} - -message PostSpansResponse { -} - -service CollectorService { - rpc PostSpans(PostSpansRequest) returns (PostSpansResponse) {} -} diff --git a/exporters/jaeger-proto/src/main/proto/jaeger/api_v2/model.proto b/exporters/jaeger-proto/src/main/proto/jaeger/api_v2/model.proto deleted file mode 100644 index 0d2bd212787..00000000000 --- a/exporters/jaeger-proto/src/main/proto/jaeger/api_v2/model.proto +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -syntax="proto3"; - -package jaeger.api_v2; - -import "google/protobuf/timestamp.proto"; -import "google/protobuf/duration.proto"; - -option java_package = "io.opentelemetry.exporter.jaeger.proto.api_v2"; - -enum ValueType { - STRING = 0; - BOOL = 1; - INT64 = 2; - FLOAT64 = 3; - BINARY = 4; -}; - -message Log { - google.protobuf.Timestamp timestamp = 1; - repeated KeyValue fields = 2; -} - -message KeyValue { - string key = 1; - ValueType v_type = 2; - string v_str = 3; - bool v_bool = 4; - int64 v_int64 = 5; - double v_float64 = 6; - bytes v_binary = 7; -} - -enum SpanRefType { - CHILD_OF = 0; - FOLLOWS_FROM = 1; -}; - -message SpanRef { - bytes trace_id = 1; - bytes span_id = 2; - SpanRefType ref_type = 3; -} - -message Process { - string service_name = 1; - repeated KeyValue tags = 2; -} - -message Span { - bytes trace_id = 1; - bytes span_id = 2; - string operation_name = 3; - repeated SpanRef references = 4; - uint32 flags = 5; - google.protobuf.Timestamp start_time = 6; - google.protobuf.Duration duration = 7; - repeated KeyValue tags = 8; - repeated Log logs = 9; - Process process = 10; - string process_id = 11; - repeated string warnings = 12; -} - -message Trace { - message ProcessMapping { - string process_id = 1; - Process process = 2; - } - repeated Span spans = 1; - repeated ProcessMapping process_map = 2; - repeated string warnings = 3; -} - -message Batch { - repeated Span spans = 1; - Process process = 2; -} - -message DependencyLink { - string parent = 1; - string child = 2; - uint64 call_count = 3; - string source = 4; -} diff --git a/exporters/jaeger-proto/src/main/proto/jaeger/api_v2/time.proto b/exporters/jaeger-proto/src/main/proto/jaeger/api_v2/time.proto deleted file mode 100644 index b92529d7a56..00000000000 --- a/exporters/jaeger-proto/src/main/proto/jaeger/api_v2/time.proto +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -// Includes work from: - -// Protocol Buffers - Google's data interchange format -// Copyright 2008 Google Inc. All rights reserved. -// https://developers.google.com/protocol-buffers/ -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following disclaimer -// in the documentation and/or other materials provided with the -// distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -syntax = "proto3"; - -package io.opentelemetry.internal; - -option java_package = "io.opentelemetry.exporter.jaeger.internal.protobuf"; -option java_outer_classname = "TimeProto"; -option java_multiple_files = true; - -// Copied from google.protobuf.Timestamp to provide access to the wire format. -message Time { - // Represents seconds of UTC time since Unix epoch - // 1970-01-01T00:00:00Z. Must be from 0001-01-01T00:00:00Z to - // 9999-12-31T23:59:59Z inclusive. - int64 seconds = 1; - - // Non-negative fractions of a second at nanosecond resolution. Negative - // second values with fractions must still have non-negative nanos values - // that count forward in time. Must be from 0 to 999,999,999 - // inclusive. - int32 nanos = 2; -} diff --git a/exporters/jaeger-thrift/build.gradle.kts b/exporters/jaeger-thrift/build.gradle.kts deleted file mode 100644 index 383b9d6943c..00000000000 --- a/exporters/jaeger-thrift/build.gradle.kts +++ /dev/null @@ -1,27 +0,0 @@ -plugins { - id("otel.java-conventions") - id("otel.publish-conventions") - - id("otel.animalsniffer-conventions") -} - -description = "OpenTelemetry - Jaeger Thrift Exporter" -otelJava.moduleName.set("io.opentelemetry.exporter.jaeger.thrift") - -dependencies { - api(project(":sdk:all")) - - implementation(project(":sdk:all")) - - implementation("com.fasterxml.jackson.jr:jackson-jr-objects") - implementation("io.jaegertracing:jaeger-client") { - exclude("com.google.code.gson", "gson") - } - - testImplementation("com.fasterxml.jackson.jr:jackson-jr-stree") - testImplementation("org.testcontainers:junit-jupiter") - testImplementation("com.squareup.okhttp3:okhttp") - testImplementation("com.google.guava:guava-testlib") - - testImplementation(project(":sdk:testing")) -} diff --git a/exporters/jaeger-thrift/src/main/java/io/opentelemetry/exporter/jaeger/thrift/Adapter.java b/exporters/jaeger-thrift/src/main/java/io/opentelemetry/exporter/jaeger/thrift/Adapter.java deleted file mode 100644 index cd3bf4ec26f..00000000000 --- a/exporters/jaeger-thrift/src/main/java/io/opentelemetry/exporter/jaeger/thrift/Adapter.java +++ /dev/null @@ -1,277 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.exporter.jaeger.thrift; - -import static io.opentelemetry.api.common.AttributeKey.booleanKey; - -import com.fasterxml.jackson.jr.ob.JSON; -import io.jaegertracing.thriftjava.Log; -import io.jaegertracing.thriftjava.Span; -import io.jaegertracing.thriftjava.SpanRef; -import io.jaegertracing.thriftjava.SpanRefType; -import io.jaegertracing.thriftjava.Tag; -import io.jaegertracing.thriftjava.TagType; -import io.opentelemetry.api.common.AttributeKey; -import io.opentelemetry.api.common.Attributes; -import io.opentelemetry.api.trace.SpanKind; -import io.opentelemetry.api.trace.StatusCode; -import io.opentelemetry.sdk.trace.data.EventData; -import io.opentelemetry.sdk.trace.data.LinkData; -import io.opentelemetry.sdk.trace.data.SpanData; -import java.io.IOException; -import java.io.UncheckedIOException; -import java.math.BigInteger; -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; -import java.util.Locale; -import java.util.concurrent.TimeUnit; -import java.util.stream.Collectors; -import javax.annotation.concurrent.ThreadSafe; - -/** Adapts OpenTelemetry objects to Jaeger objects. */ -@ThreadSafe -final class Adapter { - - static final AttributeKey KEY_ERROR = booleanKey("error"); - static final String KEY_LOG_EVENT = "event"; - static final String KEY_EVENT_DROPPED_ATTRIBUTES_COUNT = "otel.event.dropped_attributes_count"; - static final String KEY_DROPPED_ATTRIBUTES_COUNT = "otel.dropped_attributes_count"; - static final String KEY_DROPPED_EVENTS_COUNT = "otel.dropped_events_count"; - static final String KEY_SPAN_KIND = "span.kind"; - static final String KEY_SPAN_STATUS_MESSAGE = "otel.status_message"; - static final String KEY_SPAN_STATUS_CODE = "otel.status_code"; - static final String KEY_INSTRUMENTATION_SCOPE_NAME = "otel.scope.name"; - static final String KEY_INSTRUMENTATION_SCOPE_VERSION = "otel.scope.version"; - static final String KEY_INSTRUMENTATION_LIBRARY_NAME = "otel.library.name"; - static final String KEY_INSTRUMENTATION_LIBRARY_VERSION = "otel.library.version"; - - private Adapter() {} - - /** - * Converts a list of {@link SpanData} into a collection of Jaeger's {@link Span}. - * - * @param spans the list of spans to be converted - * @return the collection of Jaeger spans - * @see #toJaeger(SpanData) - */ - static List toJaeger(Collection spans) { - return spans.stream().map(Adapter::toJaeger).collect(Collectors.toList()); - } - - /** - * Converts a single {@link SpanData} into a Jaeger's {@link Span}. - * - * @param span the span to be converted - * @return the Jaeger span - */ - static Span toJaeger(SpanData span) { - Span target = new Span(); - - long traceIdHigh = traceIdAsLongHigh(span.getTraceId()); - long traceIdLow = traceIdAsLongLow(span.getTraceId()); - - target.setTraceIdHigh(traceIdHigh); - target.setTraceIdLow(traceIdLow); - target.setSpanId(spanIdAsLong(span.getSpanId())); - target.setOperationName(span.getName()); - target.setStartTime(TimeUnit.NANOSECONDS.toMicros(span.getStartEpochNanos())); - target.setDuration( - TimeUnit.NANOSECONDS.toMicros(span.getEndEpochNanos() - span.getStartEpochNanos())); - - List tags = toTags(span.getAttributes()); - int droppedAttributes = span.getTotalAttributeCount() - span.getAttributes().size(); - if (droppedAttributes > 0) { - tags.add(new Tag(KEY_DROPPED_ATTRIBUTES_COUNT, TagType.LONG).setVLong(droppedAttributes)); - } - - target.setLogs(toJaegerLogs(span.getEvents())); - int droppedEvents = span.getTotalRecordedEvents() - span.getEvents().size(); - if (droppedEvents > 0) { - tags.add(new Tag(KEY_DROPPED_EVENTS_COUNT, TagType.LONG).setVLong(droppedEvents)); - } - - List references = toSpanRefs(span.getLinks()); - - // add the parent span - if (span.getParentSpanContext().isValid()) { - long parentSpanId = spanIdAsLong(span.getParentSpanId()); - references.add(new SpanRef(SpanRefType.CHILD_OF, traceIdLow, traceIdHigh, parentSpanId)); - target.setParentSpanId(parentSpanId); - } - target.setReferences(references); - - if (span.getKind() != SpanKind.INTERNAL) { - tags.add( - new Tag(KEY_SPAN_KIND, TagType.STRING) - .setVStr(span.getKind().name().toLowerCase(Locale.ROOT))); - } - - if (!span.getStatus().getDescription().isEmpty()) { - tags.add( - new Tag(KEY_SPAN_STATUS_MESSAGE, TagType.STRING) - .setVStr(span.getStatus().getDescription())); - } - - if (span.getStatus().getStatusCode() != StatusCode.UNSET) { - tags.add( - new Tag(KEY_SPAN_STATUS_CODE, TagType.STRING) - .setVStr(span.getStatus().getStatusCode().name())); - } - - tags.add( - new Tag(KEY_INSTRUMENTATION_SCOPE_NAME, TagType.STRING) - .setVStr(span.getInstrumentationScopeInfo().getName())); - // Include instrumentation library name for backwards compatibility - tags.add( - new Tag(KEY_INSTRUMENTATION_LIBRARY_NAME, TagType.STRING) - .setVStr(span.getInstrumentationScopeInfo().getName())); - - if (span.getInstrumentationScopeInfo().getVersion() != null) { - tags.add( - new Tag(KEY_INSTRUMENTATION_SCOPE_VERSION, TagType.STRING) - .setVStr(span.getInstrumentationScopeInfo().getVersion())); - // Include instrumentation library name for backwards compatibility - tags.add( - new Tag(KEY_INSTRUMENTATION_LIBRARY_VERSION, TagType.STRING) - .setVStr(span.getInstrumentationScopeInfo().getVersion())); - } - - if (span.getStatus().getStatusCode() == StatusCode.ERROR) { - tags.add(toTag(KEY_ERROR, true)); - } - target.setTags(tags); - - return target; - } - - /** - * Converts {@link EventData}s into a collection of Jaeger's {@link Log}. - * - * @param timedEvents the timed events to be converted - * @return a collection of Jaeger logs - * @see #toJaegerLog(EventData) - */ - // VisibleForTesting - static List toJaegerLogs(List timedEvents) { - return timedEvents.stream().map(Adapter::toJaegerLog).collect(Collectors.toList()); - } - - /** - * Converts a {@link EventData} into Jaeger's {@link Log}. - * - * @param event the timed event to be converted - * @return a Jaeger log - */ - // VisibleForTesting - static Log toJaegerLog(EventData event) { - Log result = new Log(); - result.setTimestamp(TimeUnit.NANOSECONDS.toMicros(event.getEpochNanos())); - result.addToFields(new Tag(KEY_LOG_EVENT, TagType.STRING).setVStr(event.getName())); - - int droppedAttributesCount = event.getDroppedAttributesCount(); - if (droppedAttributesCount > 0) { - result.addToFields( - new Tag(KEY_EVENT_DROPPED_ATTRIBUTES_COUNT, TagType.LONG) - .setVLong(droppedAttributesCount)); - } - List attributeTags = toTags(event.getAttributes()); - for (Tag attributeTag : attributeTags) { - result.addToFields(attributeTag); - } - return result; - } - - /** - * Converts a map of attributes into a collection of Jaeger's {@link Tag}. - * - * @param attributes the span attributes - * @return a collection of Jaeger key values - * @see #toTag - */ - static List toTags(Attributes attributes) { - List results = new ArrayList<>(); - attributes.forEach((key, value) -> results.add(toTag(key, value))); - return results; - } - - /** - * Converts the given {@link AttributeKey} and value into Jaeger's {@link Tag}. - * - * @param key the entry key as string - * @param value the entry value - * @return a Jaeger key value - */ - // VisibleForTesting - static Tag toTag(AttributeKey key, Object value) { - switch (key.getType()) { - case STRING: - return new Tag(key.getKey(), TagType.STRING).setVStr((String) value); - case LONG: - return new Tag(key.getKey(), TagType.LONG).setVLong((long) value); - case BOOLEAN: - return new Tag(key.getKey(), TagType.BOOL).setVBool((boolean) value); - case DOUBLE: - return new Tag(key.getKey(), TagType.DOUBLE).setVDouble((double) value); - default: - try { - return new Tag(key.getKey(), TagType.STRING).setVStr(JSON.std.asString(value)); - } catch (IOException e) { - // Can't have an exception serializing a plain Java object to a String. Add an exception - // mostly to satisfy the compiler. - throw new UncheckedIOException( - "Error serializing a plain Java object to String. " - + "This is a bug in the OpenTelemetry library.", - e); - } - } - } - - /** - * Converts {@link LinkData}s into a collection of Jaeger's {@link SpanRef}. - * - * @param links the span's links property to be converted - * @return a collection of Jaeger span references - */ - // VisibleForTesting - static List toSpanRefs(List links) { - List spanRefs = new ArrayList<>(links.size()); - for (LinkData link : links) { - spanRefs.add(toSpanRef(link)); - } - return spanRefs; - } - - /** - * Converts a single {@link LinkData} into a Jaeger's {@link SpanRef}. - * - * @param link the OpenTelemetry link to be converted - * @return the Jaeger span reference - */ - // VisibleForTesting - static SpanRef toSpanRef(LinkData link) { - // we can assume that all links are *follows from* - // https://github.com/open-telemetry/opentelemetry-java/issues/475 - // https://github.com/open-telemetry/opentelemetry-java/pull/481/files#r312577862 - return new SpanRef( - SpanRefType.FOLLOWS_FROM, - traceIdAsLongLow(link.getSpanContext().getTraceId()), - traceIdAsLongHigh(link.getSpanContext().getTraceId()), - spanIdAsLong(link.getSpanContext().getSpanId())); - } - - private static long traceIdAsLongHigh(String traceId) { - return new BigInteger(traceId.substring(0, 16), 16).longValue(); - } - - private static long traceIdAsLongLow(String traceId) { - return new BigInteger(traceId.substring(16, 32), 16).longValue(); - } - - private static long spanIdAsLong(String spanId) { - return new BigInteger(spanId, 16).longValue(); - } -} diff --git a/exporters/jaeger-thrift/src/main/java/io/opentelemetry/exporter/jaeger/thrift/JaegerThriftSpanExporter.java b/exporters/jaeger-thrift/src/main/java/io/opentelemetry/exporter/jaeger/thrift/JaegerThriftSpanExporter.java deleted file mode 100644 index bfab4122b05..00000000000 --- a/exporters/jaeger-thrift/src/main/java/io/opentelemetry/exporter/jaeger/thrift/JaegerThriftSpanExporter.java +++ /dev/null @@ -1,170 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.exporter.jaeger.thrift; - -import io.jaegertracing.internal.exceptions.SenderException; -import io.jaegertracing.thrift.internal.senders.ThriftSender; -import io.jaegertracing.thriftjava.Process; -import io.jaegertracing.thriftjava.Span; -import io.jaegertracing.thriftjava.Tag; -import io.jaegertracing.thriftjava.TagType; -import io.opentelemetry.api.common.AttributeKey; -import io.opentelemetry.sdk.common.CompletableResultCode; -import io.opentelemetry.sdk.internal.ThrottlingLogger; -import io.opentelemetry.sdk.resources.Resource; -import io.opentelemetry.sdk.trace.data.SpanData; -import io.opentelemetry.sdk.trace.export.SpanExporter; -import java.net.InetAddress; -import java.net.UnknownHostException; -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; -import java.util.Map; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.logging.Level; -import java.util.logging.Logger; -import java.util.stream.Collectors; -import javax.annotation.concurrent.ThreadSafe; - -/** - * Exports spans to Jaeger via Thrift, using Jaeger's thrift model. - * - * @deprecated Use {@code OtlpGrpcSpanExporter} or {@code OtlpHttpSpanExporter} from opentelemetry-exporter-otlp - * instead. - */ -@ThreadSafe -@Deprecated -public final class JaegerThriftSpanExporter implements SpanExporter { - - private static final AttributeKey SERVICE_NAME = AttributeKey.stringKey("service.name"); - - static final String DEFAULT_ENDPOINT = "http://localhost:14268/api/traces"; - - private static final String DEFAULT_HOST_NAME = "unknown"; - private static final String CLIENT_VERSION_KEY = "jaeger.version"; - private static final String CLIENT_VERSION_VALUE = "opentelemetry-java"; - private static final String HOSTNAME_KEY = "hostname"; - private static final String IP_KEY = "ip"; - private static final String IP_DEFAULT = "0.0.0.0"; - - private final ThrottlingLogger logger = - new ThrottlingLogger(Logger.getLogger(JaegerThriftSpanExporter.class.getName())); - private final AtomicBoolean isShutdown = new AtomicBoolean(); - private final ThriftSender thriftSender; - private final Process process; - - /** - * Creates a new Jaeger gRPC Span Reporter with the given name, using the given channel. - * - * @param thriftSender The sender used for sending the data. - */ - JaegerThriftSpanExporter(ThriftSender thriftSender) { - this.thriftSender = thriftSender; - String hostname; - String ipv4; - - try { - hostname = InetAddress.getLocalHost().getHostName(); - ipv4 = InetAddress.getLocalHost().getHostAddress(); - } catch (UnknownHostException e) { - hostname = DEFAULT_HOST_NAME; - ipv4 = IP_DEFAULT; - } - - Tag clientTag = new Tag(CLIENT_VERSION_KEY, TagType.STRING).setVStr(CLIENT_VERSION_VALUE); - Tag ipv4Tag = new Tag(IP_KEY, TagType.STRING).setVStr(ipv4); - Tag hostnameTag = new Tag(HOSTNAME_KEY, TagType.STRING).setVStr(hostname); - - this.process = new Process(); - this.process.addToTags(clientTag); - this.process.addToTags(ipv4Tag); - this.process.addToTags(hostnameTag); - } - - /** - * Submits all the given spans in a single batch to the Jaeger collector. - * - * @param spans the list of sampled Spans to be exported. - * @return the result of the operation - */ - @Override - public CompletableResultCode export(Collection spans) { - if (isShutdown.get()) { - return CompletableResultCode.ofFailure(); - } - - Map> batches = - spans.stream().collect(Collectors.groupingBy(SpanData::getResource)).entrySet().stream() - .collect( - Collectors.toMap( - entry -> createProcess(entry.getKey()), - entry -> Adapter.toJaeger(entry.getValue()))); - List batchResults = new ArrayList<>(batches.size()); - batches.forEach( - (process, jaegerSpans) -> { - CompletableResultCode batchResult = new CompletableResultCode(); - batchResults.add(batchResult); - try { - // todo: consider making truly async - thriftSender.send(process, jaegerSpans); - batchResult.succeed(); - } catch (SenderException e) { - logger.log(Level.WARNING, "Failed to export spans", e); - batchResult.fail(); - } - }); - return CompletableResultCode.ofAll(batchResults); - } - - private Process createProcess(Resource resource) { - Process result = new Process(this.process); - - String serviceName = resource.getAttribute(SERVICE_NAME); - if (serviceName == null || serviceName.isEmpty()) { - serviceName = Resource.getDefault().getAttribute(SERVICE_NAME); - } - // In practice should never be null unless the default Resource spec is changed. - if (serviceName != null) { - result.setServiceName(serviceName); - } - - List tags = Adapter.toTags(resource.getAttributes()); - tags.forEach(result::addToTags); - return result; - } - - /** - * The Jaeger exporter does not batch spans, so this method will immediately return with success. - * - * @return always Success - */ - @Override - public CompletableResultCode flush() { - return CompletableResultCode.ofSuccess(); - } - - /** - * Returns a new builder instance for this exporter. - * - * @return a new builder instance for this exporter. - */ - public static JaegerThriftSpanExporterBuilder builder() { - return new JaegerThriftSpanExporterBuilder(); - } - - /** - * Initiates an orderly shutdown in which preexisting calls continue but new calls are immediately - * cancelled. - */ - @Override - public CompletableResultCode shutdown() { - if (!isShutdown.compareAndSet(false, true)) { - logger.log(Level.INFO, "Calling shutdown() multiple times."); - } - return CompletableResultCode.ofSuccess(); - } -} diff --git a/exporters/jaeger-thrift/src/main/java/io/opentelemetry/exporter/jaeger/thrift/JaegerThriftSpanExporterBuilder.java b/exporters/jaeger-thrift/src/main/java/io/opentelemetry/exporter/jaeger/thrift/JaegerThriftSpanExporterBuilder.java deleted file mode 100644 index faa88e53a21..00000000000 --- a/exporters/jaeger-thrift/src/main/java/io/opentelemetry/exporter/jaeger/thrift/JaegerThriftSpanExporterBuilder.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.exporter.jaeger.thrift; - -import io.jaegertracing.thrift.internal.senders.HttpSender; -import io.jaegertracing.thrift.internal.senders.ThriftSender; -import javax.annotation.Nullable; -import org.apache.thrift.transport.TTransportException; - -/** - * Builder utility for this exporter. - * - * @deprecated Use {@code OtlpGrpcSpanExporter} or {@code OtlpHttpSpanExporter} from opentelemetry-exporter-otlp - * instead. - */ -@Deprecated -public final class JaegerThriftSpanExporterBuilder { - - private String endpoint = JaegerThriftSpanExporter.DEFAULT_ENDPOINT; - @Nullable private ThriftSender thriftSender; - - /** - * Explicitly set the {@link ThriftSender} instance to use for this Exporter. Will override any - * endpoint that has been set. - * - * @param thriftSender The ThriftSender to use. - * @return this. - */ - public JaegerThriftSpanExporterBuilder setThriftSender(ThriftSender thriftSender) { - this.thriftSender = thriftSender; - return this; - } - - /** - * Sets the Jaeger endpoint to connect to. Needs to include the full API path for trace ingest. - * - *

Optional, defaults to "http://localhost:14268/api/traces". - * - * @param endpoint The Jaeger endpoint URL, ex. "https://jaegerhost:14268/api/traces". - * @return this. - */ - public JaegerThriftSpanExporterBuilder setEndpoint(String endpoint) { - this.endpoint = endpoint; - return this; - } - - /** - * Constructs a new instance of the exporter based on the builder's values. - * - * @return a new exporter's instance. - */ - public JaegerThriftSpanExporter build() { - ThriftSender thriftSender = this.thriftSender; - if (thriftSender == null) { - try { - thriftSender = new HttpSender.Builder(endpoint).build(); - } catch (TTransportException e) { - throw new IllegalStateException("Failed to construct a thrift HttpSender.", e); - } - } - return new JaegerThriftSpanExporter(thriftSender); - } - - JaegerThriftSpanExporterBuilder() {} -} diff --git a/exporters/jaeger-thrift/src/test/java/io/opentelemetry/exporter/jaeger/thrift/AdapterTest.java b/exporters/jaeger-thrift/src/test/java/io/opentelemetry/exporter/jaeger/thrift/AdapterTest.java deleted file mode 100644 index a084b671a36..00000000000 --- a/exporters/jaeger-thrift/src/test/java/io/opentelemetry/exporter/jaeger/thrift/AdapterTest.java +++ /dev/null @@ -1,363 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.exporter.jaeger.thrift; - -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 java.util.concurrent.TimeUnit.MILLISECONDS; -import static org.assertj.core.api.Assertions.assertThat; - -import com.google.common.io.BaseEncoding; -import io.jaegertracing.thriftjava.Log; -import io.jaegertracing.thriftjava.SpanRef; -import io.jaegertracing.thriftjava.SpanRefType; -import io.jaegertracing.thriftjava.Tag; -import io.opentelemetry.api.common.Attributes; -import io.opentelemetry.api.trace.SpanContext; -import io.opentelemetry.api.trace.SpanKind; -import io.opentelemetry.api.trace.StatusCode; -import io.opentelemetry.api.trace.TraceFlags; -import io.opentelemetry.api.trace.TraceState; -import io.opentelemetry.sdk.resources.Resource; -import io.opentelemetry.sdk.testing.trace.TestSpanData; -import io.opentelemetry.sdk.trace.data.EventData; -import io.opentelemetry.sdk.trace.data.LinkData; -import io.opentelemetry.sdk.trace.data.SpanData; -import io.opentelemetry.sdk.trace.data.StatusData; -import java.nio.ByteBuffer; -import java.nio.ByteOrder; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.List; -import javax.annotation.Nullable; -import org.junit.jupiter.api.Test; - -/** Unit tests for {@link Adapter}. */ -class AdapterTest { - private static final BaseEncoding hex = BaseEncoding.base16().lowerCase(); - private static final String LINK_TRACE_ID = "ff000000000000000000000000cba123"; - private static final String LINK_SPAN_ID = "0000000000fed456"; - private static final String TRACE_ID = "0000000000000000ff00000000abc123"; - private static final String SPAN_ID = "ff00000000def456"; - private static final String PARENT_SPAN_ID = "0000000000aef789"; - - @Test - void testThriftSpans() { - long duration = 900; // ms - long startMs = System.currentTimeMillis(); - long endMs = startMs + duration; - - SpanData span = getSpanData(startMs, endMs, SpanKind.SERVER); - List spans = Collections.singletonList(span); - - List jaegerSpans = Adapter.toJaeger(spans); - - // the span contents are checked somewhere else - assertThat(jaegerSpans).hasSize(1); - } - - @Test - void testThriftSpan() { - long duration = 900; // ms - long startMs = System.currentTimeMillis(); - long endMs = startMs + duration; - - SpanData span = getSpanData(startMs, endMs, SpanKind.SERVER, 2, 4); - - // test - io.jaegertracing.thriftjava.Span jaegerSpan = Adapter.toJaeger(span); - - String rebuildTraceId = - traceIdFromLongs(jaegerSpan.getTraceIdHigh(), jaegerSpan.getTraceIdLow()); - assertThat(rebuildTraceId).isEqualTo(span.getTraceId()); - assertThat(spanIdFromLong(jaegerSpan.getSpanId())).isEqualTo(span.getSpanId()); - assertThat(jaegerSpan.getOperationName()).isEqualTo("GET /api/endpoint"); - assertThat(jaegerSpan.getStartTime()).isEqualTo(MILLISECONDS.toMicros(startMs)); - assertThat(jaegerSpan.getDuration()).isEqualTo(MILLISECONDS.toMicros(duration)); - - assertThat(jaegerSpan.getTagsSize()).isEqualTo(8); - assertThat(getValue(jaegerSpan.getTags(), Adapter.KEY_SPAN_KIND).getVStr()).isEqualTo("server"); - assertThat(getValue(jaegerSpan.getTags(), Adapter.KEY_SPAN_STATUS_CODE).getVLong()) - .isEqualTo(0); - assertThat(getValue(jaegerSpan.getTags(), Adapter.KEY_SPAN_STATUS_MESSAGE).getVStr()) - .isEqualTo("ok!"); - assertThat(getValue(jaegerSpan.getTags(), Adapter.KEY_DROPPED_EVENTS_COUNT).getVLong()) - .isEqualTo(1); - assertThat(getValue(jaegerSpan.getTags(), Adapter.KEY_DROPPED_ATTRIBUTES_COUNT).getVLong()) - .isEqualTo(3); - - assertThat(jaegerSpan.getLogsSize()).isEqualTo(1); - Log log = jaegerSpan.getLogs().get(0); - assertThat(getValue(log.getFields(), Adapter.KEY_LOG_EVENT).getVStr()) - .isEqualTo("the log message"); - assertThat(getValue(log.getFields(), "foo").getVStr()).isEqualTo("bar"); - - assertThat(jaegerSpan.getReferencesSize()).isEqualTo(2); - - assertHasFollowsFrom(jaegerSpan); - assertHasParent(jaegerSpan); - } - - @Test - void testThriftSpan_internal() { - long duration = 900; // ms - long startMs = System.currentTimeMillis(); - long endMs = startMs + duration; - - SpanData span = getSpanData(startMs, endMs, SpanKind.INTERNAL); - - // test - io.jaegertracing.thriftjava.Span jaegerSpan = Adapter.toJaeger(span); - - assertThat(jaegerSpan.getTagsSize()).isEqualTo(5); - assertThat(getValue(jaegerSpan.getTags(), Adapter.KEY_SPAN_KIND)).isNull(); - } - - @Test - void testJaegerLogs() { - // prepare - EventData eventsData = getTimedEvent(); - - // test - Collection logs = Adapter.toJaegerLogs(Collections.singletonList(eventsData)); - - // verify - assertThat(logs).hasSize(1); - } - - @Test - void testJaegerLog() { - // prepare - EventData event = getTimedEvent(); - - // test - Log log = Adapter.toJaegerLog(event); - - // verify - assertThat(log.getFieldsSize()).isEqualTo(2); - - assertThat(getValue(log.getFields(), Adapter.KEY_LOG_EVENT).getVStr()) - .isEqualTo("the log message"); - assertThat(getValue(log.getFields(), "foo").getVStr()).isEqualTo("bar"); - assertThat(getValue(log.getFields(), Adapter.KEY_EVENT_DROPPED_ATTRIBUTES_COUNT)).isNull(); - } - - @Test - void jaegerLog_droppedAttributes() { - EventData event = getTimedEvent(3); - - // test - Log log = Adapter.toJaegerLog(event); - - // verify - assertThat(getValue(log.getFields(), Adapter.KEY_EVENT_DROPPED_ATTRIBUTES_COUNT).getVLong()) - .isEqualTo(2); - } - - @Test - void testKeyValue() { - // test - Tag kvB = Adapter.toTag(booleanKey("valueB"), true); - Tag kvD = Adapter.toTag(doubleKey("valueD"), 1.); - Tag kvI = Adapter.toTag(longKey("valueI"), 2L); - Tag kvS = Adapter.toTag(stringKey("valueS"), "foobar"); - Tag kvArrayB = Adapter.toTag(booleanArrayKey("valueArrayB"), Arrays.asList(true, false)); - Tag kvArrayD = Adapter.toTag(doubleArrayKey("valueArrayD"), Arrays.asList(1.2345, 6.789)); - Tag kvArrayI = Adapter.toTag(longArrayKey("valueArrayI"), Arrays.asList(12345L, 67890L)); - Tag kvArrayS = Adapter.toTag(stringArrayKey("valueArrayS"), Arrays.asList("foobar", "barfoo")); - - // verify - assertThat(kvB.isVBool()).isTrue(); - - assertThat(kvD.getVDouble()).isEqualTo(1); - assertThat(kvI.getVLong()).isEqualTo(2); - assertThat(kvS.getVStr()).isEqualTo("foobar"); - assertThat(kvArrayB.getVStr()).isEqualTo("[true,false]"); - assertThat(kvArrayD.getVStr()).isEqualTo("[1.2345,6.789]"); - assertThat(kvArrayI.getVStr()).isEqualTo("[12345,67890]"); - assertThat(kvArrayS.getVStr()).isEqualTo("[\"foobar\",\"barfoo\"]"); - } - - @Test - void testSpanRefs() { - // prepare - LinkData link = - LinkData.create(createSpanContext("00000000000000000000000000cba123", "0000000000fed456")); - - // test - Collection spanRefs = Adapter.toSpanRefs(Collections.singletonList(link)); - - // verify - assertThat(spanRefs).hasSize(1); // the actual span ref is tested in another test - } - - @Test - void testSpanRef() { - // prepare - LinkData link = LinkData.create(createSpanContext(TRACE_ID, SPAN_ID)); - - // test - SpanRef spanRef = Adapter.toSpanRef(link); - - // verify - assertThat(spanIdFromLong(spanRef.getSpanId())).isEqualTo(SPAN_ID); - assertThat(traceIdFromLongs(spanRef.getTraceIdHigh(), spanRef.getTraceIdLow())) - .isEqualTo(TRACE_ID); - assertThat(spanRef.getRefType()).isEqualTo(SpanRefType.FOLLOWS_FROM); - } - - @Test - void testStatusNotUnset() { - long startMs = System.currentTimeMillis(); - long endMs = startMs + 900; - SpanData span = - TestSpanData.builder() - .setHasEnded(true) - .setSpanContext(createSpanContext(TRACE_ID, SPAN_ID)) - .setName("GET /api/endpoint") - .setStartEpochNanos(MILLISECONDS.toNanos(startMs)) - .setEndEpochNanos(MILLISECONDS.toNanos(endMs)) - .setKind(SpanKind.SERVER) - .setStatus(StatusData.error()) - .setTotalRecordedEvents(0) - .setTotalRecordedLinks(0) - .build(); - - assertThat(Adapter.toJaeger(span)).isNotNull(); - } - - @Test - void testSpanError() { - Attributes attributes = - Attributes.of( - stringKey("error.type"), - this.getClass().getName(), - stringKey("error.message"), - "server error"); - long startMs = System.currentTimeMillis(); - long endMs = startMs + 900; - SpanData span = - TestSpanData.builder() - .setHasEnded(true) - .setSpanContext(createSpanContext(TRACE_ID, SPAN_ID)) - .setName("GET /api/endpoint") - .setStartEpochNanos(MILLISECONDS.toNanos(startMs)) - .setEndEpochNanos(MILLISECONDS.toNanos(endMs)) - .setKind(SpanKind.SERVER) - .setStatus(StatusData.error()) - .setAttributes(attributes) - .setTotalRecordedEvents(0) - .setTotalRecordedLinks(0) - .build(); - - io.jaegertracing.thriftjava.Span jaegerSpan = Adapter.toJaeger(span); - assertThat(getValue(jaegerSpan.getTags(), "error.type").getVStr()) - .isEqualTo(this.getClass().getName()); - assertThat(getValue(jaegerSpan.getTags(), "error").isVBool()).isTrue(); - } - - private static EventData getTimedEvent() { - return getTimedEvent(-1); - } - - private static EventData getTimedEvent(int totalAttributeCount) { - long epochNanos = MILLISECONDS.toNanos(System.currentTimeMillis()); - Attributes attributes = Attributes.of(stringKey("foo"), "bar"); - if (totalAttributeCount <= 0) { - totalAttributeCount = attributes.size(); - } - return EventData.create(epochNanos, "the log message", attributes, totalAttributeCount); - } - - private static SpanData getSpanData(long startMs, long endMs, SpanKind kind) { - return getSpanData(startMs, endMs, kind, 1, 1); - } - - private static SpanData getSpanData( - long startMs, long endMs, SpanKind kind, int totalRecordedEvents, int totalAttributeCount) { - Attributes attributes = Attributes.of(booleanKey("valueB"), true); - - LinkData link = LinkData.create(createSpanContext(LINK_TRACE_ID, LINK_SPAN_ID), attributes); - - return TestSpanData.builder() - .setHasEnded(true) - .setSpanContext(createSpanContext(TRACE_ID, SPAN_ID)) - .setParentSpanContext( - SpanContext.create( - TRACE_ID, PARENT_SPAN_ID, TraceFlags.getDefault(), TraceState.getDefault())) - .setName("GET /api/endpoint") - .setStartEpochNanos(MILLISECONDS.toNanos(startMs)) - .setEndEpochNanos(MILLISECONDS.toNanos(endMs)) - .setAttributes(Attributes.of(booleanKey("valueB"), true)) - .setTotalAttributeCount(totalAttributeCount) - .setEvents(Collections.singletonList(getTimedEvent())) - .setTotalRecordedEvents(totalRecordedEvents) - .setLinks(Collections.singletonList(link)) - .setTotalRecordedLinks(1) - .setKind(kind) - .setResource(Resource.create(Attributes.empty())) - .setStatus(StatusData.create(StatusCode.OK, "ok!")) - .build(); - } - - private static SpanContext createSpanContext(String traceId, String spanId) { - return SpanContext.create(traceId, spanId, TraceFlags.getDefault(), TraceState.getDefault()); - } - - @Nullable - private static Tag getValue(List tagsList, String s) { - for (Tag kv : tagsList) { - if (kv.getKey().equals(s)) { - return kv; - } - } - return null; - } - - private static void assertHasFollowsFrom(io.jaegertracing.thriftjava.Span jaegerSpan) { - boolean found = false; - for (SpanRef spanRef : jaegerSpan.getReferences()) { - - if (SpanRefType.FOLLOWS_FROM.equals(spanRef.getRefType())) { - assertThat(traceIdFromLongs(spanRef.getTraceIdHigh(), spanRef.getTraceIdLow())) - .isEqualTo(LINK_TRACE_ID); - assertThat(spanIdFromLong(spanRef.getSpanId())).isEqualTo(LINK_SPAN_ID); - found = true; - } - } - assertThat(found).isTrue(); - } - - private static void assertHasParent(io.jaegertracing.thriftjava.Span jaegerSpan) { - boolean found = false; - for (SpanRef spanRef : jaegerSpan.getReferences()) { - if (SpanRefType.CHILD_OF.equals(spanRef.getRefType())) { - assertThat(traceIdFromLongs(spanRef.getTraceIdHigh(), spanRef.getTraceIdLow())) - .isEqualTo(TRACE_ID); - assertThat(spanIdFromLong(spanRef.getSpanId())).isEqualTo(PARENT_SPAN_ID); - found = true; - } - } - assertThat(found).isTrue(); - assertThat(spanIdFromLong(jaegerSpan.getParentSpanId())).isEqualTo(PARENT_SPAN_ID); - } - - private static String traceIdFromLongs(long high, long low) { - return hex.encode( - ByteBuffer.allocate(16).order(ByteOrder.BIG_ENDIAN).putLong(high).putLong(low).array()); - } - - private static String spanIdFromLong(long id) { - return hex.encode(ByteBuffer.allocate(8).order(ByteOrder.BIG_ENDIAN).putLong(id).array()); - } -} diff --git a/exporters/jaeger-thrift/src/test/java/io/opentelemetry/exporter/jaeger/thrift/JaegerThriftIntegrationTest.java b/exporters/jaeger-thrift/src/test/java/io/opentelemetry/exporter/jaeger/thrift/JaegerThriftIntegrationTest.java deleted file mode 100644 index fe95b30894b..00000000000 --- a/exporters/jaeger-thrift/src/test/java/io/opentelemetry/exporter/jaeger/thrift/JaegerThriftIntegrationTest.java +++ /dev/null @@ -1,135 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.exporter.jaeger.thrift; - -import static io.opentelemetry.api.common.AttributeKey.stringKey; - -import com.fasterxml.jackson.core.TreeNode; -import com.fasterxml.jackson.jr.ob.JSON; -import com.fasterxml.jackson.jr.stree.JacksonJrsTreeCodec; -import io.jaegertracing.thrift.internal.senders.UdpSender; -import io.opentelemetry.api.OpenTelemetry; -import io.opentelemetry.api.trace.Span; -import io.opentelemetry.sdk.OpenTelemetrySdk; -import io.opentelemetry.sdk.resources.Resource; -import io.opentelemetry.sdk.trace.SdkTracerProvider; -import io.opentelemetry.sdk.trace.export.SimpleSpanProcessor; -import java.time.Duration; -import okhttp3.OkHttpClient; -import okhttp3.Request; -import okhttp3.Response; -import org.apache.thrift.transport.TTransportException; -import org.awaitility.Awaitility; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.ValueSource; -import org.slf4j.LoggerFactory; -import org.testcontainers.containers.GenericContainer; -import org.testcontainers.containers.output.Slf4jLogConsumer; -import org.testcontainers.containers.wait.strategy.Wait; -import org.testcontainers.images.PullPolicy; -import org.testcontainers.junit.jupiter.Container; -import org.testcontainers.junit.jupiter.Testcontainers; - -@Testcontainers(disabledWithoutDocker = true) -@SuppressWarnings("deprecation") // Testing deprecated code -class JaegerThriftIntegrationTest { - private static final OkHttpClient client = new OkHttpClient(); - - private static final int QUERY_PORT = 16686; - private static final int THRIFT_HTTP_PORT = 14268; - - private static final int THRIFT_UDP_PORT = 6831; - private static final int HEALTH_PORT = 14269; - private static final String SERVICE_NAME = "E2E-test"; - private static final String JAEGER_URL = "http://localhost"; - - @Container - public static final GenericContainer jaegerContainer = - new GenericContainer<>("ghcr.io/open-telemetry/opentelemetry-java/jaeger:1.32") - .withImagePullPolicy(PullPolicy.alwaysPull()) - .withExposedPorts(THRIFT_HTTP_PORT, THRIFT_UDP_PORT, QUERY_PORT, HEALTH_PORT) - .withLogConsumer(new Slf4jLogConsumer(LoggerFactory.getLogger("jaeger"))) - .waitingFor(Wait.forHttp("/").forPort(HEALTH_PORT)); - - @ParameterizedTest - @ValueSource(booleans = {false, true}) - void testJaegerIntegration(boolean udp) { - OpenTelemetry openTelemetry = initOpenTelemetry(udp); - imitateWork(openTelemetry); - Awaitility.await() - .atMost(Duration.ofSeconds(30)) - .until(JaegerThriftIntegrationTest::assertJaegerHasATrace); - } - - private static OpenTelemetry initOpenTelemetry(boolean udp) { - JaegerThriftSpanExporterBuilder jaegerExporter = JaegerThriftSpanExporter.builder(); - - if (udp) { - int mappedPort = jaegerContainer.getMappedPort(THRIFT_UDP_PORT); - try { - jaegerExporter.setThriftSender(new UdpSender("localhost", mappedPort, 0)); - } catch (TTransportException e) { - throw new IllegalStateException(e); - } - } else { - int mappedPort = jaegerContainer.getMappedPort(THRIFT_HTTP_PORT); - jaegerExporter.setEndpoint(JAEGER_URL + ":" + mappedPort + "/api/traces"); - } - - return OpenTelemetrySdk.builder() - .setTracerProvider( - SdkTracerProvider.builder() - .addSpanProcessor(SimpleSpanProcessor.create(jaegerExporter.build())) - .setResource( - Resource.getDefault().toBuilder() - .put(stringKey("service.name"), SERVICE_NAME) - .build()) - .build()) - .build(); - } - - private void imitateWork(OpenTelemetry openTelemetry) { - Span span = - openTelemetry.getTracer(getClass().getCanonicalName()).spanBuilder("Test span").startSpan(); - span.addEvent("some event"); - try { - Thread.sleep(1000); - } catch (InterruptedException e) { - throw new RuntimeException(e); - } - span.end(); - } - - private static boolean assertJaegerHasATrace() { - try { - Integer mappedPort = jaegerContainer.getMappedPort(QUERY_PORT); - String url = - String.format( - "%s/api/traces?service=%s", - String.format(JAEGER_URL + ":%d", mappedPort), SERVICE_NAME); - - Request request = - new Request.Builder() - .url(url) - .header("Content-Type", "application/json") - .header("Accept", "application/json") - .build(); - - TreeNode json; - try (Response response = client.newCall(request).execute()) { - json = - JSON.builder() - .treeCodec(new JacksonJrsTreeCodec()) - .build() - .treeFrom(response.body().byteStream()); - } - - return json.get("data").get(0).get("traceID") != null; - } catch (Exception e) { - return false; - } - } -} diff --git a/exporters/jaeger-thrift/src/test/java/io/opentelemetry/exporter/jaeger/thrift/JaegerThriftSpanExporterTest.java b/exporters/jaeger-thrift/src/test/java/io/opentelemetry/exporter/jaeger/thrift/JaegerThriftSpanExporterTest.java deleted file mode 100644 index f90ceddbad7..00000000000 --- a/exporters/jaeger-thrift/src/test/java/io/opentelemetry/exporter/jaeger/thrift/JaegerThriftSpanExporterTest.java +++ /dev/null @@ -1,274 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.exporter.jaeger.thrift; - -import static io.opentelemetry.api.common.AttributeKey.stringKey; -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.verify; - -import io.github.netmikey.logunit.api.LogCapturer; -import io.jaegertracing.internal.exceptions.SenderException; -import io.jaegertracing.thrift.internal.senders.ThriftSender; -import io.jaegertracing.thriftjava.Process; -import io.jaegertracing.thriftjava.Span; -import io.jaegertracing.thriftjava.SpanRef; -import io.jaegertracing.thriftjava.SpanRefType; -import io.jaegertracing.thriftjava.Tag; -import io.jaegertracing.thriftjava.TagType; -import io.opentelemetry.api.common.Attributes; -import io.opentelemetry.api.trace.SpanContext; -import io.opentelemetry.api.trace.SpanKind; -import io.opentelemetry.api.trace.TraceFlags; -import io.opentelemetry.api.trace.TraceState; -import io.opentelemetry.internal.testing.slf4j.SuppressLogger; -import io.opentelemetry.sdk.common.CompletableResultCode; -import io.opentelemetry.sdk.common.InstrumentationScopeInfo; -import io.opentelemetry.sdk.resources.Resource; -import io.opentelemetry.sdk.testing.trace.TestSpanData; -import io.opentelemetry.sdk.trace.data.SpanData; -import io.opentelemetry.sdk.trace.data.StatusData; -import java.net.InetAddress; -import java.net.UnknownHostException; -import java.time.Duration; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import java.util.concurrent.TimeUnit; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.junit.jupiter.api.extension.RegisterExtension; -import org.mockito.Mock; -import org.mockito.junit.jupiter.MockitoExtension; - -@ExtendWith(MockitoExtension.class) -@SuppressWarnings("deprecation") // Testing deprecated code -class JaegerThriftSpanExporterTest { - - private static final String TRACE_ID = "a0000000000000000000000000abc123"; - private static final long TRACE_ID_HIGH = 0xa000000000000000L; - private static final long TRACE_ID_LOW = 0x0000000000abc123L; - private static final String SPAN_ID = "00000f0000def456"; - private static final long SPAN_ID_LONG = 0x00000f0000def456L; - private static final String SPAN_ID_2 = "00a0000000aef789"; - private static final long SPAN_ID_2_LONG = 0x00a0000000aef789L; - private static final SpanContext SPAN_CONTEXT = - SpanContext.create(TRACE_ID, SPAN_ID, TraceFlags.getDefault(), TraceState.getDefault()); - private static final SpanContext SPAN_CONTEXT_2 = - SpanContext.create(TRACE_ID, SPAN_ID_2, TraceFlags.getDefault(), TraceState.getDefault()); - private static final Duration DURATION = Duration.ofMillis(900); - - @RegisterExtension - LogCapturer logs = LogCapturer.create().captureForType(JaegerThriftSpanExporter.class); - - private JaegerThriftSpanExporter exporter; - @Mock private ThriftSender thriftSender; - - @BeforeEach - void beforeEach() { - exporter = JaegerThriftSpanExporter.builder().setThriftSender(thriftSender).build(); - } - - @Test - void testExport() throws SenderException, UnknownHostException { - SpanData span = - testSpanData( - Resource.create( - Attributes.of( - stringKey("service.name"), - "myServiceName", - stringKey("resource-attr-key"), - "resource-attr-value")), - "GET /api/endpoint", - SPAN_CONTEXT, - SPAN_CONTEXT_2); - - // test - CompletableResultCode result = exporter.export(Collections.singletonList(span)); - result.join(1, TimeUnit.SECONDS); - assertThat(result.isSuccess()).isEqualTo(true); - - // verify - Process expectedProcess = new Process("myServiceName"); - expectedProcess.addToTags( - new Tag("jaeger.version", TagType.STRING).setVStr("opentelemetry-java")); - expectedProcess.addToTags( - new Tag("ip", TagType.STRING).setVStr(InetAddress.getLocalHost().getHostAddress())); - expectedProcess.addToTags( - new Tag("hostname", TagType.STRING).setVStr(InetAddress.getLocalHost().getHostName())); - expectedProcess.addToTags( - new Tag("resource-attr-key", TagType.STRING).setVStr("resource-attr-value")); - expectedProcess.addToTags(new Tag("service.name", TagType.STRING).setVStr("myServiceName")); - - Span expectedSpan = - new Span() - .setTraceIdHigh(TRACE_ID_HIGH) - .setTraceIdLow(TRACE_ID_LOW) - .setSpanId(SPAN_ID_LONG) - .setOperationName("GET /api/endpoint") - .setReferences( - Collections.singletonList( - new SpanRef() - .setSpanId(SPAN_ID_2_LONG) - .setTraceIdHigh(TRACE_ID_HIGH) - .setTraceIdLow(TRACE_ID_LOW) - .setRefType(SpanRefType.CHILD_OF))) - .setParentSpanId(SPAN_ID_2_LONG) - .setStartTime(TimeUnit.NANOSECONDS.toMicros(span.getStartEpochNanos())) - .setDuration(DURATION.toMillis() * 1000) - .setLogs(Collections.emptyList()); - expectedSpan.addToTags(new Tag("span.kind", TagType.STRING).setVStr("consumer")); - expectedSpan.addToTags(new Tag("otel.status_code", TagType.STRING).setVStr("OK")); - expectedSpan.addToTags( - new Tag("otel.scope.name", TagType.STRING).setVStr("io.opentelemetry.auto")); - expectedSpan.addToTags( - new Tag("otel.library.name", TagType.STRING).setVStr("io.opentelemetry.auto")); - expectedSpan.addToTags(new Tag("otel.scope.version", TagType.STRING).setVStr("1.0.0")); - expectedSpan.addToTags(new Tag("otel.library.version", TagType.STRING).setVStr("1.0.0")); - - List expectedSpans = Collections.singletonList(expectedSpan); - verify(thriftSender).send(expectedProcess, expectedSpans); - } - - @Test - void testExportMultipleResources() throws SenderException, UnknownHostException { - SpanData span = - testSpanData( - Resource.create( - Attributes.of( - stringKey("service.name"), - "myServiceName1", - stringKey("resource-attr-key-1"), - "resource-attr-value-1")), - "GET /api/endpoint/1", - SPAN_CONTEXT, - SpanContext.getInvalid()); - - SpanData span2 = - testSpanData( - Resource.create( - Attributes.of( - stringKey("service.name"), - "myServiceName2", - stringKey("resource-attr-key-2"), - "resource-attr-value-2")), - "GET /api/endpoint/2", - SPAN_CONTEXT_2, - SpanContext.getInvalid()); - - // test - CompletableResultCode result = exporter.export(Arrays.asList(span, span2)); - result.join(1, TimeUnit.SECONDS); - assertThat(result.isSuccess()).isEqualTo(true); - - // verify - Process expectedProcess1 = new Process("myServiceName1"); - expectedProcess1.addToTags( - new Tag("jaeger.version", TagType.STRING).setVStr("opentelemetry-java")); - expectedProcess1.addToTags( - new Tag("ip", TagType.STRING).setVStr(InetAddress.getLocalHost().getHostAddress())); - expectedProcess1.addToTags( - new Tag("hostname", TagType.STRING).setVStr(InetAddress.getLocalHost().getHostName())); - expectedProcess1.addToTags( - new Tag("resource-attr-key-1", TagType.STRING).setVStr("resource-attr-value-1")); - expectedProcess1.addToTags(new Tag("service.name", TagType.STRING).setVStr("myServiceName1")); - - Process expectedProcess2 = new Process("myServiceName2"); - expectedProcess2.addToTags( - new Tag("jaeger.version", TagType.STRING).setVStr("opentelemetry-java")); - expectedProcess2.addToTags( - new Tag("ip", TagType.STRING).setVStr(InetAddress.getLocalHost().getHostAddress())); - expectedProcess2.addToTags( - new Tag("hostname", TagType.STRING).setVStr(InetAddress.getLocalHost().getHostName())); - expectedProcess2.addToTags( - new Tag("resource-attr-key-2", TagType.STRING).setVStr("resource-attr-value-2")); - expectedProcess2.addToTags(new Tag("service.name", TagType.STRING).setVStr("myServiceName2")); - - Span expectedSpan1 = - new Span() - .setTraceIdHigh(TRACE_ID_HIGH) - .setTraceIdLow(TRACE_ID_LOW) - .setSpanId(SPAN_ID_LONG) - .setOperationName("GET /api/endpoint/1") - .setReferences(Collections.emptyList()) - .setStartTime(TimeUnit.NANOSECONDS.toMicros(span.getStartEpochNanos())) - .setDuration(DURATION.toMillis() * 1000) - .setLogs(Collections.emptyList()); - expectedSpan1.addToTags(new Tag("span.kind", TagType.STRING).setVStr("consumer")); - expectedSpan1.addToTags(new Tag("otel.status_code", TagType.STRING).setVStr("OK")); - expectedSpan1.addToTags( - new Tag("otel.scope.name", TagType.STRING).setVStr("io.opentelemetry.auto")); - expectedSpan1.addToTags( - new Tag("otel.library.name", TagType.STRING).setVStr("io.opentelemetry.auto")); - expectedSpan1.addToTags(new Tag("otel.scope.version", TagType.STRING).setVStr("1.0.0")); - expectedSpan1.addToTags(new Tag("otel.library.version", TagType.STRING).setVStr("1.0.0")); - - Span expectedSpan2 = - new Span() - .setTraceIdHigh(TRACE_ID_HIGH) - .setTraceIdLow(TRACE_ID_LOW) - .setSpanId(SPAN_ID_2_LONG) - .setOperationName("GET /api/endpoint/2") - .setReferences(Collections.emptyList()) - .setStartTime(TimeUnit.NANOSECONDS.toMicros(span2.getStartEpochNanos())) - .setDuration(DURATION.toMillis() * 1000) - .setLogs(Collections.emptyList()); - expectedSpan2.addToTags(new Tag("span.kind", TagType.STRING).setVStr("consumer")); - expectedSpan2.addToTags(new Tag("otel.status_code", TagType.STRING).setVStr("OK")); - expectedSpan2.addToTags( - new Tag("otel.scope.name", TagType.STRING).setVStr("io.opentelemetry.auto")); - expectedSpan2.addToTags( - new Tag("otel.library.name", TagType.STRING).setVStr("io.opentelemetry.auto")); - expectedSpan2.addToTags(new Tag("otel.scope.version", TagType.STRING).setVStr("1.0.0")); - expectedSpan2.addToTags(new Tag("otel.library.version", TagType.STRING).setVStr("1.0.0")); - - verify(thriftSender).send(expectedProcess2, Collections.singletonList(expectedSpan2)); - verify(thriftSender).send(expectedProcess1, Collections.singletonList(expectedSpan1)); - } - - @Test - @SuppressLogger(JaegerThriftSpanExporter.class) - void shutdown() { - assertThat(exporter.shutdown().join(1, TimeUnit.SECONDS).isSuccess()).isTrue(); - assertThat(logs.getEvents()).isEmpty(); - assertThat( - exporter - .export( - Collections.singletonList( - testSpanData( - Resource.getDefault(), - "span name", - SPAN_CONTEXT, - SpanContext.getInvalid()))) - .join(10, TimeUnit.SECONDS) - .isSuccess()) - .isFalse(); - assertThat(exporter.shutdown().join(1, TimeUnit.SECONDS).isSuccess()).isTrue(); - logs.assertContains("Calling shutdown() multiple times."); - } - - private static SpanData testSpanData( - Resource resource, String spanName, SpanContext spanContext, SpanContext parentContext) { - long startMs = System.currentTimeMillis(); - long endMs = startMs + DURATION.toMillis(); - return TestSpanData.builder() - .setHasEnded(true) - .setSpanContext(spanContext) - .setParentSpanContext(parentContext) - .setName(spanName) - .setStartEpochNanos(TimeUnit.MILLISECONDS.toNanos(startMs)) - .setEndEpochNanos(TimeUnit.MILLISECONDS.toNanos(endMs)) - .setStatus(StatusData.ok()) - .setKind(SpanKind.CONSUMER) - .setLinks(Collections.emptyList()) - .setTotalRecordedLinks(0) - .setTotalRecordedEvents(0) - .setInstrumentationScopeInfo( - InstrumentationScopeInfo.builder("io.opentelemetry.auto").setVersion("1.0.0").build()) - .setResource(resource) - .build(); - } -} diff --git a/exporters/jaeger/build.gradle.kts b/exporters/jaeger/build.gradle.kts deleted file mode 100644 index f59e6d62042..00000000000 --- a/exporters/jaeger/build.gradle.kts +++ /dev/null @@ -1,48 +0,0 @@ -plugins { - id("otel.java-conventions") - id("otel.publish-conventions") - - id("otel.animalsniffer-conventions") - - id("com.squareup.wire") -} - -description = "OpenTelemetry - Jaeger Exporter" -otelJava.moduleName.set("io.opentelemetry.exporter.jaeger") - -dependencies { - api(project(":sdk:all")) - - protoSource(project(":exporters:jaeger-proto")) - - implementation(project(":exporters:common")) - implementation(project(":exporters:sender:okhttp")) - implementation(project(":sdk-extensions:autoconfigure-spi")) - - compileOnly("io.grpc:grpc-stub") - - implementation("com.fasterxml.jackson.jr:jackson-jr-objects") - - testImplementation(project(":exporters:jaeger-proto")) - - testImplementation("com.fasterxml.jackson.jr:jackson-jr-stree") - testImplementation("com.google.protobuf:protobuf-java-util") - testImplementation("com.linecorp.armeria:armeria-junit5") - testImplementation("com.linecorp.armeria:armeria-grpc-protocol") - testImplementation("com.squareup.okhttp3:okhttp") - testImplementation("org.testcontainers:junit-jupiter") - - testImplementation(project(":sdk:testing")) -} - -wire { - custom { - schemaHandlerFactoryClass = "io.opentelemetry.gradle.ProtoFieldsWireHandlerFactory" - } -} - -afterEvaluate { - tasks.getByName("generateMainProtos") { - setDependsOn(configurations.getByName("protoPath")) - } -} diff --git a/exporters/jaeger/src/main/java/io/opentelemetry/exporter/jaeger/BatchMarshaler.java b/exporters/jaeger/src/main/java/io/opentelemetry/exporter/jaeger/BatchMarshaler.java deleted file mode 100644 index 4900b7a47d9..00000000000 --- a/exporters/jaeger/src/main/java/io/opentelemetry/exporter/jaeger/BatchMarshaler.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.exporter.jaeger; - -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.jaeger.proto.api_v2.internal.Batch; -import io.opentelemetry.sdk.resources.Resource; -import io.opentelemetry.sdk.trace.data.SpanData; -import java.io.IOException; -import java.util.List; - -final class BatchMarshaler extends MarshalerWithSize { - - private final SpanMarshaler[] spans; - private final ProcessMarshaler process; - - static BatchMarshaler create(List spans, Resource resource) { - SpanMarshaler[] spanMarshalers = SpanMarshaler.createRepeated(spans); - ProcessMarshaler processMarshaler = ProcessMarshaler.create(resource); - return new BatchMarshaler(spanMarshalers, processMarshaler); - } - - BatchMarshaler(SpanMarshaler[] spans, ProcessMarshaler process) { - super(calculateSize(spans, process)); - this.spans = spans; - this.process = process; - } - - @Override - protected void writeTo(Serializer output) throws IOException { - output.serializeRepeatedMessage(Batch.SPANS, spans); - output.serializeMessage(Batch.PROCESS, process); - } - - private static int calculateSize(SpanMarshaler[] spans, ProcessMarshaler process) { - int size = 0; - size += MarshalerUtil.sizeRepeatedMessage(Batch.SPANS, spans); - size += MarshalerUtil.sizeMessage(Batch.PROCESS, process); - return size; - } -} diff --git a/exporters/jaeger/src/main/java/io/opentelemetry/exporter/jaeger/JaegerGrpcSpanExporter.java b/exporters/jaeger/src/main/java/io/opentelemetry/exporter/jaeger/JaegerGrpcSpanExporter.java deleted file mode 100644 index acaa3ea42ad..00000000000 --- a/exporters/jaeger/src/main/java/io/opentelemetry/exporter/jaeger/JaegerGrpcSpanExporter.java +++ /dev/null @@ -1,124 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.exporter.jaeger; - -import io.opentelemetry.api.common.AttributeKey; -import io.opentelemetry.exporter.internal.grpc.GrpcExporter; -import io.opentelemetry.sdk.common.CompletableResultCode; -import io.opentelemetry.sdk.resources.Resource; -import io.opentelemetry.sdk.trace.data.SpanData; -import io.opentelemetry.sdk.trace.export.SpanExporter; -import java.net.InetAddress; -import java.net.UnknownHostException; -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; -import java.util.stream.Collectors; -import javax.annotation.concurrent.ThreadSafe; - -/** - * Exports spans to Jaeger via gRPC, using Jaeger's protobuf model. - * - * @deprecated Use {@code OtlpGrpcSpanExporter} or {@code OtlpHttpSpanExporter} from opentelemetry-exporter-otlp - * instead. - */ -@ThreadSafe -@Deprecated -public final class JaegerGrpcSpanExporter implements SpanExporter { - - private static final String DEFAULT_HOST_NAME = "unknown"; - private static final AttributeKey CLIENT_VERSION_KEY = - AttributeKey.stringKey("jaeger.version"); - private static final String CLIENT_VERSION_VALUE = "opentelemetry-java"; - private static final AttributeKey HOSTNAME_KEY = AttributeKey.stringKey("hostname"); - private static final String IP_DEFAULT = "0.0.0.0"; - // Visible for testing - static final AttributeKey IP_KEY = AttributeKey.stringKey("ip"); - - private final GrpcExporter delegate; - - // Jaeger-specific resource information - private final Resource jaegerResource; - - JaegerGrpcSpanExporter(GrpcExporter delegate) { - this.delegate = delegate; - - String hostname; - String ipv4; - - try { - hostname = InetAddress.getLocalHost().getHostName(); - ipv4 = InetAddress.getLocalHost().getHostAddress(); - } catch (UnknownHostException e) { - hostname = DEFAULT_HOST_NAME; - ipv4 = IP_DEFAULT; - } - - jaegerResource = - Resource.builder() - .put(CLIENT_VERSION_KEY, CLIENT_VERSION_VALUE) - .put(IP_KEY, ipv4) - .put(HOSTNAME_KEY, hostname) - .build(); - } - - /** - * Submits all the given spans in a single batch to the Jaeger collector. - * - * @param spans the list of sampled Spans to be exported. - * @return the result of the operation - */ - @Override - public CompletableResultCode export(Collection spans) { - List results = new ArrayList<>(); - spans.stream() - .collect(Collectors.groupingBy(SpanData::getResource)) - .forEach( - (resource, spanData) -> - results.add(delegate.export(buildRequest(resource, spanData), spanData.size()))); - - return CompletableResultCode.ofAll(results); - } - - private PostSpansRequestMarshaler buildRequest(Resource resource, List spans) { - Resource mergedResource = jaegerResource.merge(resource); - return PostSpansRequestMarshaler.create(spans, mergedResource); - } - - /** - * The Jaeger exporter does not batch spans, so this method will immediately return with success. - * - * @return always Success - */ - @Override - public CompletableResultCode flush() { - return CompletableResultCode.ofSuccess(); - } - - /** - * Returns a new builder instance for this exporter. - * - * @return a new builder instance for this exporter. - */ - public static JaegerGrpcSpanExporterBuilder builder() { - return new JaegerGrpcSpanExporterBuilder(); - } - - /** - * Initiates an orderly shutdown in which preexisting calls continue but new calls are immediately - * cancelled. - */ - @Override - public CompletableResultCode shutdown() { - return delegate.shutdown(); - } - - // Visible for testing - Resource getJaegerResource() { - return jaegerResource; - } -} diff --git a/exporters/jaeger/src/main/java/io/opentelemetry/exporter/jaeger/JaegerGrpcSpanExporterBuilder.java b/exporters/jaeger/src/main/java/io/opentelemetry/exporter/jaeger/JaegerGrpcSpanExporterBuilder.java deleted file mode 100644 index 4255401ae00..00000000000 --- a/exporters/jaeger/src/main/java/io/opentelemetry/exporter/jaeger/JaegerGrpcSpanExporterBuilder.java +++ /dev/null @@ -1,160 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.exporter.jaeger; - -import static io.opentelemetry.api.internal.Utils.checkArgument; -import static java.util.Objects.requireNonNull; - -import io.grpc.ManagedChannel; -import io.opentelemetry.api.GlobalOpenTelemetry; -import io.opentelemetry.api.metrics.MeterProvider; -import io.opentelemetry.exporter.internal.grpc.GrpcExporterBuilder; -import java.net.URI; -import java.time.Duration; -import java.util.concurrent.TimeUnit; -import javax.net.ssl.SSLContext; -import javax.net.ssl.X509TrustManager; - -/** - * Builder utility for this exporter. - * - * @deprecated Use {@code OtlpGrpcSpanExporter} or {@code OtlpHttpSpanExporter} from opentelemetry-exporter-otlp - * instead. - */ -@Deprecated -public final class JaegerGrpcSpanExporterBuilder { - - private static final String GRPC_SERVICE_NAME = "jaeger.api_v2.CollectorService"; - - // Visible for testing - static final String GRPC_ENDPOINT_PATH = "/" + GRPC_SERVICE_NAME + "/PostSpans"; - - private static final String DEFAULT_ENDPOINT_URL = "http://localhost:14250"; - private static final URI DEFAULT_ENDPOINT = URI.create(DEFAULT_ENDPOINT_URL); - private static final long DEFAULT_TIMEOUT_SECS = 10; - - private final GrpcExporterBuilder delegate; - - JaegerGrpcSpanExporterBuilder() { - delegate = - new GrpcExporterBuilder<>( - "jaeger", - "span", - DEFAULT_TIMEOUT_SECS, - DEFAULT_ENDPOINT, - () -> MarshalerCollectorServiceGrpc::newFutureStub, - GRPC_ENDPOINT_PATH); - } - - /** - * Sets the managed channel to use when communicating with the backend. Takes precedence over - * {@link #setEndpoint(String)} if both are called. - * - * @param channel the channel to use. - * @return this. - * @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 JaegerGrpcSpanExporterBuilder setChannel(ManagedChannel channel) { - delegate.setChannel(channel); - return this; - } - - /** - * Sets the Jaeger endpoint to connect to. If unset, defaults to {@value DEFAULT_ENDPOINT_URL}. - * The endpoint must start with either http:// or https://. - */ - public JaegerGrpcSpanExporterBuilder setEndpoint(String endpoint) { - requireNonNull(endpoint, "endpoint"); - delegate.setEndpoint(endpoint); - return this; - } - - /** - * Sets the method used to compress payloads. If unset, compression is disabled. Currently - * supported compression methods include "gzip" and "none". - * - * @since 1.20.0 - */ - public JaegerGrpcSpanExporterBuilder setCompression(String compressionMethod) { - requireNonNull(compressionMethod, "compressionMethod"); - checkArgument( - compressionMethod.equals("gzip") || compressionMethod.equals("none"), - "Unsupported compression method. Supported compression methods include: gzip, none."); - delegate.setCompression(compressionMethod); - return this; - } - - /** - * Sets the maximum time to wait for the collector to process an exported batch of spans. If - * unset, defaults to {@value DEFAULT_TIMEOUT_SECS}s. - */ - public JaegerGrpcSpanExporterBuilder 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 spans. If - * unset, defaults to {@value DEFAULT_TIMEOUT_SECS}s. - */ - public JaegerGrpcSpanExporterBuilder setTimeout(Duration timeout) { - requireNonNull(timeout, "timeout"); - delegate.setTimeout(timeout); - 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 JaegerGrpcSpanExporterBuilder setTrustedCertificates(byte[] trustedCertificatesPem) { - delegate.setTrustManagerFromCerts(trustedCertificatesPem); - return this; - } - - /** Sets the client key and chain to use for verifying servers when mTLS is enabled. */ - public JaegerGrpcSpanExporterBuilder 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 JaegerGrpcSpanExporterBuilder setSslContext( - SSLContext sslContext, X509TrustManager trustManager) { - delegate.setSslContext(sslContext, trustManager); - return this; - } - - /** - * Sets the {@link MeterProvider} to use to collect metrics related to export. If not set, uses - * {@link GlobalOpenTelemetry#getMeterProvider()}. - * - * @since 1.15.0 - */ - public JaegerGrpcSpanExporterBuilder setMeterProvider(MeterProvider meterProvider) { - requireNonNull(meterProvider, "meterProvider"); - delegate.setMeterProvider(() -> meterProvider); - return this; - } - - /** - * Constructs a new instance of the exporter based on the builder's values. - * - * @return a new exporter's instance. - */ - public JaegerGrpcSpanExporter build() { - return new JaegerGrpcSpanExporter(delegate.build()); - } -} diff --git a/exporters/jaeger/src/main/java/io/opentelemetry/exporter/jaeger/KeyValueMarshaler.java b/exporters/jaeger/src/main/java/io/opentelemetry/exporter/jaeger/KeyValueMarshaler.java deleted file mode 100644 index 09f8b2f5734..00000000000 --- a/exporters/jaeger/src/main/java/io/opentelemetry/exporter/jaeger/KeyValueMarshaler.java +++ /dev/null @@ -1,132 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.exporter.jaeger; - -import com.fasterxml.jackson.jr.ob.JSON; -import io.opentelemetry.api.common.AttributeKey; -import io.opentelemetry.api.common.Attributes; -import io.opentelemetry.exporter.internal.marshal.MarshalerUtil; -import io.opentelemetry.exporter.internal.marshal.MarshalerWithSize; -import io.opentelemetry.exporter.internal.marshal.ProtoEnumInfo; -import io.opentelemetry.exporter.internal.marshal.Serializer; -import io.opentelemetry.exporter.jaeger.proto.api_v2.internal.KeyValue; -import io.opentelemetry.exporter.jaeger.proto.api_v2.internal.ValueType; -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; - -@SuppressWarnings({ - "checkstyle:LocalVariableName", - "checkstyle:MemberName", - "checkstyle:ParameterName", -}) -final class KeyValueMarshaler extends MarshalerWithSize { - - private static final byte[] EMPTY_BYTES = new byte[0]; - - private final byte[] keyUtf8; - private final ProtoEnumInfo valueType; - private final byte[] vStrUtf8; - private final boolean vBool; - private final long vInt64; - private final double vFloat64; - - static List createRepeated(Attributes attributes) { - if (attributes.isEmpty()) { - return new ArrayList<>(); - } - - List marshalers = new ArrayList<>(attributes.size()); - attributes.forEach((attributeKey, o) -> marshalers.add(create(attributeKey, o))); - return marshalers; - } - - static KeyValueMarshaler create(AttributeKey key, Object value) { - byte[] keyUtf8 = MarshalerUtil.toBytes(key.getKey()); - - // Default is the 0 value, string in this case - ProtoEnumInfo valueType = ValueType.STRING; - byte[] vStrUtf8 = EMPTY_BYTES; - boolean vBool = false; - long vInt64 = 0; - double vFloat64 = 0; - - switch (key.getType()) { - case STRING: - valueType = ValueType.STRING; - vStrUtf8 = MarshalerUtil.toBytes(((String) value)); - break; - case BOOLEAN: - valueType = ValueType.BOOL; - vBool = (boolean) value; - break; - case LONG: - valueType = ValueType.INT64; - vInt64 = (long) value; - break; - case DOUBLE: - valueType = ValueType.FLOAT64; - vFloat64 = (double) value; - break; - case STRING_ARRAY: - case BOOLEAN_ARRAY: - case LONG_ARRAY: - case DOUBLE_ARRAY: - valueType = ValueType.STRING; - try { - vStrUtf8 = JSON.std.asBytes(value); - } catch (IOException e) { - // Can't happen, just ignore it. - } - break; - } - - return new KeyValueMarshaler(keyUtf8, valueType, vStrUtf8, vBool, vInt64, vFloat64); - } - - KeyValueMarshaler( - byte[] keyUtf8, - ProtoEnumInfo valueType, - byte[] vStrUtf8, - boolean vBool, - long vInt64, - double vFloat64) { - super(calculateSize(keyUtf8, valueType, vStrUtf8, vBool, vInt64, vFloat64)); - this.keyUtf8 = keyUtf8; - this.valueType = valueType; - this.vStrUtf8 = vStrUtf8; - this.vBool = vBool; - this.vInt64 = vInt64; - this.vFloat64 = vFloat64; - } - - @Override - protected void writeTo(Serializer output) throws IOException { - output.serializeString(KeyValue.KEY, keyUtf8); - output.serializeEnum(KeyValue.V_TYPE, valueType); - output.serializeString(KeyValue.V_STR, vStrUtf8); - output.serializeBool(KeyValue.V_BOOL, vBool); - output.serializeInt64(KeyValue.V_INT64, vInt64); - output.serializeDouble(KeyValue.V_FLOAT64, vFloat64); - } - - private static int calculateSize( - byte[] keyUtf8, - ProtoEnumInfo valueType, - byte[] vStrUtf8, - boolean vBool, - long vInt64, - double vFloat64) { - int size = 0; - size += MarshalerUtil.sizeBytes(KeyValue.KEY, keyUtf8); - size += MarshalerUtil.sizeEnum(KeyValue.V_TYPE, valueType); - size += MarshalerUtil.sizeBytes(KeyValue.V_STR, vStrUtf8); - size += MarshalerUtil.sizeBool(KeyValue.V_BOOL, vBool); - size += MarshalerUtil.sizeInt64(KeyValue.V_INT64, vInt64); - size += MarshalerUtil.sizeDouble(KeyValue.V_FLOAT64, vFloat64); - return size; - } -} diff --git a/exporters/jaeger/src/main/java/io/opentelemetry/exporter/jaeger/LogMarshaler.java b/exporters/jaeger/src/main/java/io/opentelemetry/exporter/jaeger/LogMarshaler.java deleted file mode 100644 index be871489f0b..00000000000 --- a/exporters/jaeger/src/main/java/io/opentelemetry/exporter/jaeger/LogMarshaler.java +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.exporter.jaeger; - -import io.opentelemetry.api.common.AttributeKey; -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.jaeger.proto.api_v2.internal.Log; -import io.opentelemetry.sdk.trace.data.EventData; -import java.io.IOException; -import java.util.List; - -final class LogMarshaler extends MarshalerWithSize { - - private static final AttributeKey KEY_LOG_EVENT = AttributeKey.stringKey("event"); - private static final AttributeKey KEY_EVENT_DROPPED_ATTRIBUTES_COUNT = - AttributeKey.longKey("otel.event.dropped_attributes_count"); - - private final TimeMarshaler timestamp; - private final List fields; - - static LogMarshaler[] createRepeated(List events) { - int len = events.size(); - LogMarshaler[] marshalers = new LogMarshaler[len]; - for (int i = 0; i < len; i++) { - marshalers[i] = create(events.get(i)); - } - return marshalers; - } - - static LogMarshaler create(EventData event) { - TimeMarshaler timestamp = TimeMarshaler.create(event.getEpochNanos()); - - List fields = KeyValueMarshaler.createRepeated(event.getAttributes()); - - // name is a top-level property in OpenTelemetry - fields.add(KeyValueMarshaler.create(KEY_LOG_EVENT, event.getName())); - - int droppedAttributesCount = event.getDroppedAttributesCount(); - if (droppedAttributesCount > 0) { - fields.add( - KeyValueMarshaler.create( - KEY_EVENT_DROPPED_ATTRIBUTES_COUNT, (long) droppedAttributesCount)); - } - - return new LogMarshaler(timestamp, fields); - } - - LogMarshaler(TimeMarshaler timestamp, List fields) { - super(calculateSize(timestamp, fields)); - this.timestamp = timestamp; - this.fields = fields; - } - - @Override - protected void writeTo(Serializer output) throws IOException { - output.serializeMessage(Log.TIMESTAMP, timestamp); - output.serializeRepeatedMessage(Log.FIELDS, fields); - } - - private static int calculateSize(TimeMarshaler timestamp, List fields) { - int size = 0; - size += MarshalerUtil.sizeMessage(Log.TIMESTAMP, timestamp); - size += MarshalerUtil.sizeRepeatedMessage(Log.FIELDS, fields); - return size; - } -} diff --git a/exporters/jaeger/src/main/java/io/opentelemetry/exporter/jaeger/MarshalerCollectorServiceGrpc.java b/exporters/jaeger/src/main/java/io/opentelemetry/exporter/jaeger/MarshalerCollectorServiceGrpc.java deleted file mode 100644 index 7eb71fb71d1..00000000000 --- a/exporters/jaeger/src/main/java/io/opentelemetry/exporter/jaeger/MarshalerCollectorServiceGrpc.java +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.exporter.jaeger; - -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 java.io.InputStream; -import javax.annotation.Nullable; - -// Adapted from the protoc generated code for CollectorServiceGrpc. -final class MarshalerCollectorServiceGrpc { - - private static final String SERVICE_NAME = "jaeger.api_v2.CollectorService"; - - private static final MethodDescriptor.Marshaller REQUEST_MARSHALLER = - new MethodDescriptor.Marshaller() { - @Override - public InputStream stream(PostSpansRequestMarshaler value) { - return new MarshalerInputStream(value); - } - - @Override - public PostSpansRequestMarshaler parse(InputStream stream) { - throw new UnsupportedOperationException("Only for serializing"); - } - }; - - private static final MethodDescriptor.Marshaller RESPONSE_MARSHALER = - new MethodDescriptor.Marshaller() { - @Override - public InputStream stream(PostSpansResponse value) { - throw new UnsupportedOperationException("Only for parsing"); - } - - @Override - public PostSpansResponse parse(InputStream stream) { - return PostSpansResponse.INSTANCE; - } - }; - - private static final MethodDescriptor - getPostSpansMethod = - MethodDescriptor.newBuilder() - .setType(MethodDescriptor.MethodType.UNARY) - .setFullMethodName(generateFullMethodName(SERVICE_NAME, "PostSpans")) - .setRequestMarshaller(REQUEST_MARSHALLER) - .setResponseMarshaller(RESPONSE_MARSHALER) - .build(); - - static CollectorServiceFutureStub newFutureStub( - Channel channel, @Nullable String authorityOverride) { - return CollectorServiceFutureStub.newStub( - (c, options) -> new CollectorServiceFutureStub(c, options.withAuthority(authorityOverride)), - channel); - } - - static final class CollectorServiceFutureStub - extends MarshalerServiceStub< - PostSpansRequestMarshaler, PostSpansResponse, CollectorServiceFutureStub> { - private CollectorServiceFutureStub(Channel channel, CallOptions callOptions) { - super(channel, callOptions); - } - - @Override - protected MarshalerCollectorServiceGrpc.CollectorServiceFutureStub build( - Channel channel, CallOptions callOptions) { - return new MarshalerCollectorServiceGrpc.CollectorServiceFutureStub(channel, callOptions); - } - - @Override - public ListenableFuture export(PostSpansRequestMarshaler request) { - return ClientCalls.futureUnaryCall( - getChannel().newCall(getPostSpansMethod, getCallOptions()), request); - } - } - - private MarshalerCollectorServiceGrpc() {} -} diff --git a/exporters/jaeger/src/main/java/io/opentelemetry/exporter/jaeger/PostSpansRequestMarshaler.java b/exporters/jaeger/src/main/java/io/opentelemetry/exporter/jaeger/PostSpansRequestMarshaler.java deleted file mode 100644 index 69347f82c80..00000000000 --- a/exporters/jaeger/src/main/java/io/opentelemetry/exporter/jaeger/PostSpansRequestMarshaler.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.exporter.jaeger; - -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.jaeger.proto.api_v2.internal.PostSpansRequest; -import io.opentelemetry.sdk.resources.Resource; -import io.opentelemetry.sdk.trace.data.SpanData; -import java.io.IOException; -import java.util.List; - -final class PostSpansRequestMarshaler extends MarshalerWithSize { - - private final BatchMarshaler batch; - - static PostSpansRequestMarshaler create(List spans, Resource resource) { - return new PostSpansRequestMarshaler(BatchMarshaler.create(spans, resource)); - } - - PostSpansRequestMarshaler(BatchMarshaler batch) { - super(calculateSize(batch)); - this.batch = batch; - } - - @Override - protected void writeTo(Serializer output) throws IOException { - output.serializeMessage(PostSpansRequest.BATCH, batch); - } - - private static int calculateSize(BatchMarshaler batch) { - int size = 0; - size += MarshalerUtil.sizeMessage(PostSpansRequest.BATCH, batch); - return size; - } -} diff --git a/exporters/jaeger/src/main/java/io/opentelemetry/exporter/jaeger/PostSpansResponse.java b/exporters/jaeger/src/main/java/io/opentelemetry/exporter/jaeger/PostSpansResponse.java deleted file mode 100644 index 3daa41ce560..00000000000 --- a/exporters/jaeger/src/main/java/io/opentelemetry/exporter/jaeger/PostSpansResponse.java +++ /dev/null @@ -1,15 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.exporter.jaeger; - -// A Java object to correspond to the gRPC response for the Collector.PostSpans 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 PostSpansResponse { - INSTANCE; -} diff --git a/exporters/jaeger/src/main/java/io/opentelemetry/exporter/jaeger/ProcessMarshaler.java b/exporters/jaeger/src/main/java/io/opentelemetry/exporter/jaeger/ProcessMarshaler.java deleted file mode 100644 index e50027a894c..00000000000 --- a/exporters/jaeger/src/main/java/io/opentelemetry/exporter/jaeger/ProcessMarshaler.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.exporter.jaeger; - -import io.opentelemetry.api.common.AttributeKey; -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.jaeger.proto.api_v2.internal.Process; -import io.opentelemetry.sdk.resources.Resource; -import java.io.IOException; -import java.util.List; - -final class ProcessMarshaler extends MarshalerWithSize { - - private static final AttributeKey SERVICE_NAME = AttributeKey.stringKey("service.name"); - - private final byte[] serviceNameUtf8; - private final List tags; - - static ProcessMarshaler create(Resource resource) { - String serviceName = resource.getAttribute(SERVICE_NAME); - if (serviceName == null || serviceName.isEmpty()) { - serviceName = Resource.getDefault().getAttribute(SERVICE_NAME); - } - - return new ProcessMarshaler( - MarshalerUtil.toBytes(serviceName), - KeyValueMarshaler.createRepeated(resource.getAttributes())); - } - - ProcessMarshaler(byte[] serviceNameUtf8, List tags) { - super(calculateSize(serviceNameUtf8, tags)); - this.serviceNameUtf8 = serviceNameUtf8; - this.tags = tags; - } - - @Override - protected void writeTo(Serializer output) throws IOException { - output.serializeString(Process.SERVICE_NAME, serviceNameUtf8); - output.serializeRepeatedMessage(Process.TAGS, tags); - } - - private static int calculateSize(byte[] serviceNameUtf8, List tags) { - int size = 0; - size += MarshalerUtil.sizeBytes(Process.SERVICE_NAME, serviceNameUtf8); - size += MarshalerUtil.sizeRepeatedMessage(Process.TAGS, tags); - return size; - } -} diff --git a/exporters/jaeger/src/main/java/io/opentelemetry/exporter/jaeger/SpanMarshaler.java b/exporters/jaeger/src/main/java/io/opentelemetry/exporter/jaeger/SpanMarshaler.java deleted file mode 100644 index 40f2457eecf..00000000000 --- a/exporters/jaeger/src/main/java/io/opentelemetry/exporter/jaeger/SpanMarshaler.java +++ /dev/null @@ -1,186 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.exporter.jaeger; - -import static io.opentelemetry.api.common.AttributeKey.booleanKey; - -import io.opentelemetry.api.common.AttributeKey; -import io.opentelemetry.api.trace.SpanContext; -import io.opentelemetry.api.trace.SpanKind; -import io.opentelemetry.api.trace.StatusCode; -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.jaeger.proto.api_v2.internal.Span; -import io.opentelemetry.sdk.trace.data.SpanData; -import java.io.IOException; -import java.util.List; -import java.util.Locale; - -final class SpanMarshaler extends MarshalerWithSize { - - private static final AttributeKey KEY_ERROR = booleanKey("error"); - private static final AttributeKey KEY_DROPPED_ATTRIBUTES_COUNT = - AttributeKey.longKey("otel.dropped_attributes_count"); - private static final AttributeKey KEY_DROPPED_EVENTS_COUNT = - AttributeKey.longKey("otel.dropped_events_count"); - private static final AttributeKey KEY_SPAN_KIND = AttributeKey.stringKey("span.kind"); - private static final AttributeKey KEY_SPAN_STATUS_MESSAGE = - AttributeKey.stringKey("otel.status_description"); - private static final AttributeKey KEY_SPAN_STATUS_CODE = - AttributeKey.stringKey("otel.status_code"); - private static final AttributeKey KEY_INSTRUMENTATION_SCOPE_NAME = - AttributeKey.stringKey("otel.scope.name"); - private static final AttributeKey KEY_INSTRUMENTATION_SCOPE_VERSION = - AttributeKey.stringKey("otel.scope.version"); - private static final AttributeKey KEY_INSTRUMENTATION_LIBRARY_NAME = - AttributeKey.stringKey("otel.library.name"); - private static final AttributeKey KEY_INSTRUMENTATION_LIBRARY_VERSION = - AttributeKey.stringKey("otel.library.version"); - - private final String traceId; - private final String spanId; - private final byte[] operationNameUtf8; - private final TimeMarshaler startTime; - private final TimeMarshaler duration; - private final List tags; - private final LogMarshaler[] logs; - private final List references; - - static SpanMarshaler[] createRepeated(List spans) { - int len = spans.size(); - SpanMarshaler[] marshalers = new SpanMarshaler[len]; - for (int i = 0; i < len; i++) { - marshalers[i] = SpanMarshaler.create(spans.get(i)); - } - return marshalers; - } - - static SpanMarshaler create(SpanData span) { - String traceId = span.getSpanContext().getTraceId(); - String spanId = span.getSpanContext().getSpanId(); - byte[] operationNameUtf8 = MarshalerUtil.toBytes(span.getName()); - TimeMarshaler startTime = TimeMarshaler.create(span.getStartEpochNanos()); - TimeMarshaler duration = - TimeMarshaler.create(span.getEndEpochNanos() - span.getStartEpochNanos()); - - List tags = KeyValueMarshaler.createRepeated(span.getAttributes()); - int droppedAttributes = span.getTotalAttributeCount() - span.getAttributes().size(); - if (droppedAttributes > 0) { - tags.add(KeyValueMarshaler.create(KEY_DROPPED_ATTRIBUTES_COUNT, (long) droppedAttributes)); - } - - LogMarshaler[] logs = LogMarshaler.createRepeated(span.getEvents()); - int droppedEvents = span.getTotalRecordedEvents() - span.getEvents().size(); - if (droppedEvents > 0) { - tags.add(KeyValueMarshaler.create(KEY_DROPPED_EVENTS_COUNT, (long) droppedEvents)); - } - - List references = SpanRefMarshaler.createRepeated(span.getLinks()); - - // add the parent span - SpanContext parentSpanContext = span.getParentSpanContext(); - if (parentSpanContext.isValid()) { - references.add(SpanRefMarshaler.create(parentSpanContext)); - } - - if (span.getKind() != SpanKind.INTERNAL) { - tags.add( - KeyValueMarshaler.create(KEY_SPAN_KIND, span.getKind().name().toLowerCase(Locale.ROOT))); - } - - if (!span.getStatus().getDescription().isEmpty()) { - tags.add( - KeyValueMarshaler.create(KEY_SPAN_STATUS_MESSAGE, span.getStatus().getDescription())); - } - - if (span.getStatus().getStatusCode() != StatusCode.UNSET) { - tags.add( - KeyValueMarshaler.create(KEY_SPAN_STATUS_CODE, span.getStatus().getStatusCode().name())); - } - - tags.add( - KeyValueMarshaler.create( - KEY_INSTRUMENTATION_SCOPE_NAME, span.getInstrumentationScopeInfo().getName())); - // Include instrumentation library name for backwards compatibility - tags.add( - KeyValueMarshaler.create( - KEY_INSTRUMENTATION_LIBRARY_NAME, span.getInstrumentationScopeInfo().getName())); - - if (span.getInstrumentationScopeInfo().getVersion() != null) { - tags.add( - KeyValueMarshaler.create( - KEY_INSTRUMENTATION_SCOPE_VERSION, span.getInstrumentationScopeInfo().getVersion())); - // Include instrumentation library name for backwards compatibility - tags.add( - KeyValueMarshaler.create( - KEY_INSTRUMENTATION_LIBRARY_VERSION, - span.getInstrumentationScopeInfo().getVersion())); - } - - if (span.getStatus().getStatusCode() == StatusCode.ERROR) { - tags.add(KeyValueMarshaler.create(KEY_ERROR, true)); - } - - return new SpanMarshaler( - traceId, spanId, operationNameUtf8, startTime, duration, tags, logs, references); - } - - SpanMarshaler( - String traceId, - String spanId, - byte[] operationNameUtf8, - TimeMarshaler startTime, - TimeMarshaler duration, - List tags, - LogMarshaler[] logs, - List references) { - super( - calculateSize( - traceId, spanId, operationNameUtf8, startTime, duration, tags, logs, references)); - this.traceId = traceId; - this.spanId = spanId; - this.operationNameUtf8 = operationNameUtf8; - this.startTime = startTime; - this.duration = duration; - this.tags = tags; - this.logs = logs; - this.references = references; - } - - @Override - protected void writeTo(Serializer output) throws IOException { - output.serializeTraceId(Span.TRACE_ID, traceId); - output.serializeSpanId(Span.SPAN_ID, spanId); - output.serializeString(Span.OPERATION_NAME, operationNameUtf8); - output.serializeMessage(Span.START_TIME, startTime); - output.serializeMessage(Span.DURATION, duration); - output.serializeRepeatedMessage(Span.TAGS, tags); - output.serializeRepeatedMessage(Span.LOGS, logs); - output.serializeRepeatedMessage(Span.REFERENCES, references); - } - - private static int calculateSize( - String traceId, - String spanId, - byte[] operationNameUtf8, - TimeMarshaler startTime, - TimeMarshaler duration, - List tags, - LogMarshaler[] logs, - List references) { - int size = 0; - size += MarshalerUtil.sizeTraceId(Span.TRACE_ID, traceId); - size += MarshalerUtil.sizeSpanId(Span.SPAN_ID, spanId); - size += MarshalerUtil.sizeBytes(Span.OPERATION_NAME, operationNameUtf8); - size += MarshalerUtil.sizeMessage(Span.START_TIME, startTime); - size += MarshalerUtil.sizeMessage(Span.DURATION, duration); - size += MarshalerUtil.sizeRepeatedMessage(Span.TAGS, tags); - size += MarshalerUtil.sizeRepeatedMessage(Span.LOGS, logs); - size += MarshalerUtil.sizeRepeatedMessage(Span.REFERENCES, references); - return size; - } -} diff --git a/exporters/jaeger/src/main/java/io/opentelemetry/exporter/jaeger/SpanRefMarshaler.java b/exporters/jaeger/src/main/java/io/opentelemetry/exporter/jaeger/SpanRefMarshaler.java deleted file mode 100644 index a6dd61a418a..00000000000 --- a/exporters/jaeger/src/main/java/io/opentelemetry/exporter/jaeger/SpanRefMarshaler.java +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.exporter.jaeger; - -import io.opentelemetry.api.trace.SpanContext; -import io.opentelemetry.exporter.internal.marshal.MarshalerUtil; -import io.opentelemetry.exporter.internal.marshal.MarshalerWithSize; -import io.opentelemetry.exporter.internal.marshal.ProtoEnumInfo; -import io.opentelemetry.exporter.internal.marshal.Serializer; -import io.opentelemetry.exporter.jaeger.proto.api_v2.internal.SpanRef; -import io.opentelemetry.exporter.jaeger.proto.api_v2.internal.SpanRefType; -import io.opentelemetry.sdk.trace.data.LinkData; -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; - -final class SpanRefMarshaler extends MarshalerWithSize { - - private final String traceId; - private final String spanId; - private final ProtoEnumInfo refType; - - static List createRepeated(List links) { - List marshalers = new ArrayList<>(links.size()); - for (LinkData link : links) { - // we can assume that all links are *follows from* - // https://github.com/open-telemetry/opentelemetry-java/issues/475 - // https://github.com/open-telemetry/opentelemetry-java/pull/481/files#r312577862 - marshalers.add(create(link)); - ; - } - return marshalers; - } - - static SpanRefMarshaler create(SpanContext spanContext) { - return new SpanRefMarshaler( - spanContext.getTraceId(), spanContext.getSpanId(), SpanRefType.CHILD_OF); - } - - static SpanRefMarshaler create(LinkData link) { - return new SpanRefMarshaler( - link.getSpanContext().getTraceId(), - link.getSpanContext().getSpanId(), - SpanRefType.FOLLOWS_FROM); - } - - SpanRefMarshaler(String traceId, String spanId, ProtoEnumInfo refType) { - super(calculateSize(traceId, spanId, refType)); - this.traceId = traceId; - this.spanId = spanId; - this.refType = refType; - } - - @Override - protected void writeTo(Serializer output) throws IOException { - output.serializeTraceId(SpanRef.TRACE_ID, traceId); - output.serializeSpanId(SpanRef.SPAN_ID, spanId); - output.serializeEnum(SpanRef.REF_TYPE, refType); - } - - private static int calculateSize(String traceId, String spanId, ProtoEnumInfo refType) { - int size = 0; - size += MarshalerUtil.sizeTraceId(SpanRef.TRACE_ID, traceId); - size += MarshalerUtil.sizeSpanId(SpanRef.SPAN_ID, spanId); - size += MarshalerUtil.sizeEnum(SpanRef.REF_TYPE, refType); - return size; - } -} diff --git a/exporters/jaeger/src/main/java/io/opentelemetry/exporter/jaeger/TimeMarshaler.java b/exporters/jaeger/src/main/java/io/opentelemetry/exporter/jaeger/TimeMarshaler.java deleted file mode 100644 index 961e5461133..00000000000 --- a/exporters/jaeger/src/main/java/io/opentelemetry/exporter/jaeger/TimeMarshaler.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.exporter.jaeger; - -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.jaeger.internal.protobuf.internal.Time; -import java.io.IOException; -import java.util.concurrent.TimeUnit; - -// The wire format for Timestamp and Duration are exactly the same. Just implement one Marshaler -// for them. -final class TimeMarshaler extends MarshalerWithSize { - private static final long NANOS_PER_SECOND = TimeUnit.SECONDS.toNanos(1); - - private final long seconds; - private final int nanos; - - static TimeMarshaler create(long timeNanos) { - long seconds = timeNanos / NANOS_PER_SECOND; - int nanos = (int) (timeNanos % NANOS_PER_SECOND); - return new TimeMarshaler(seconds, nanos); - } - - TimeMarshaler(long seconds, int nanos) { - super(calculateSize(seconds, nanos)); - this.seconds = seconds; - this.nanos = nanos; - } - - @Override - protected void writeTo(Serializer output) throws IOException { - output.serializeInt64(Time.SECONDS, seconds); - output.serializeInt32(Time.NANOS, nanos); - } - - private static int calculateSize(long seconds, int nanos) { - int size = 0; - size += MarshalerUtil.sizeInt64(Time.SECONDS, seconds); - size += MarshalerUtil.sizeInt32(Time.NANOS, nanos); - return size; - } -} diff --git a/exporters/jaeger/src/main/java/io/opentelemetry/exporter/jaeger/internal/JaegerGrpcSpanExporterProvider.java b/exporters/jaeger/src/main/java/io/opentelemetry/exporter/jaeger/internal/JaegerGrpcSpanExporterProvider.java deleted file mode 100644 index bf3ba40b477..00000000000 --- a/exporters/jaeger/src/main/java/io/opentelemetry/exporter/jaeger/internal/JaegerGrpcSpanExporterProvider.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.exporter.jaeger.internal; - -import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; -import io.opentelemetry.sdk.autoconfigure.spi.traces.ConfigurableSpanExporterProvider; -import io.opentelemetry.sdk.trace.export.SpanExporter; -import java.time.Duration; - -/** - * {@link SpanExporter} SPI implementation for {@link - * io.opentelemetry.exporter.jaeger.JaegerGrpcSpanExporter}. - * - *

This class is internal and is hence not for public use. Its APIs are unstable and can change - * at any time. - * - * @deprecated Use {@code OtlpGrpcSpanExporter} or {@code OtlpHttpSpanExporter} from opentelemetry-exporter-otlp - * instead. - */ -@Deprecated -public class JaegerGrpcSpanExporterProvider implements ConfigurableSpanExporterProvider { - @Override - public String getName() { - return "jaeger"; - } - - @Override - public SpanExporter createExporter(ConfigProperties config) { - io.opentelemetry.exporter.jaeger.JaegerGrpcSpanExporterBuilder builder = - io.opentelemetry.exporter.jaeger.JaegerGrpcSpanExporter.builder(); - - String endpoint = config.getString("otel.exporter.jaeger.endpoint"); - if (endpoint != null) { - builder.setEndpoint(endpoint); - } - - Duration timeout = config.getDuration("otel.exporter.jaeger.timeout"); - if (timeout != null) { - builder.setTimeout(timeout); - } - - return builder.build(); - } -} diff --git a/exporters/jaeger/src/main/java/io/opentelemetry/exporter/jaeger/package-info.java b/exporters/jaeger/src/main/java/io/opentelemetry/exporter/jaeger/package-info.java deleted file mode 100644 index 23e486e2fd8..00000000000 --- a/exporters/jaeger/src/main/java/io/opentelemetry/exporter/jaeger/package-info.java +++ /dev/null @@ -1,9 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -@ParametersAreNonnullByDefault -package io.opentelemetry.exporter.jaeger; - -import javax.annotation.ParametersAreNonnullByDefault; diff --git a/exporters/jaeger/src/main/proto/README.md b/exporters/jaeger/src/main/proto/README.md deleted file mode 100644 index f36259bdd21..00000000000 --- a/exporters/jaeger/src/main/proto/README.md +++ /dev/null @@ -1 +0,0 @@ -Non-empty folder required for wire proto compiler. diff --git a/exporters/jaeger/src/main/resources/META-INF/services/io.opentelemetry.sdk.autoconfigure.spi.traces.ConfigurableSpanExporterProvider b/exporters/jaeger/src/main/resources/META-INF/services/io.opentelemetry.sdk.autoconfigure.spi.traces.ConfigurableSpanExporterProvider deleted file mode 100644 index 4c94f1676f8..00000000000 --- a/exporters/jaeger/src/main/resources/META-INF/services/io.opentelemetry.sdk.autoconfigure.spi.traces.ConfigurableSpanExporterProvider +++ /dev/null @@ -1 +0,0 @@ -io.opentelemetry.exporter.jaeger.internal.JaegerGrpcSpanExporterProvider diff --git a/exporters/jaeger/src/test/java/io/opentelemetry/exporter/jaeger/JaegerGrpcSpanExporterTest.java b/exporters/jaeger/src/test/java/io/opentelemetry/exporter/jaeger/JaegerGrpcSpanExporterTest.java deleted file mode 100644 index 994998d6176..00000000000 --- a/exporters/jaeger/src/test/java/io/opentelemetry/exporter/jaeger/JaegerGrpcSpanExporterTest.java +++ /dev/null @@ -1,422 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.exporter.jaeger; - -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 static org.assertj.core.api.Assertions.assertThatThrownBy; -import static org.assertj.core.api.Assertions.fail; - -import com.google.protobuf.InvalidProtocolBufferException; -import com.linecorp.armeria.server.ServerBuilder; -import com.linecorp.armeria.server.ServiceRequestContext; -import com.linecorp.armeria.server.grpc.protocol.AbstractUnaryGrpcService; -import com.linecorp.armeria.testing.junit5.server.SelfSignedCertificateExtension; -import com.linecorp.armeria.testing.junit5.server.ServerExtension; -import io.github.netmikey.logunit.api.LogCapturer; -import io.opentelemetry.api.common.Attributes; -import io.opentelemetry.api.metrics.MeterProvider; -import io.opentelemetry.api.trace.SpanContext; -import io.opentelemetry.api.trace.SpanId; -import io.opentelemetry.api.trace.SpanKind; -import io.opentelemetry.api.trace.TraceFlags; -import io.opentelemetry.api.trace.TraceId; -import io.opentelemetry.api.trace.TraceState; -import io.opentelemetry.exporter.internal.TlsUtil; -import io.opentelemetry.exporter.internal.grpc.GrpcExporter; -import io.opentelemetry.exporter.jaeger.proto.api_v2.Collector; -import io.opentelemetry.exporter.jaeger.proto.api_v2.Model; -import io.opentelemetry.internal.testing.slf4j.SuppressLogger; -import io.opentelemetry.sdk.common.CompletableResultCode; -import io.opentelemetry.sdk.common.InstrumentationScopeInfo; -import io.opentelemetry.sdk.resources.Resource; -import io.opentelemetry.sdk.testing.trace.TestSpanData; -import io.opentelemetry.sdk.trace.IdGenerator; -import io.opentelemetry.sdk.trace.data.SpanData; -import io.opentelemetry.sdk.trace.data.StatusData; -import java.net.InetAddress; -import java.net.URISyntaxException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import java.util.Optional; -import java.util.concurrent.BlockingQueue; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.CompletionStage; -import java.util.concurrent.LinkedBlockingDeque; -import java.util.concurrent.TimeUnit; -import javax.net.ssl.KeyManager; -import javax.net.ssl.SSLContext; -import javax.net.ssl.TrustManager; -import javax.net.ssl.X509KeyManager; -import javax.net.ssl.X509TrustManager; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.RegisterExtension; - -@SuppressWarnings("deprecation") // Testing deprecated code -class JaegerGrpcSpanExporterTest { - private static final BlockingQueue postedRequests = - new LinkedBlockingDeque<>(); - - @RegisterExtension - static final ServerExtension server = - new ServerExtension() { - @Override - protected void configure(ServerBuilder sb) { - sb.service( - JaegerGrpcSpanExporterBuilder.GRPC_ENDPOINT_PATH, - new AbstractUnaryGrpcService() { - @Override - protected CompletionStage handleMessage( - ServiceRequestContext ctx, byte[] message) { - try { - postedRequests.add(Collector.PostSpansRequest.parseFrom(message)); - } catch (InvalidProtocolBufferException e) { - CompletableFuture future = new CompletableFuture<>(); - future.completeExceptionally(e); - return future; - } - return CompletableFuture.completedFuture( - Collector.PostSpansResponse.getDefaultInstance().toByteArray()); - } - }); - } - }; - - @RegisterExtension LogCapturer logs = LogCapturer.create().captureForType(GrpcExporter.class); - - @RegisterExtension - static final SelfSignedCertificateExtension serverTls = new SelfSignedCertificateExtension(); - - @RegisterExtension - static final SelfSignedCertificateExtension clientTls = new SelfSignedCertificateExtension(); - - private JaegerGrpcSpanExporter exporter; - - @BeforeEach - void setUp() { - exporter = - JaegerGrpcSpanExporter.builder() - .setEndpoint(server.httpUri().toString()) - .setMeterProvider(MeterProvider.noop()) - .build(); - } - - @AfterEach - void tearDown() { - exporter.shutdown(); - postedRequests.clear(); - } - - @Test - void testExport() throws Exception { - SpanData span = - testSpanData( - Resource.create( - Attributes.of( - stringKey("service.name"), - "myServiceName", - stringKey("resource-attr-key"), - "resource-attr-value")), - "GET /api/endpoint"); - - // test - CompletableResultCode result = exporter.export(Collections.singletonList(span)); - result.join(10, TimeUnit.SECONDS); - assertThat(result.isSuccess()).isEqualTo(true); - - // verify - assertThat(postedRequests).hasSize(1); - Model.Batch batch = postedRequests.poll().getBatch(); - assertThat(batch.getSpans(0).getOperationName()).isEqualTo("GET /api/endpoint"); - assertThat(SpanId.fromBytes(batch.getSpans(0).getSpanId().toByteArray())) - .isEqualTo(span.getSpanContext().getSpanId()); - - assertThat( - getTagValue(batch.getProcess().getTagsList(), "resource-attr-key") - .orElseThrow(() -> new AssertionError("resource-attr-key not found")) - .getVStr()) - .isEqualTo("resource-attr-value"); - - verifyBatch(batch); - assertThat(batch.getProcess().getServiceName()).isEqualTo("myServiceName"); - } - - @Test - void testExportMultipleResources() throws Exception { - SpanData span = - testSpanData( - Resource.create( - Attributes.of( - stringKey("service.name"), - "myServiceName1", - stringKey("resource-attr-key-1"), - "resource-attr-value-1")), - "GET /api/endpoint/1"); - - SpanData span2 = - testSpanData( - Resource.create( - Attributes.of( - stringKey("service.name"), - "myServiceName2", - stringKey("resource-attr-key-2"), - "resource-attr-value-2")), - "GET /api/endpoint/2"); - - // test - CompletableResultCode result = exporter.export(Arrays.asList(span, span2)); - result.join(10, TimeUnit.SECONDS); - assertThat(result.isSuccess()).isEqualTo(true); - - // verify - assertThat(postedRequests).hasSize(2); - List requests = new ArrayList<>(postedRequests); - assertThat(requests).hasSize(2); - for (Collector.PostSpansRequest request : requests) { - Model.Batch batch = request.getBatch(); - - verifyBatch(batch); - - Optional processTag = - getTagValue(batch.getProcess().getTagsList(), "resource-attr-key-1"); - Optional processTag2 = - getTagValue(batch.getProcess().getTagsList(), "resource-attr-key-2"); - if (processTag.isPresent()) { - assertThat(processTag2.isPresent()).isFalse(); - assertThat(batch.getSpans(0).getOperationName()).isEqualTo("GET /api/endpoint/1"); - assertThat(SpanId.fromBytes(batch.getSpans(0).getSpanId().toByteArray())) - .isEqualTo(span.getSpanContext().getSpanId()); - assertThat(processTag.get().getVStr()).isEqualTo("resource-attr-value-1"); - assertThat(batch.getProcess().getServiceName()).isEqualTo("myServiceName1"); - } else if (processTag2.isPresent()) { - assertThat(batch.getSpans(0).getOperationName()).isEqualTo("GET /api/endpoint/2"); - assertThat(SpanId.fromBytes(batch.getSpans(0).getSpanId().toByteArray())) - .isEqualTo(span2.getSpanContext().getSpanId()); - assertThat(processTag2.get().getVStr()).isEqualTo("resource-attr-value-2"); - assertThat(batch.getProcess().getServiceName()).isEqualTo("myServiceName2"); - } else { - fail("No process tag resource-attr-key-1 or resource-attr-key-2"); - } - } - } - - private void verifyBatch(Model.Batch batch) throws Exception { - assertThat(batch.getSpansCount()).isEqualTo(1); - assertThat(TraceId.fromBytes(batch.getSpans(0).getTraceId().toByteArray())).isNotNull(); - assertThat(batch.getProcess().getTagsCount()).isEqualTo(5); - - assertThat( - getSpanTagValue(batch.getSpans(0), "otel.scope.name") - .orElseThrow(() -> new AssertionError("otel.scope.name not found")) - .getVStr()) - .isEqualTo("io.opentelemetry.auto"); - - assertThat( - getSpanTagValue(batch.getSpans(0), "otel.library.name") - .orElseThrow(() -> new AssertionError("otel.library.name not found")) - .getVStr()) - .isEqualTo("io.opentelemetry.auto"); - - assertThat( - getSpanTagValue(batch.getSpans(0), "otel.library.version") - .orElseThrow(() -> new AssertionError("otel.library.version not found")) - .getVStr()) - .isEqualTo("1.0.0"); - - assertThat( - getSpanTagValue(batch.getSpans(0), "otel.scope.version") - .orElseThrow(() -> new AssertionError("otel.scope.version not found")) - .getVStr()) - .isEqualTo("1.0.0"); - - assertThat( - getTagValue(batch.getProcess().getTagsList(), "ip") - .orElseThrow(() -> new AssertionError("ip not found")) - .getVStr()) - .isEqualTo(exporter.getJaegerResource().getAttribute(JaegerGrpcSpanExporter.IP_KEY)); - - assertThat( - getTagValue(batch.getProcess().getTagsList(), "hostname") - .orElseThrow(() -> new AssertionError("hostname not found")) - .getVStr()) - .isEqualTo(InetAddress.getLocalHost().getHostName()); - - assertThat( - getTagValue(batch.getProcess().getTagsList(), "jaeger.version") - .orElseThrow(() -> new AssertionError("jaeger.version not found")) - .getVStr()) - .isEqualTo("opentelemetry-java"); - } - - private static Optional getSpanTagValue(Model.Span span, String tagKey) { - return getTagValue(span.getTagsList(), tagKey); - } - - private static Optional getTagValue(List tags, String tagKey) { - return tags.stream().filter(kv -> kv.getKey().equals(tagKey)).findFirst(); - } - - private static SpanData testSpanData(Resource resource, String spanName) { - long duration = 900; // ms - long startMs = System.currentTimeMillis(); - long endMs = startMs + duration; - return TestSpanData.builder() - .setHasEnded(true) - .setSpanContext( - SpanContext.create( - IdGenerator.random().generateTraceId(), - IdGenerator.random().generateSpanId(), - TraceFlags.getSampled(), - TraceState.getDefault())) - .setName(spanName) - .setStartEpochNanos(TimeUnit.MILLISECONDS.toNanos(startMs)) - .setEndEpochNanos(TimeUnit.MILLISECONDS.toNanos(endMs)) - .setStatus(StatusData.ok()) - .setKind(SpanKind.CONSUMER) - .setLinks(Collections.emptyList()) - .setTotalRecordedLinks(0) - .setTotalRecordedEvents(0) - .setInstrumentationScopeInfo( - InstrumentationScopeInfo.builder("io.opentelemetry.auto").setVersion("1.0.0").build()) - .setResource(resource) - .build(); - } - - @Test - void validTrustedConfig() throws Exception { - assertThatCode( - () -> - JaegerGrpcSpanExporter.builder() - .setTrustedCertificates(serverTls.certificate().getEncoded())) - .doesNotThrowAnyException(); - } - - @Test - void validClientKeyConfig() throws Exception { - assertThatCode( - () -> - JaegerGrpcSpanExporter.builder() - .setClientTls( - clientTls.privateKey().getEncoded(), serverTls.certificate().getEncoded())) - .doesNotThrowAnyException(); - } - - @Test - void validSslContextConfig() throws Exception { - X509TrustManager trustManager = TlsUtil.trustManager(serverTls.certificate().getEncoded()); - - X509KeyManager keyManager = - TlsUtil.keyManager( - clientTls.privateKey().getEncoded(), clientTls.certificate().getEncoded()); - - SSLContext sslContext = SSLContext.getInstance("TLS"); - sslContext.init(new KeyManager[] {keyManager}, new TrustManager[] {trustManager}, null); - - assertThatCode(() -> JaegerGrpcSpanExporter.builder().setSslContext(sslContext, trustManager)) - .doesNotThrowAnyException(); - } - - @Test - @SuppressWarnings("PreferJavaTimeOverload") - void invalidConfig() { - assertThatThrownBy(() -> JaegerGrpcSpanExporter.builder().setTimeout(-1, TimeUnit.MILLISECONDS)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("timeout must be non-negative"); - assertThatThrownBy(() -> JaegerGrpcSpanExporter.builder().setTimeout(1, null)) - .isInstanceOf(NullPointerException.class) - .hasMessage("unit"); - assertThatThrownBy(() -> JaegerGrpcSpanExporter.builder().setTimeout(null)) - .isInstanceOf(NullPointerException.class) - .hasMessage("timeout"); - - assertThatThrownBy(() -> JaegerGrpcSpanExporter.builder().setEndpoint(null)) - .isInstanceOf(NullPointerException.class) - .hasMessage("endpoint"); - assertThatThrownBy(() -> JaegerGrpcSpanExporter.builder().setEndpoint("😺://localhost")) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("Invalid endpoint, must be a URL: 😺://localhost") - .hasCauseInstanceOf(URISyntaxException.class); - assertThatThrownBy(() -> JaegerGrpcSpanExporter.builder().setEndpoint("localhost")) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("Invalid endpoint, must start with http:// or https://: localhost"); - assertThatThrownBy(() -> JaegerGrpcSpanExporter.builder().setEndpoint("gopher://localhost")) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("Invalid endpoint, must start with http:// or https://: gopher://localhost"); - - assertThatThrownBy(() -> JaegerGrpcSpanExporter.builder().setCompression(null)) - .isInstanceOf(NullPointerException.class) - .hasMessage("compressionMethod"); - assertThatThrownBy(() -> JaegerGrpcSpanExporter.builder().setCompression("foo")) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage( - "Unsupported compression method. Supported compression methods include: gzip, none."); - } - - @Test - void compressionDefault() { - JaegerGrpcSpanExporter exporter = JaegerGrpcSpanExporter.builder().build(); - try { - assertThat(exporter).extracting("delegate.grpcSender.compressionEnabled").isEqualTo(false); - } finally { - exporter.shutdown(); - } - } - - @Test - void compressionNone() { - JaegerGrpcSpanExporter exporter = - JaegerGrpcSpanExporter.builder().setCompression("none").build(); - try { - assertThat(exporter).extracting("delegate.grpcSender.compressionEnabled").isEqualTo(false); - } finally { - exporter.shutdown(); - } - } - - @Test - void compressionGzip() { - JaegerGrpcSpanExporter exporter = - JaegerGrpcSpanExporter.builder().setCompression("gzip").build(); - try { - assertThat(exporter).extracting("delegate.grpcSender.compressionEnabled").isEqualTo(true); - } finally { - exporter.shutdown(); - } - } - - @Test - void compressionEnabledAndDisabled() { - JaegerGrpcSpanExporter exporter = - JaegerGrpcSpanExporter.builder().setCompression("gzip").setCompression("none").build(); - try { - assertThat(exporter).extracting("delegate.grpcSender.compressionEnabled").isEqualTo(false); - } finally { - exporter.shutdown(); - } - } - - @Test - @SuppressLogger(GrpcExporter.class) - void shutdown() { - JaegerGrpcSpanExporter exporter = - JaegerGrpcSpanExporter.builder().setEndpoint(server.httpUri().toString()).build(); - assertThat(exporter.shutdown().join(1, TimeUnit.SECONDS).isSuccess()).isTrue(); - assertThat(logs.getEvents()).isEmpty(); - assertThat( - exporter - .export(Collections.singletonList(testSpanData(Resource.getDefault(), "span name"))) - .join(10, TimeUnit.SECONDS) - .isSuccess()) - .isFalse(); - assertThat(exporter.shutdown().join(1, TimeUnit.SECONDS).isSuccess()).isTrue(); - logs.assertContains("Calling shutdown() multiple times."); - } -} diff --git a/exporters/jaeger/src/test/java/io/opentelemetry/exporter/jaeger/JaegerIntegrationTest.java b/exporters/jaeger/src/test/java/io/opentelemetry/exporter/jaeger/JaegerIntegrationTest.java deleted file mode 100644 index bfd7db8046f..00000000000 --- a/exporters/jaeger/src/test/java/io/opentelemetry/exporter/jaeger/JaegerIntegrationTest.java +++ /dev/null @@ -1,118 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.exporter.jaeger; - -import static io.opentelemetry.api.common.AttributeKey.stringKey; - -import com.fasterxml.jackson.core.TreeNode; -import com.fasterxml.jackson.jr.ob.JSON; -import com.fasterxml.jackson.jr.stree.JacksonJrsTreeCodec; -import io.opentelemetry.api.OpenTelemetry; -import io.opentelemetry.api.trace.Span; -import io.opentelemetry.sdk.OpenTelemetrySdk; -import io.opentelemetry.sdk.resources.Resource; -import io.opentelemetry.sdk.trace.SdkTracerProvider; -import io.opentelemetry.sdk.trace.export.SimpleSpanProcessor; -import io.opentelemetry.sdk.trace.export.SpanExporter; -import java.time.Duration; -import okhttp3.OkHttpClient; -import okhttp3.Request; -import okhttp3.Response; -import org.awaitility.Awaitility; -import org.junit.jupiter.api.Test; -import org.testcontainers.containers.GenericContainer; -import org.testcontainers.containers.wait.strategy.Wait; -import org.testcontainers.images.PullPolicy; -import org.testcontainers.junit.jupiter.Container; -import org.testcontainers.junit.jupiter.Testcontainers; - -@Testcontainers(disabledWithoutDocker = true) -@SuppressWarnings("deprecation") // Testing deprecated code -class JaegerIntegrationTest { - private static final OkHttpClient client = new OkHttpClient(); - - private static final int QUERY_PORT = 16686; - private static final int COLLECTOR_PORT = 14250; - private static final int HEALTH_PORT = 14269; - private static final String SERVICE_NAME = "E2E-test"; - private static final String JAEGER_URL = "http://localhost"; - - @Container - public static final GenericContainer jaegerContainer = - new GenericContainer<>("ghcr.io/open-telemetry/opentelemetry-java/jaeger:1.32") - .withImagePullPolicy(PullPolicy.alwaysPull()) - .withExposedPorts(COLLECTOR_PORT, QUERY_PORT, HEALTH_PORT) - .waitingFor(Wait.forHttp("/").forPort(HEALTH_PORT)); - - @Test - void testJaegerIntegration() { - OpenTelemetry openTelemetry = initOpenTelemetry(); - imitateWork(openTelemetry); - Awaitility.await() - .atMost(Duration.ofSeconds(30)) - .until(JaegerIntegrationTest::assertJaegerHaveTrace); - } - - private static OpenTelemetry initOpenTelemetry() { - SpanExporter jaegerExporter = - JaegerGrpcSpanExporter.builder() - .setEndpoint("http://localhost:" + jaegerContainer.getMappedPort(COLLECTOR_PORT)) - .setTimeout(Duration.ofSeconds(30)) - .build(); - return OpenTelemetrySdk.builder() - .setTracerProvider( - SdkTracerProvider.builder() - .addSpanProcessor(SimpleSpanProcessor.create(jaegerExporter)) - .setResource( - Resource.getDefault().toBuilder() - .put(stringKey("service.name"), SERVICE_NAME) - .build()) - .build()) - .build(); - } - - private void imitateWork(OpenTelemetry openTelemetry) { - Span span = - openTelemetry.getTracer(getClass().getCanonicalName()).spanBuilder("Test span").startSpan(); - span.addEvent("some event"); - try { - Thread.sleep(1000); - } catch (InterruptedException e) { - throw new RuntimeException(e); - } - span.end(); - } - - private static boolean assertJaegerHaveTrace() { - try { - String url = - String.format( - "%s/api/traces?service=%s", - String.format(JAEGER_URL + ":%d", jaegerContainer.getMappedPort(QUERY_PORT)), - SERVICE_NAME); - - Request request = - new Request.Builder() - .url(url) - .header("Content-Type", "application/json") - .header("Accept", "application/json") - .build(); - - TreeNode json; - try (Response response = client.newCall(request).execute()) { - json = - JSON.builder() - .treeCodec(new JacksonJrsTreeCodec()) - .build() - .treeFrom(response.body().byteStream()); - } - - return json.get("data").get(0).get("traceID") != null; - } catch (Exception e) { - return false; - } - } -} diff --git a/exporters/jaeger/src/test/java/io/opentelemetry/exporter/jaeger/PostSpansRequestMarshalerTest.java b/exporters/jaeger/src/test/java/io/opentelemetry/exporter/jaeger/PostSpansRequestMarshalerTest.java deleted file mode 100644 index 00e10c2124e..00000000000 --- a/exporters/jaeger/src/test/java/io/opentelemetry/exporter/jaeger/PostSpansRequestMarshalerTest.java +++ /dev/null @@ -1,437 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.exporter.jaeger; - -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 com.google.protobuf.InvalidProtocolBufferException; -import com.google.protobuf.Message; -import com.google.protobuf.util.Durations; -import com.google.protobuf.util.Timestamps; -import io.opentelemetry.api.common.Attributes; -import io.opentelemetry.api.trace.SpanContext; -import io.opentelemetry.api.trace.SpanId; -import io.opentelemetry.api.trace.SpanKind; -import io.opentelemetry.api.trace.TraceFlags; -import io.opentelemetry.api.trace.TraceId; -import io.opentelemetry.api.trace.TraceState; -import io.opentelemetry.exporter.internal.marshal.Marshaler; -import io.opentelemetry.exporter.jaeger.proto.api_v2.Model; -import io.opentelemetry.sdk.resources.Resource; -import io.opentelemetry.sdk.testing.trace.TestSpanData; -import io.opentelemetry.sdk.trace.data.EventData; -import io.opentelemetry.sdk.trace.data.LinkData; -import io.opentelemetry.sdk.trace.data.SpanData; -import io.opentelemetry.sdk.trace.data.StatusData; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.UncheckedIOException; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import java.util.concurrent.TimeUnit; -import javax.annotation.Nullable; -import org.junit.jupiter.api.Test; - -class PostSpansRequestMarshalerTest { - - private static final String KEY_LOG_EVENT = "event"; - private static final String KEY_EVENT_DROPPED_ATTRIBUTES_COUNT = - "otel.event.dropped_attributes_count"; - private static final String KEY_DROPPED_ATTRIBUTES_COUNT = "otel.dropped_attributes_count"; - private static final String KEY_DROPPED_EVENTS_COUNT = "otel.dropped_events_count"; - private static final String KEY_SPAN_KIND = "span.kind"; - - private static final String LINK_TRACE_ID = "00000000000000000000000000cba123"; - private static final String LINK_SPAN_ID = "0000000000fed456"; - private static final String TRACE_ID = "00000000000000000000000000abc123"; - private static final String SPAN_ID = "0000000000def456"; - private static final String PARENT_SPAN_ID = "0000000000aef789"; - - @Test - void testProtoSpans() { - long duration = 900; // ms - long startMs = System.currentTimeMillis(); - long endMs = startMs + duration; - - SpanData span = getSpanData(startMs, endMs, SpanKind.SERVER); - List spans = Collections.singletonList(span); - - SpanMarshaler[] jaegerSpans = SpanMarshaler.createRepeated(spans); - - // the span contents are checked somewhere else - assertThat(jaegerSpans).hasSize(1); - } - - @Test - @SuppressWarnings({"ProtoTimestampGetSecondsGetNano", "ProtoDurationGetSecondsGetNano"}) - void testProtoSpan() { - long duration = 900; // ms - long startMs = System.currentTimeMillis(); - long endMs = startMs + duration; - - SpanData span = getSpanData(startMs, endMs, SpanKind.SERVER, 2); - - // test - Model.Span jaegerSpan = parse(Model.Span.getDefaultInstance(), SpanMarshaler.create(span)); - assertThat(TraceId.fromBytes(jaegerSpan.getTraceId().toByteArray())) - .isEqualTo(span.getTraceId()); - assertThat(SpanId.fromBytes(jaegerSpan.getSpanId().toByteArray())).isEqualTo(span.getSpanId()); - assertThat(jaegerSpan.getOperationName()).isEqualTo("GET /api/endpoint"); - assertThat(jaegerSpan.getStartTime()).isEqualTo(Timestamps.fromMillis(startMs)); - assertThat(jaegerSpan.getDuration()).isEqualTo(Durations.fromMillis(duration)); - - assertThat(jaegerSpan.getTagsCount()).isEqualTo(7); - Model.KeyValue keyValue = getValue(jaegerSpan.getTagsList(), KEY_SPAN_KIND); - assertThat(keyValue).isNotNull(); - assertThat(keyValue.getVStr()).isEqualTo("server"); - - Model.KeyValue droppedAttributes = - getValue(jaegerSpan.getTagsList(), KEY_DROPPED_ATTRIBUTES_COUNT); - assertThat(droppedAttributes) - .isEqualTo( - Model.KeyValue.newBuilder() - .setKey(KEY_DROPPED_ATTRIBUTES_COUNT) - .setVType(Model.ValueType.INT64) - .setVInt64(2) - .build()); - - assertThat(jaegerSpan.getLogsCount()).isEqualTo(1); - Model.KeyValue droppedEvents = getValue(jaegerSpan.getTagsList(), KEY_DROPPED_EVENTS_COUNT); - assertThat(droppedEvents) - .isEqualTo( - Model.KeyValue.newBuilder() - .setKey(KEY_DROPPED_EVENTS_COUNT) - .setVType(Model.ValueType.INT64) - .setVInt64(1) - .build()); - - Model.Log log = jaegerSpan.getLogs(0); - keyValue = getValue(log.getFieldsList(), KEY_LOG_EVENT); - assertThat(keyValue).isNotNull(); - assertThat(keyValue.getVStr()).isEqualTo("the log message"); - keyValue = getValue(log.getFieldsList(), "foo"); - assertThat(keyValue).isNotNull(); - assertThat(keyValue.getVStr()).isEqualTo("bar"); - - assertThat(jaegerSpan.getReferencesCount()).isEqualTo(2); - - assertHasFollowsFrom(jaegerSpan); - assertHasParent(jaegerSpan); - } - - @Test - void testProtoSpan_internal() { - long duration = 900; // ms - long startMs = System.currentTimeMillis(); - long endMs = startMs + duration; - - SpanData span = getSpanData(startMs, endMs, SpanKind.INTERNAL); - - // test - Model.Span jaegerSpan = parse(Model.Span.getDefaultInstance(), SpanMarshaler.create(span)); - Model.KeyValue keyValue = getValue(jaegerSpan.getTagsList(), KEY_SPAN_KIND); - assertThat(keyValue).isNull(); - } - - @Test - void testJaegerLogs() { - // prepare - EventData eventsData = getTimedEvent(); - - // test - LogMarshaler[] logs = LogMarshaler.createRepeated(Collections.singletonList(eventsData)); - - // verify - assertThat(logs).hasSize(1); - } - - @Test - void testJaegerLog() { - // prepare - EventData event = getTimedEvent(); - - // test - Model.Log log = parse(Model.Log.getDefaultInstance(), LogMarshaler.create(event)); - - // verify - assertThat(log.getFieldsCount()).isEqualTo(2); - - Model.KeyValue keyValue = getValue(log.getFieldsList(), KEY_LOG_EVENT); - assertThat(keyValue).isNotNull(); - assertThat(keyValue.getVStr()).isEqualTo("the log message"); - keyValue = getValue(log.getFieldsList(), "foo"); - assertThat(keyValue).isNotNull(); - assertThat(keyValue.getVStr()).isEqualTo("bar"); - keyValue = getValue(log.getFieldsList(), KEY_EVENT_DROPPED_ATTRIBUTES_COUNT); - assertThat(keyValue).isNull(); - - // verify dropped_attributes_count - event = getTimedEvent(3); - log = parse(Model.Log.getDefaultInstance(), LogMarshaler.create(event)); - keyValue = getValue(log.getFieldsList(), KEY_EVENT_DROPPED_ATTRIBUTES_COUNT); - assertThat(keyValue).isNotNull(); - assertThat(keyValue.getVInt64()).isEqualTo(2); - } - - @Test - void testKeyValue() { - // test - Model.KeyValue kvB = - parse( - Model.KeyValue.getDefaultInstance(), - KeyValueMarshaler.create(booleanKey("valueB"), true)); - Model.KeyValue kvD = - parse( - Model.KeyValue.getDefaultInstance(), KeyValueMarshaler.create(doubleKey("valueD"), 1.)); - Model.KeyValue kvI = - parse(Model.KeyValue.getDefaultInstance(), KeyValueMarshaler.create(longKey("valueI"), 2L)); - Model.KeyValue kvS = - parse( - Model.KeyValue.getDefaultInstance(), - KeyValueMarshaler.create(stringKey("valueS"), "foobar")); - Model.KeyValue kvArrayB = - parse( - Model.KeyValue.getDefaultInstance(), - KeyValueMarshaler.create(booleanArrayKey("valueArrayB"), Arrays.asList(true, false))); - Model.KeyValue kvArrayD = - parse( - Model.KeyValue.getDefaultInstance(), - KeyValueMarshaler.create(doubleArrayKey("valueArrayD"), Arrays.asList(1.2345, 6.789))); - Model.KeyValue kvArrayI = - parse( - Model.KeyValue.getDefaultInstance(), - KeyValueMarshaler.create(longArrayKey("valueArrayI"), Arrays.asList(12345L, 67890L))); - Model.KeyValue kvArrayS = - parse( - Model.KeyValue.getDefaultInstance(), - KeyValueMarshaler.create( - stringArrayKey("valueArrayS"), Arrays.asList("foobar", "barfoo"))); - - // verify - assertThat(kvB.getVBool()).isTrue(); - assertThat(kvB.getVType()).isEqualTo(Model.ValueType.BOOL); - assertThat(kvD.getVFloat64()).isEqualTo(1.); - assertThat(kvD.getVType()).isEqualTo(Model.ValueType.FLOAT64); - assertThat(kvI.getVInt64()).isEqualTo(2); - assertThat(kvI.getVType()).isEqualTo(Model.ValueType.INT64); - assertThat(kvS.getVStr()).isEqualTo("foobar"); - assertThat(kvS.getVStrBytes().toStringUtf8()).isEqualTo("foobar"); - assertThat(kvS.getVType()).isEqualTo(Model.ValueType.STRING); - assertThat(kvArrayB.getVStr()).isEqualTo("[true,false]"); - assertThat(kvArrayB.getVStrBytes().toStringUtf8()).isEqualTo("[true,false]"); - assertThat(kvArrayB.getVType()).isEqualTo(Model.ValueType.STRING); - assertThat(kvArrayD.getVStr()).isEqualTo("[1.2345,6.789]"); - assertThat(kvArrayD.getVStrBytes().toStringUtf8()).isEqualTo("[1.2345,6.789]"); - assertThat(kvArrayD.getVType()).isEqualTo(Model.ValueType.STRING); - assertThat(kvArrayI.getVStr()).isEqualTo("[12345,67890]"); - assertThat(kvArrayI.getVStrBytes().toStringUtf8()).isEqualTo("[12345,67890]"); - assertThat(kvArrayI.getVType()).isEqualTo(Model.ValueType.STRING); - assertThat(kvArrayS.getVStr()).isEqualTo("[\"foobar\",\"barfoo\"]"); - assertThat(kvArrayS.getVStrBytes().toStringUtf8()).isEqualTo("[\"foobar\",\"barfoo\"]"); - assertThat(kvArrayS.getVType()).isEqualTo(Model.ValueType.STRING); - } - - @Test - void testSpanRefs() { - // prepare - LinkData link = - LinkData.create(createSpanContext("00000000000000000000000000cba123", "0000000000fed456")); - - // test - List spanRefs = - SpanRefMarshaler.createRepeated(Collections.singletonList(link)); - - // verify - assertThat(spanRefs).hasSize(1); // the actual span ref is tested in another test - } - - @Test - void testSpanRef() { - // prepare - LinkData link = LinkData.create(createSpanContext(TRACE_ID, SPAN_ID)); - - // test - Model.SpanRef spanRef = - parse(Model.SpanRef.getDefaultInstance(), SpanRefMarshaler.create(link)); - - // verify - assertThat(SpanId.fromBytes(spanRef.getSpanId().toByteArray())).isEqualTo(SPAN_ID); - assertThat(TraceId.fromBytes(spanRef.getTraceId().toByteArray())).isEqualTo(TRACE_ID); - assertThat(spanRef.getRefType()).isEqualTo(Model.SpanRefType.FOLLOWS_FROM); - } - - @Test - void testStatusNotUnset() { - long startMs = System.currentTimeMillis(); - long endMs = startMs + 900; - SpanData span = - TestSpanData.builder() - .setHasEnded(true) - .setSpanContext(createSpanContext(TRACE_ID, SPAN_ID)) - .setName("GET /api/endpoint") - .setStartEpochNanos(TimeUnit.MILLISECONDS.toNanos(startMs)) - .setEndEpochNanos(TimeUnit.MILLISECONDS.toNanos(endMs)) - .setKind(SpanKind.SERVER) - .setStatus(StatusData.error()) - .setTotalRecordedEvents(0) - .setTotalRecordedLinks(0) - .build(); - - assertThat(SpanMarshaler.create(span)).isNotNull(); - } - - @Test - void testSpanError() { - Attributes attributes = - Attributes.of( - stringKey("error.type"), - this.getClass().getName(), - stringKey("error.message"), - "server error"); - long startMs = System.currentTimeMillis(); - long endMs = startMs + 900; - SpanData span = - TestSpanData.builder() - .setHasEnded(true) - .setSpanContext(createSpanContext(TRACE_ID, SPAN_ID)) - .setName("GET /api/endpoint") - .setStartEpochNanos(TimeUnit.MILLISECONDS.toNanos(startMs)) - .setEndEpochNanos(TimeUnit.MILLISECONDS.toNanos(endMs)) - .setKind(SpanKind.SERVER) - .setStatus(StatusData.error()) - .setAttributes(attributes) - .setTotalRecordedEvents(0) - .setTotalRecordedLinks(0) - .build(); - - Model.Span jaegerSpan = parse(Model.Span.getDefaultInstance(), SpanMarshaler.create(span)); - Model.KeyValue errorType = getValue(jaegerSpan.getTagsList(), "error.type"); - assertThat(errorType).isNotNull(); - assertThat(errorType.getVStr()).isEqualTo(this.getClass().getName()); - Model.KeyValue error = getValue(jaegerSpan.getTagsList(), "error"); - assertThat(error).isNotNull(); - assertThat(error.getVBool()).isTrue(); - } - - private static EventData getTimedEvent() { - return getTimedEvent(-1); - } - - private static EventData getTimedEvent(int totalAttributeCount) { - long epochNanos = TimeUnit.MILLISECONDS.toNanos(System.currentTimeMillis()); - Attributes attributes = Attributes.of(stringKey("foo"), "bar"); - if (totalAttributeCount <= 0) { - totalAttributeCount = attributes.size(); - } - return EventData.create(epochNanos, "the log message", attributes, totalAttributeCount); - } - - private static SpanData getSpanData(long startMs, long endMs, SpanKind kind) { - return getSpanData(startMs, endMs, kind, 1); - } - - private static SpanData getSpanData( - long startMs, long endMs, SpanKind kind, int totalRecordedEvents) { - Attributes attributes = Attributes.of(booleanKey("valueB"), true); - - LinkData link = LinkData.create(createSpanContext(LINK_TRACE_ID, LINK_SPAN_ID), attributes); - - return TestSpanData.builder() - .setHasEnded(true) - .setSpanContext(createSpanContext(TRACE_ID, SPAN_ID)) - .setParentSpanContext( - SpanContext.create( - TRACE_ID, PARENT_SPAN_ID, TraceFlags.getDefault(), TraceState.getDefault())) - .setName("GET /api/endpoint") - .setStartEpochNanos(TimeUnit.MILLISECONDS.toNanos(startMs)) - .setEndEpochNanos(TimeUnit.MILLISECONDS.toNanos(endMs)) - .setAttributes(Attributes.of(booleanKey("valueB"), true)) - .setTotalAttributeCount(3) - .setEvents(Collections.singletonList(getTimedEvent())) - .setTotalRecordedEvents(totalRecordedEvents) - .setLinks(Collections.singletonList(link)) - .setTotalRecordedLinks(1) - .setKind(kind) - .setResource(Resource.create(Attributes.empty())) - .setStatus(StatusData.ok()) - .build(); - } - - private static SpanContext createSpanContext(String traceId, String spanId) { - return SpanContext.create(traceId, spanId, TraceFlags.getSampled(), TraceState.getDefault()); - } - - @Nullable - private static Model.KeyValue getValue(List tagsList, String s) { - for (Model.KeyValue kv : tagsList) { - if (kv.getKey().equals(s)) { - return kv; - } - } - return null; - } - - private static void assertHasFollowsFrom(Model.Span jaegerSpan) { - boolean found = false; - for (Model.SpanRef spanRef : jaegerSpan.getReferencesList()) { - if (Model.SpanRefType.FOLLOWS_FROM.equals(spanRef.getRefType())) { - assertThat(TraceId.fromBytes(spanRef.getTraceId().toByteArray())).isEqualTo(LINK_TRACE_ID); - assertThat(SpanId.fromBytes(spanRef.getSpanId().toByteArray())).isEqualTo(LINK_SPAN_ID); - found = true; - } - } - assertThat(found).withFailMessage("Should have found the follows-from reference").isTrue(); - } - - private static void assertHasParent(Model.Span jaegerSpan) { - boolean found = false; - for (Model.SpanRef spanRef : jaegerSpan.getReferencesList()) { - if (Model.SpanRefType.CHILD_OF.equals(spanRef.getRefType())) { - assertThat(TraceId.fromBytes(spanRef.getTraceId().toByteArray())).isEqualTo(TRACE_ID); - assertThat(SpanId.fromBytes(spanRef.getSpanId().toByteArray())).isEqualTo(PARENT_SPAN_ID); - found = true; - } - } - assertThat(found).withFailMessage("Should have found the parent reference").isTrue(); - } - - @SuppressWarnings("unchecked") - private static T parse(T prototype, Marshaler marshaler) { - byte[] serialized = toByteArray(marshaler); - T result; - try { - result = (T) prototype.newBuilderForType().mergeFrom(serialized).build(); - } catch (InvalidProtocolBufferException e) { - throw new UncheckedIOException(e); - } - // Our marshaler should produce the exact same length of serialized output (for example, field - // default values are not outputted), so we check that here. The output itself may have slightly - // different ordering, mostly due to the way we don't output oneof values in field order all the - // tieme. If the lengths are equal and the resulting protos are equal, the marshaling is - // guaranteed to be valid. - assertThat(result.getSerializedSize()).isEqualTo(serialized.length); - return result; - } - - private static byte[] toByteArray(Marshaler marshaler) { - ByteArrayOutputStream bos = new ByteArrayOutputStream(); - try { - marshaler.writeBinaryTo(bos); - } catch (IOException e) { - throw new UncheckedIOException(e); - } - return bos.toByteArray(); - } -} diff --git a/exporters/jaeger/src/test/java/io/opentelemetry/exporter/jaeger/internal/JaegerGrpcSpanExporterProviderTest.java b/exporters/jaeger/src/test/java/io/opentelemetry/exporter/jaeger/internal/JaegerGrpcSpanExporterProviderTest.java deleted file mode 100644 index 82ba56a2ebe..00000000000 --- a/exporters/jaeger/src/test/java/io/opentelemetry/exporter/jaeger/internal/JaegerGrpcSpanExporterProviderTest.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.exporter.jaeger.internal; - -import static org.assertj.core.api.Assertions.assertThat; - -import io.opentelemetry.sdk.autoconfigure.spi.internal.DefaultConfigProperties; -import io.opentelemetry.sdk.trace.export.SpanExporter; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; -import okhttp3.HttpUrl; -import org.junit.jupiter.api.Test; - -@SuppressWarnings("deprecation") // Testing deprecated code -class JaegerGrpcSpanExporterProviderTest { - - private static final JaegerGrpcSpanExporterProvider provider = - new JaegerGrpcSpanExporterProvider(); - - @Test - void getName() { - assertThat(provider.getName()).isEqualTo("jaeger"); - } - - @Test - void createExporter_Default() { - try (SpanExporter spanExporter = - provider.createExporter(DefaultConfigProperties.createFromMap(Collections.emptyMap()))) { - assertThat(spanExporter) - .isInstanceOf(io.opentelemetry.exporter.jaeger.JaegerGrpcSpanExporter.class); - assertThat(spanExporter) - .extracting("delegate.grpcSender") - .extracting("client") - .extracting("callTimeoutMillis") - .isEqualTo(10000); - assertThat(spanExporter) - .extracting("delegate.grpcSender") - .extracting("url") - .isEqualTo( - HttpUrl.get("http://localhost:14250/jaeger.api_v2.CollectorService/PostSpans")); - } - } - - @Test - void createExporter_WithConfiguration() { - Map config = new HashMap<>(); - config.put("otel.exporter.jaeger.endpoint", "http://endpoint:8080"); - config.put("otel.exporter.jaeger.timeout", "1s"); - - try (SpanExporter spanExporter = - provider.createExporter(DefaultConfigProperties.createFromMap(config))) { - assertThat(spanExporter) - .isInstanceOf(io.opentelemetry.exporter.jaeger.JaegerGrpcSpanExporter.class); - assertThat(spanExporter) - .extracting("delegate.grpcSender") - .extracting("client") - .extracting("callTimeoutMillis") - .isEqualTo(1000); - assertThat(spanExporter) - .extracting("delegate.grpcSender") - .extracting("url") - .isEqualTo(HttpUrl.get("http://endpoint:8080/jaeger.api_v2.CollectorService/PostSpans")); - } - } -} diff --git a/exporters/jaeger/src/test/resources/logback-test.xml b/exporters/jaeger/src/test/resources/logback-test.xml deleted file mode 100644 index 0f157506f5e..00000000000 --- a/exporters/jaeger/src/test/resources/logback-test.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - %d{HH:mm:ss.SSS} %-5level %logger - %msg%n - - - - - - - \ No newline at end of file diff --git a/exporters/otlp/common/src/main/java/io/opentelemetry/exporter/internal/otlp/metrics/ExponentialHistogramBucketsMarshaler.java b/exporters/otlp/common/src/main/java/io/opentelemetry/exporter/internal/otlp/metrics/ExponentialHistogramBucketsMarshaler.java index 54bb047398c..1f5affce3c0 100644 --- a/exporters/otlp/common/src/main/java/io/opentelemetry/exporter/internal/otlp/metrics/ExponentialHistogramBucketsMarshaler.java +++ b/exporters/otlp/common/src/main/java/io/opentelemetry/exporter/internal/otlp/metrics/ExponentialHistogramBucketsMarshaler.java @@ -9,6 +9,7 @@ import io.opentelemetry.exporter.internal.marshal.MarshalerWithSize; import io.opentelemetry.exporter.internal.marshal.Serializer; import io.opentelemetry.proto.metrics.v1.internal.ExponentialHistogramDataPoint; +import io.opentelemetry.sdk.internal.DynamicPrimitiveLongList; import io.opentelemetry.sdk.internal.PrimitiveLongList; import io.opentelemetry.sdk.metrics.data.ExponentialHistogramBuckets; import java.io.IOException; @@ -35,16 +36,29 @@ private ExponentialHistogramBucketsMarshaler(int offset, List counts) { @Override protected void writeTo(Serializer output) throws IOException { output.serializeSInt32(ExponentialHistogramDataPoint.Buckets.OFFSET, offset); - output.serializeRepeatedUInt64( - ExponentialHistogramDataPoint.Buckets.BUCKET_COUNTS, PrimitiveLongList.toArray(counts)); + if (counts instanceof DynamicPrimitiveLongList) { + output.serializeRepeatedUInt64( + ExponentialHistogramDataPoint.Buckets.BUCKET_COUNTS, (DynamicPrimitiveLongList) counts); + } else { + output.serializeRepeatedUInt64( + ExponentialHistogramDataPoint.Buckets.BUCKET_COUNTS, PrimitiveLongList.toArray(counts)); + } } private static int calculateSize(int offset, List counts) { int size = 0; size += MarshalerUtil.sizeSInt32(ExponentialHistogramDataPoint.Buckets.OFFSET, offset); - size += - MarshalerUtil.sizeRepeatedUInt64( - ExponentialHistogramDataPoint.Buckets.BUCKET_COUNTS, PrimitiveLongList.toArray(counts)); + if (counts instanceof DynamicPrimitiveLongList) { + size += + MarshalerUtil.sizeRepeatedUInt64( + ExponentialHistogramDataPoint.Buckets.BUCKET_COUNTS, + (DynamicPrimitiveLongList) counts); + } else { + size += + MarshalerUtil.sizeRepeatedUInt64( + ExponentialHistogramDataPoint.Buckets.BUCKET_COUNTS, + PrimitiveLongList.toArray(counts)); + } return size; } } diff --git a/exporters/otlp/common/src/test/java/io/opentelemetry/exporter/internal/otlp/metrics/MetricsRequestMarshalerTest.java b/exporters/otlp/common/src/test/java/io/opentelemetry/exporter/internal/otlp/metrics/MetricsRequestMarshalerTest.java index fc92bb29cc9..2e0de0ac239 100644 --- a/exporters/otlp/common/src/test/java/io/opentelemetry/exporter/internal/otlp/metrics/MetricsRequestMarshalerTest.java +++ b/exporters/otlp/common/src/test/java/io/opentelemetry/exporter/internal/otlp/metrics/MetricsRequestMarshalerTest.java @@ -40,6 +40,7 @@ import io.opentelemetry.proto.metrics.v1.Summary; import io.opentelemetry.proto.metrics.v1.SummaryDataPoint; import io.opentelemetry.sdk.common.InstrumentationScopeInfo; +import io.opentelemetry.sdk.internal.DynamicPrimitiveLongList; import io.opentelemetry.sdk.metrics.data.AggregationTemporality; import io.opentelemetry.sdk.metrics.data.ExponentialHistogramPointData; import io.opentelemetry.sdk.metrics.data.HistogramPointData; @@ -61,6 +62,8 @@ import io.opentelemetry.sdk.metrics.internal.data.ImmutableSummaryData; import io.opentelemetry.sdk.metrics.internal.data.ImmutableSummaryPointData; import io.opentelemetry.sdk.metrics.internal.data.ImmutableValueAtQuantile; +import io.opentelemetry.sdk.metrics.internal.data.MutableExponentialHistogramBuckets; +import io.opentelemetry.sdk.metrics.internal.data.MutableExponentialHistogramPointData; import io.opentelemetry.sdk.resources.Resource; import java.io.ByteArrayOutputStream; import java.io.IOException; @@ -508,6 +511,105 @@ void exponentialHistogramDataPoints() { .build()); } + @SuppressWarnings("PointlessArithmeticExpression") + @Test + void exponentialHistogramReusableDataPoints() { + assertThat( + toExponentialHistogramDataPoints( + ImmutableList.of( + new MutableExponentialHistogramPointData() + .set( + 0, + 123.4, + 1, + /* hasMin= */ false, + 0, + /* hasMax= */ false, + 0, + new MutableExponentialHistogramBuckets() + .set(0, 0, 0, DynamicPrimitiveLongList.empty()), + new MutableExponentialHistogramBuckets() + .set(0, 0, 0, DynamicPrimitiveLongList.empty()), + 123, + 456, + Attributes.empty(), + Collections.emptyList()), + new MutableExponentialHistogramPointData() + .set( + 0, + 123.4, + 1, + /* hasMin= */ true, + 3.3, + /* hasMax= */ true, + 80.1, + new MutableExponentialHistogramBuckets() + .set(0, 1, 1 + 0 + 2, DynamicPrimitiveLongList.of(1L, 0L, 2L)), + new MutableExponentialHistogramBuckets() + .set(0, 0, 0, DynamicPrimitiveLongList.empty()), + 123, + 456, + Attributes.of(stringKey("key"), "value"), + ImmutableList.of( + ImmutableDoubleExemplarData.create( + Attributes.of(stringKey("test"), "value"), + 2, + SpanContext.create( + "00000000000000000000000000000001", + "0000000000000002", + TraceFlags.getDefault(), + TraceState.getDefault()), + 1.5)))))) + .containsExactly( + ExponentialHistogramDataPoint.newBuilder() + .setStartTimeUnixNano(123) + .setTimeUnixNano(456) + .setCount(1) + .setScale(0) + .setSum(123.4) + .setZeroCount(1) + .setPositive( + ExponentialHistogramDataPoint.Buckets.newBuilder().setOffset(0)) // no buckets + .setNegative( + ExponentialHistogramDataPoint.Buckets.newBuilder().setOffset(0)) // no buckets + .build(), + ExponentialHistogramDataPoint.newBuilder() + .setStartTimeUnixNano(123) + .setTimeUnixNano(456) + .setCount(4) // Counts in positive, negative, and zero count. + .addAllAttributes( + singletonList( + KeyValue.newBuilder().setKey("key").setValue(stringValue("value")).build())) + .setScale(0) + .setSum(123.4) + .setMin(3.3) + .setMax(80.1) + .setZeroCount(1) + .setPositive( + ExponentialHistogramDataPoint.Buckets.newBuilder() + .setOffset(1) + .addBucketCounts(1) + .addBucketCounts(0) + .addBucketCounts(2)) + .setNegative( + ExponentialHistogramDataPoint.Buckets.newBuilder().setOffset(0)) // no buckets + .addExemplars( + Exemplar.newBuilder() + .setTimeUnixNano(2) + .addFilteredAttributes( + KeyValue.newBuilder() + .setKey("test") + .setValue(stringValue("value")) + .build()) + .setSpanId(ByteString.copyFrom(new byte[] {0, 0, 0, 0, 0, 0, 0, 2})) + .setTraceId( + ByteString.copyFrom( + new byte[] {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1})) + .setAsDouble(1.5) + .build()) + .build()); + } + @Test void toProtoMetric_monotonic() { assertThat( 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 67a5870015c..820f1f64d6a 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 @@ -183,8 +183,7 @@ private CounterSnapshot convertLongCounter( MetricMetadata metadata, InstrumentationScopeInfo scope, Collection dataPoints) { - List data = - new ArrayList(dataPoints.size()); + List data = new ArrayList<>(dataPoints.size()); for (LongPointData longData : dataPoints) { data.add( new CounterDataPointSnapshot( @@ -449,8 +448,14 @@ private static MetricMetadata convertMetadata(MetricData metricData) { String help = metricData.getDescription(); Unit unit = PrometheusUnitsHelper.convertUnit(metricData.getUnit()); if (unit != null && !name.endsWith(unit.toString())) { - name += "_" + unit; + // Need to re-sanitize metric name since unit may contain illegal characters + name = sanitizeMetricName(name + "_" + unit); + } + // Repeated __ are not allowed according to spec, although this is allowed in prometheus + while (name.contains("__")) { + name = name.replace("__", "_"); } + return new MetricMetadata(name, help, unit); } @@ -538,7 +543,8 @@ private static MetricMetadata mergeMetadata(MetricMetadata a, MetricMetadata b) "Conflicting metrics: Multiple metrics with name " + name + " but different units found. Dropping the one with unit " - + b.getUnit()); + + b.getUnit() + + "."); return null; } return new MetricMetadata(name, help, unit); diff --git a/exporters/prometheus/src/main/java/io/opentelemetry/exporter/prometheus/PrometheusUnitsHelper.java b/exporters/prometheus/src/main/java/io/opentelemetry/exporter/prometheus/PrometheusUnitsHelper.java index 9657b88ada5..db0503eb3fb 100644 --- a/exporters/prometheus/src/main/java/io/opentelemetry/exporter/prometheus/PrometheusUnitsHelper.java +++ b/exporters/prometheus/src/main/java/io/opentelemetry/exporter/prometheus/PrometheusUnitsHelper.java @@ -69,9 +69,7 @@ private static void initUnit(String otelName, String pluralName, String singular @Nullable static Unit convertUnit(String otelUnit) { - if (otelUnit.isEmpty() || otelUnit.equals("1")) { - // The spec says "1" should be translated to "ratio", but this is not implemented in the Java - // SDK. + if (otelUnit.isEmpty()) { return null; } if (otelUnit.contains("{")) { 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 new file mode 100644 index 00000000000..6347d781498 --- /dev/null +++ b/exporters/prometheus/src/test/java/io/opentelemetry/exporter/prometheus/Otel2PrometheusConverterTest.java @@ -0,0 +1,224 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.exporter.prometheus; + +import static org.assertj.core.api.Assertions.assertThat; + +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.sdk.common.InstrumentationScopeInfo; +import io.opentelemetry.sdk.metrics.data.AggregationTemporality; +import io.opentelemetry.sdk.metrics.data.MetricData; +import io.opentelemetry.sdk.metrics.data.MetricDataType; +import io.opentelemetry.sdk.metrics.internal.data.ImmutableGaugeData; +import io.opentelemetry.sdk.metrics.internal.data.ImmutableHistogramData; +import io.opentelemetry.sdk.metrics.internal.data.ImmutableHistogramPointData; +import io.opentelemetry.sdk.metrics.internal.data.ImmutableLongPointData; +import io.opentelemetry.sdk.metrics.internal.data.ImmutableMetricData; +import io.opentelemetry.sdk.metrics.internal.data.ImmutableSumData; +import io.opentelemetry.sdk.metrics.internal.data.ImmutableSummaryData; +import io.opentelemetry.sdk.metrics.internal.data.ImmutableSummaryPointData; +import io.opentelemetry.sdk.resources.Resource; +import io.prometheus.metrics.expositionformats.ExpositionFormats; +import io.prometheus.metrics.model.snapshots.MetricSnapshots; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.Arrays; +import java.util.Collections; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Stream; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +class Otel2PrometheusConverterTest { + + private static final Pattern PATTERN = + Pattern.compile( + "# HELP (?.*)\n# TYPE (?.*)\n(?.*)\\{otel_scope_name=\"scope\"}(.|\\n)*"); + + private final Otel2PrometheusConverter converter = new Otel2PrometheusConverter(true); + + @ParameterizedTest + @MethodSource("metricMetadataArgs") + void metricMetadata( + MetricData metricData, String expectedType, String expectedHelp, String expectedMetricName) + throws IOException { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + MetricSnapshots snapshots = converter.convert(Collections.singletonList(metricData)); + ExpositionFormats.init().getPrometheusTextFormatWriter().write(baos, snapshots); + String expositionFormat = new String(baos.toByteArray(), StandardCharsets.UTF_8); + + // Uncomment to debug exposition format output + // System.out.println(expositionFormat); + + Matcher matcher = PATTERN.matcher(expositionFormat); + assertThat(matcher.matches()).isTrue(); + assertThat(matcher.group("help")).isEqualTo(expectedHelp); + assertThat(matcher.group("type")).isEqualTo(expectedType); + // Note: Summaries and histograms produce output which matches METRIC_NAME_PATTERN multiple + // times. The pattern ends up matching against the first. + assertThat(matcher.group("metricName")).isEqualTo(expectedMetricName); + } + + 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_hertz_total counter", + "metric_total_hertz_hertz_total description", + "metric_total_hertz_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")); + } + + static MetricData createSampleMetricData( + String metricName, String metricUnit, MetricDataType metricDataType) { + switch (metricDataType) { + case SUMMARY: + return ImmutableMetricData.createDoubleSummary( + Resource.getDefault(), + InstrumentationScopeInfo.create("scope"), + metricName, + "description", + metricUnit, + ImmutableSummaryData.create( + Collections.singletonList( + ImmutableSummaryPointData.create( + 0, 1, Attributes.empty(), 1, 1, Collections.emptyList())))); + case LONG_SUM: + return ImmutableMetricData.createLongSum( + Resource.getDefault(), + InstrumentationScopeInfo.create("scope"), + metricName, + "description", + metricUnit, + ImmutableSumData.create( + true, + AggregationTemporality.CUMULATIVE, + Collections.singletonList( + ImmutableLongPointData.create(0, 1, Attributes.empty(), 1L)))); + case LONG_GAUGE: + return ImmutableMetricData.createLongGauge( + Resource.getDefault(), + InstrumentationScopeInfo.create("scope"), + metricName, + "description", + metricUnit, + ImmutableGaugeData.create( + Collections.singletonList( + ImmutableLongPointData.create(0, 1, Attributes.empty(), 1L)))); + case HISTOGRAM: + return ImmutableMetricData.createDoubleHistogram( + Resource.getDefault(), + InstrumentationScopeInfo.create("scope"), + metricName, + "description", + metricUnit, + ImmutableHistogramData.create( + AggregationTemporality.CUMULATIVE, + Collections.singletonList( + ImmutableHistogramPointData.create( + 0, + 1, + Attributes.empty(), + 1, + false, + -1, + false, + -1, + Collections.singletonList(1.0), + Arrays.asList(0L, 1L))))); + default: + throw new IllegalArgumentException("Unsupported metric data type: " + metricDataType); + } + } +} 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 9f172e83804..d899dddd122 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 @@ -115,12 +115,12 @@ void fetchPrometheus() { .isEqualTo("text/plain; version=0.0.4; charset=utf-8"); assertThat(response.contentUtf8()) .isEqualTo( - "# HELP grpc_name_total long_description\n" - + "# TYPE grpc_name_total counter\n" - + "grpc_name_total{kp=\"vp\",otel_scope_name=\"grpc\",otel_scope_version=\"version\"} 5.0\n" - + "# HELP http_name_total double_description\n" - + "# TYPE http_name_total counter\n" - + "http_name_total{kp=\"vp\",otel_scope_name=\"http\",otel_scope_version=\"version\"} 3.5\n" + "# HELP grpc_name_unit_total long_description\n" + + "# TYPE grpc_name_unit_total counter\n" + + "grpc_name_unit_total{kp=\"vp\",otel_scope_name=\"grpc\",otel_scope_version=\"version\"} 5.0\n" + + "# HELP http_name_unit_total double_description\n" + + "# TYPE http_name_unit_total counter\n" + + "http_name_unit_total{kp=\"vp\",otel_scope_name=\"http\",otel_scope_version=\"version\"} 3.5\n" + "# TYPE target_info gauge\n" + "target_info{kr=\"vr\"} 1\n"); } @@ -142,12 +142,14 @@ void fetchOpenMetrics() { .isEqualTo("application/openmetrics-text; version=1.0.0; charset=utf-8"); assertThat(response.contentUtf8()) .isEqualTo( - "# TYPE grpc_name counter\n" - + "# HELP grpc_name long_description\n" - + "grpc_name_total{kp=\"vp\",otel_scope_name=\"grpc\",otel_scope_version=\"version\"} 5.0\n" - + "# TYPE http_name counter\n" - + "# HELP http_name double_description\n" - + "http_name_total{kp=\"vp\",otel_scope_name=\"http\",otel_scope_version=\"version\"} 3.5\n" + "# TYPE grpc_name_unit counter\n" + + "# UNIT grpc_name_unit unit\n" + + "# HELP grpc_name_unit long_description\n" + + "grpc_name_unit_total{kp=\"vp\",otel_scope_name=\"grpc\",otel_scope_version=\"version\"} 5.0\n" + + "# TYPE http_name_unit counter\n" + + "# UNIT http_name_unit unit\n" + + "# HELP http_name_unit double_description\n" + + "http_name_unit_total{kp=\"vp\",otel_scope_name=\"http\",otel_scope_version=\"version\"} 3.5\n" + "# TYPE target info\n" + "target_info{kr=\"vr\"} 1\n" + "# EOF\n"); @@ -157,7 +159,7 @@ void fetchOpenMetrics() { void fetchFiltered() { AggregatedHttpResponse response = client - .get("/?name[]=grpc_name_total&name[]=bears_total&name[]=target_info") + .get("/?name[]=grpc_name_unit_total&name[]=bears_total&name[]=target_info") .aggregate() .join(); assertThat(response.status()).isEqualTo(HttpStatus.OK); @@ -166,9 +168,9 @@ void fetchFiltered() { assertThat(response.contentUtf8()) .isEqualTo( "" - + "# HELP grpc_name_total long_description\n" - + "# TYPE grpc_name_total counter\n" - + "grpc_name_total{kp=\"vp\",otel_scope_name=\"grpc\",otel_scope_version=\"version\"} 5.0\n" + + "# HELP grpc_name_unit_total long_description\n" + + "# TYPE grpc_name_unit_total counter\n" + + "grpc_name_unit_total{kp=\"vp\",otel_scope_name=\"grpc\",otel_scope_version=\"version\"} 5.0\n" + "# TYPE target_info gauge\n" + "target_info{kr=\"vr\"} 1\n"); } @@ -189,12 +191,12 @@ void fetchPrometheusCompressed() throws IOException { String content = new String(ByteStreams.toByteArray(gis), StandardCharsets.UTF_8); assertThat(content) .isEqualTo( - "# HELP grpc_name_total long_description\n" - + "# TYPE grpc_name_total counter\n" - + "grpc_name_total{kp=\"vp\",otel_scope_name=\"grpc\",otel_scope_version=\"version\"} 5.0\n" - + "# HELP http_name_total double_description\n" - + "# TYPE http_name_total counter\n" - + "http_name_total{kp=\"vp\",otel_scope_name=\"http\",otel_scope_version=\"version\"} 3.5\n" + "# HELP grpc_name_unit_total long_description\n" + + "# TYPE grpc_name_unit_total counter\n" + + "grpc_name_unit_total{kp=\"vp\",otel_scope_name=\"grpc\",otel_scope_version=\"version\"} 5.0\n" + + "# HELP http_name_unit_total double_description\n" + + "# TYPE http_name_unit_total counter\n" + + "http_name_unit_total{kp=\"vp\",otel_scope_name=\"http\",otel_scope_version=\"version\"} 3.5\n" + "# TYPE target_info gauge\n" + "target_info{kr=\"vr\"} 1\n"); } @@ -252,7 +254,7 @@ void fetch_DuplicateMetrics() { InstrumentationScopeInfo.create("scope3"), "foo_unit_total", "unused", - "unit", + "", ImmutableGaugeData.create( Collections.singletonList( ImmutableLongPointData.create(123, 456, Attributes.empty(), 3)))))); @@ -272,7 +274,7 @@ void fetch_DuplicateMetrics() { // Validate conflict warning message assertThat(logs.getEvents()).hasSize(1); logs.assertContains( - "Conflicting metric name foo_unit: Found one metric with type counter and one of type gauge. Dropping the one with type gauge."); + "Conflicting metrics: Multiple metrics with name foo_unit but different units found. Dropping the one with unit null."); } @Test @@ -318,7 +320,7 @@ private static List generateTestData() { InstrumentationScopeInfo.builder("grpc").setVersion("version").build(), "grpc.name", "long_description", - "1", + "unit", ImmutableSumData.create( /* isMonotonic= */ true, AggregationTemporality.CUMULATIVE, @@ -330,7 +332,7 @@ private static List generateTestData() { InstrumentationScopeInfo.builder("http").setVersion("version").build(), "http.name", "double_description", - "1", + "unit", ImmutableSumData.create( /* isMonotonic= */ true, AggregationTemporality.CUMULATIVE, diff --git a/exporters/prometheus/src/test/java/io/opentelemetry/exporter/prometheus/PrometheusUnitsHelperTest.java b/exporters/prometheus/src/test/java/io/opentelemetry/exporter/prometheus/PrometheusUnitsHelperTest.java index aa44005978d..658642c024a 100644 --- a/exporters/prometheus/src/test/java/io/opentelemetry/exporter/prometheus/PrometheusUnitsHelperTest.java +++ b/exporters/prometheus/src/test/java/io/opentelemetry/exporter/prometheus/PrometheusUnitsHelperTest.java @@ -70,7 +70,7 @@ private static Stream providePrometheusOTelUnitEquivalentPairs() { // Unit not found - Case sensitive Arguments.of("S", "S"), // Special case - 1 - Arguments.of("1", null), + Arguments.of("1", "ratio"), // Special Case - Drop metric units in {} Arguments.of("{packets}", null), // Special Case - Dropped metric units only in {} diff --git a/exporters/zipkin/src/main/java/io/opentelemetry/exporter/zipkin/BytesEncoderAdapter.java b/exporters/zipkin/src/main/java/io/opentelemetry/exporter/zipkin/BytesEncoderAdapter.java new file mode 100644 index 00000000000..8605eacd936 --- /dev/null +++ b/exporters/zipkin/src/main/java/io/opentelemetry/exporter/zipkin/BytesEncoderAdapter.java @@ -0,0 +1,58 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.exporter.zipkin; + +import zipkin2.Span; +import zipkin2.reporter.BytesEncoder; +import zipkin2.reporter.Encoding; + +/** + * This supports the deprecated method {@link + * ZipkinSpanExporterBuilder#setEncoder(zipkin2.codec.BytesEncoder)}. + */ +final class BytesEncoderAdapter implements BytesEncoder { + private final zipkin2.codec.BytesEncoder delegate; + private final Encoding encoding; + + @SuppressWarnings("deprecation") // we have to use the deprecated thrift encoding to return it + BytesEncoderAdapter(zipkin2.codec.BytesEncoder delegate) { + this.delegate = delegate; + switch (delegate.encoding()) { + case JSON: + this.encoding = Encoding.JSON; + break; + case PROTO3: + this.encoding = Encoding.PROTO3; + break; + case THRIFT: + this.encoding = Encoding.THRIFT; + break; + default: + // Only possible if zipkin2 adds an encoding besides above, which is very unlikely. + throw new UnsupportedOperationException("unsupported encoding " + delegate.encoding()); + } + } + + @Override + public Encoding encoding() { + return encoding; + } + + @Override + public int sizeInBytes(Span span) { + return delegate.sizeInBytes(span); + } + + @Override + public byte[] encode(Span span) { + return delegate.encode(span); + } + + @Override + public String toString() { + return delegate.toString(); + } +} 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 37d8c1fd022..840e53b58de 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 @@ -19,10 +19,10 @@ import java.util.function.Supplier; import java.util.logging.Level; import java.util.logging.Logger; -import zipkin2.Callback; import zipkin2.Span; -import zipkin2.codec.BytesEncoder; -import zipkin2.codec.Encoding; +import zipkin2.reporter.BytesEncoder; +import zipkin2.reporter.Callback; +import zipkin2.reporter.Encoding; import zipkin2.reporter.Sender; /** 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 2691c700954..ad19cbc8d33 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 @@ -17,9 +17,9 @@ import java.util.function.Supplier; import javax.annotation.Nullable; import zipkin2.Span; -import zipkin2.codec.BytesEncoder; -import zipkin2.codec.SpanBytesEncoder; +import zipkin2.reporter.BytesEncoder; import zipkin2.reporter.Sender; +import zipkin2.reporter.SpanBytesEncoder; import zipkin2.reporter.okhttp3.OkHttpSender; /** Builder class for {@link ZipkinSpanExporter}. */ @@ -49,6 +49,21 @@ public ZipkinSpanExporterBuilder setSender(Sender sender) { return this; } + /** + * Sets the {@link zipkin2.codec.BytesEncoder}, which controls the format used by the {@link + * Sender}. Defaults to the {@link zipkin2.codec.SpanBytesEncoder#JSON_V2}. + * + * @param encoder the {@code BytesEncoder} to use. + * @return this. + * @see zipkin2.codec.SpanBytesEncoder + * @deprecated Use {@link #setEncoder(BytesEncoder)} instead. + */ + @Deprecated + public ZipkinSpanExporterBuilder setEncoder(zipkin2.codec.BytesEncoder encoder) { + requireNonNull(encoder, "encoder"); + return setEncoder(new BytesEncoderAdapter(encoder)); + } + /** * Sets the {@link BytesEncoder}, which controls the format used by the {@link Sender}. Defaults * to the {@link SpanBytesEncoder#JSON_V2}. diff --git a/exporters/zipkin/src/test/java/io/opentelemetry/exporter/zipkin/BytesEncoderAdapterTest.java b/exporters/zipkin/src/test/java/io/opentelemetry/exporter/zipkin/BytesEncoderAdapterTest.java new file mode 100644 index 00000000000..24b16eb3a06 --- /dev/null +++ b/exporters/zipkin/src/test/java/io/opentelemetry/exporter/zipkin/BytesEncoderAdapterTest.java @@ -0,0 +1,70 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.exporter.zipkin; + +import static io.opentelemetry.exporter.zipkin.ZipkinTestUtil.PARENT_SPAN_ID; +import static io.opentelemetry.exporter.zipkin.ZipkinTestUtil.SPAN_ID; +import static io.opentelemetry.exporter.zipkin.ZipkinTestUtil.TRACE_ID; +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.jupiter.api.Test; +import zipkin2.Endpoint; +import zipkin2.Span; +import zipkin2.reporter.Encoding; +import zipkin2.reporter.SpanBytesEncoder; + +class BytesEncoderAdapterTest { + + /** Contains {@link Span#localEndpoint()} to ensure would be encoded differently. */ + private final Span testSpan = + Span.newBuilder() + .traceId(TRACE_ID) + .parentId(PARENT_SPAN_ID) + .id(SPAN_ID) + .localEndpoint(Endpoint.newBuilder().serviceName("test").build()) + .build(); + + @Test + void testJsonV2() { + BytesEncoderAdapter adapter = new BytesEncoderAdapter(zipkin2.codec.SpanBytesEncoder.JSON_V2); + assertThat(adapter.encoding()).isEqualTo(Encoding.JSON); + assertThat(adapter.encode(testSpan)).isEqualTo(SpanBytesEncoder.JSON_V2.encode(testSpan)); + assertThat(adapter.sizeInBytes(testSpan)) + .isEqualTo(SpanBytesEncoder.JSON_V2.sizeInBytes(testSpan)); + assertThat(adapter).hasToString(SpanBytesEncoder.JSON_V2.toString()); + } + + @Test + void testProtobuf() { + BytesEncoderAdapter adapter = new BytesEncoderAdapter(zipkin2.codec.SpanBytesEncoder.PROTO3); + assertThat(adapter.encoding()).isEqualTo(Encoding.PROTO3); + assertThat(adapter.encode(testSpan)).isEqualTo(SpanBytesEncoder.PROTO3.encode(testSpan)); + assertThat(adapter.sizeInBytes(testSpan)) + .isEqualTo(SpanBytesEncoder.PROTO3.sizeInBytes(testSpan)); + assertThat(adapter).hasToString(SpanBytesEncoder.PROTO3.toString()); + } + + @Test + @SuppressWarnings("deprecation") // we have to use the deprecated thrift encoding to test it + void testThrift() { + BytesEncoderAdapter adapter = new BytesEncoderAdapter(zipkin2.codec.SpanBytesEncoder.THRIFT); + assertThat(adapter.encoding()).isEqualTo(Encoding.THRIFT); + assertThat(adapter.encode(testSpan)).isEqualTo(SpanBytesEncoder.THRIFT.encode(testSpan)); + assertThat(adapter.sizeInBytes(testSpan)) + .isEqualTo(SpanBytesEncoder.THRIFT.sizeInBytes(testSpan)); + assertThat(adapter).hasToString(SpanBytesEncoder.THRIFT.toString()); + } + + @Test + void testJsonV1() { + BytesEncoderAdapter adapter = new BytesEncoderAdapter(zipkin2.codec.SpanBytesEncoder.JSON_V1); + assertThat(adapter.encoding()).isEqualTo(Encoding.JSON); + assertThat(adapter.encode(testSpan)).isEqualTo(SpanBytesEncoder.JSON_V1.encode(testSpan)); + assertThat(adapter.sizeInBytes(testSpan)) + .isEqualTo(SpanBytesEncoder.JSON_V1.sizeInBytes(testSpan)); + assertThat(adapter).hasToString(SpanBytesEncoder.JSON_V1.toString()); + } +} diff --git a/exporters/zipkin/src/test/java/io/opentelemetry/exporter/zipkin/ZipkinSpanExporterEndToEndHttpTest.java b/exporters/zipkin/src/test/java/io/opentelemetry/exporter/zipkin/ZipkinSpanExporterEndToEndHttpTest.java index c7d23af452f..c55d2c01c67 100644 --- a/exporters/zipkin/src/test/java/io/opentelemetry/exporter/zipkin/ZipkinSpanExporterEndToEndHttpTest.java +++ b/exporters/zipkin/src/test/java/io/opentelemetry/exporter/zipkin/ZipkinSpanExporterEndToEndHttpTest.java @@ -44,9 +44,9 @@ import org.testcontainers.junit.jupiter.Testcontainers; import zipkin2.Endpoint; import zipkin2.Span; -import zipkin2.codec.Encoding; import zipkin2.codec.SpanBytesDecoder; -import zipkin2.codec.SpanBytesEncoder; +import zipkin2.reporter.Encoding; +import zipkin2.reporter.SpanBytesEncoder; import zipkin2.reporter.okhttp3.OkHttpSender; @Testcontainers(disabledWithoutDocker = true) @@ -82,7 +82,7 @@ class ZipkinSpanExporterEndToEndHttpTest { @Container public static final GenericContainer zipkinContainer = - new GenericContainer<>("ghcr.io/openzipkin/zipkin:2.23") + new GenericContainer<>("ghcr.io/openzipkin/zipkin:2.27") .withExposedPorts(ZIPKIN_API_PORT) .waitingFor(Wait.forHttp("/health").forPort(ZIPKIN_API_PORT)); 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 263edadad53..38e4534c71a 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 @@ -29,11 +29,13 @@ import org.junit.jupiter.api.extension.RegisterExtension; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; -import zipkin2.Call; -import zipkin2.Callback; import zipkin2.Span; -import zipkin2.codec.SpanBytesEncoder; +import zipkin2.reporter.BytesEncoder; +import zipkin2.reporter.Call; +import zipkin2.reporter.Callback; +import zipkin2.reporter.Encoding; import zipkin2.reporter.Sender; +import zipkin2.reporter.SpanBytesEncoder; @ExtendWith(MockitoExtension.class) class ZipkinSpanExporterTest { @@ -144,7 +146,8 @@ void testShutdown() throws IOException { } @Test - @SuppressWarnings("PreferJavaTimeOverload") + @SuppressWarnings({"PreferJavaTimeOverload", "deprecation"}) + // we have to use the deprecated setEncoder overload to test it void invalidConfig() { assertThatThrownBy(() -> ZipkinSpanExporter.builder().setReadTimeout(-1, TimeUnit.MILLISECONDS)) .isInstanceOf(IllegalArgumentException.class) @@ -170,9 +173,33 @@ void invalidConfig() { .isInstanceOf(NullPointerException.class) .hasMessage("sender"); - assertThatThrownBy(() -> ZipkinSpanExporter.builder().setEncoder(null)) + assertThatThrownBy( + () -> ZipkinSpanExporter.builder().setEncoder((zipkin2.codec.BytesEncoder) null)) .isInstanceOf(NullPointerException.class) .hasMessage("encoder"); + + assertThatThrownBy(() -> ZipkinSpanExporter.builder().setEncoder((BytesEncoder) null)) + .isInstanceOf(NullPointerException.class) + .hasMessage("encoder"); + } + + @Test + void encoderProtobuf() { + @SuppressWarnings("deprecation") // we have to use the deprecated setEncoderto test it + ZipkinSpanExporter exporter = + ZipkinSpanExporter.builder().setEncoder(zipkin2.codec.SpanBytesEncoder.PROTO3).build(); + try { + assertThat(exporter).extracting("encoder.encoding").isEqualTo(Encoding.PROTO3); + } finally { + exporter.shutdown(); + } + + exporter = ZipkinSpanExporter.builder().setEncoder(SpanBytesEncoder.PROTO3).build(); + try { + assertThat(exporter).extracting("encoder").isEqualTo(SpanBytesEncoder.PROTO3); + } finally { + exporter.shutdown(); + } } @Test diff --git a/integration-tests/graal/build.gradle.kts b/integration-tests/graal/build.gradle.kts index cb39297a76a..090be76a2f5 100644 --- a/integration-tests/graal/build.gradle.kts +++ b/integration-tests/graal/build.gradle.kts @@ -16,7 +16,9 @@ sourceSets { } dependencies { - implementation(project(path = ":sdk:trace-shaded-deps")) + implementation(project(":sdk:all")) + implementation(project(":sdk:trace-shaded-deps")) + implementation(project(":exporters:otlp:all")) } // org.graalvm.buildtools.native pluging requires java 11+ as of version 0.9.26 diff --git a/integration-tests/graal/src/test/java/io/opentelemetry/integrationtests/graal/InitializeSdkTest.java b/integration-tests/graal/src/test/java/io/opentelemetry/integrationtests/graal/InitializeSdkTest.java new file mode 100644 index 00000000000..8826db405de --- /dev/null +++ b/integration-tests/graal/src/test/java/io/opentelemetry/integrationtests/graal/InitializeSdkTest.java @@ -0,0 +1,53 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.integrationtests.graal; + +import static org.assertj.core.api.Assertions.assertThatCode; + +import io.opentelemetry.exporter.otlp.logs.OtlpGrpcLogRecordExporter; +import io.opentelemetry.exporter.otlp.metrics.OtlpGrpcMetricExporter; +import io.opentelemetry.exporter.otlp.trace.OtlpGrpcSpanExporter; +import io.opentelemetry.sdk.OpenTelemetrySdk; +import io.opentelemetry.sdk.logs.SdkLoggerProvider; +import io.opentelemetry.sdk.logs.export.BatchLogRecordProcessor; +import io.opentelemetry.sdk.metrics.SdkMeterProvider; +import io.opentelemetry.sdk.metrics.export.PeriodicMetricReader; +import io.opentelemetry.sdk.trace.SdkTracerProvider; +import io.opentelemetry.sdk.trace.export.BatchSpanProcessor; +import org.junit.jupiter.api.Test; + +class InitializeSdkTest { + + @Test + void initializeSdk() { + assertThatCode( + () -> { + OpenTelemetrySdk sdk = + OpenTelemetrySdk.builder() + .setTracerProvider( + SdkTracerProvider.builder() + .addSpanProcessor( + BatchSpanProcessor.builder(OtlpGrpcSpanExporter.getDefault()) + .build()) + .build()) + .setMeterProvider( + SdkMeterProvider.builder() + .registerMetricReader( + PeriodicMetricReader.create(OtlpGrpcMetricExporter.getDefault())) + .build()) + .setLoggerProvider( + SdkLoggerProvider.builder() + .addLogRecordProcessor( + BatchLogRecordProcessor.builder( + OtlpGrpcLogRecordExporter.getDefault()) + .build()) + .build()) + .build(); + sdk.close(); + }) + .doesNotThrowAnyException(); + } +} diff --git a/sdk-extensions/autoconfigure/build.gradle.kts b/sdk-extensions/autoconfigure/build.gradle.kts index cc47e419045..62fcd639c79 100644 --- a/sdk-extensions/autoconfigure/build.gradle.kts +++ b/sdk-extensions/autoconfigure/build.gradle.kts @@ -49,7 +49,6 @@ testing { dependencies { implementation(project(":api:events")) implementation(project(":extensions:trace-propagators")) - implementation(project(":exporters:jaeger")) implementation(project(":exporters:logging")) implementation(project(":exporters:logging-otlp")) implementation(project(":exporters:otlp:all")) diff --git a/sdk-extensions/autoconfigure/src/testFullConfig/java/io/opentelemetry/sdk/autoconfigure/SpanExporterConfigurationTest.java b/sdk-extensions/autoconfigure/src/testFullConfig/java/io/opentelemetry/sdk/autoconfigure/SpanExporterConfigurationTest.java index 86e6bf24354..340d322dfc8 100644 --- a/sdk-extensions/autoconfigure/src/testFullConfig/java/io/opentelemetry/sdk/autoconfigure/SpanExporterConfigurationTest.java +++ b/sdk-extensions/autoconfigure/src/testFullConfig/java/io/opentelemetry/sdk/autoconfigure/SpanExporterConfigurationTest.java @@ -28,14 +28,11 @@ class SpanExporterConfigurationTest { SpiHelper.create(SpanExporterConfigurationTest.class.getClassLoader()); @Test - @SuppressWarnings("deprecation") // Testing deprecated jaeger exporter void configureExporter_KnownSpiExportersOnClasspath() { NamedSpiManager spiExportersManager = SpanExporterConfiguration.spanExporterSpiManager( DefaultConfigProperties.createFromMap(Collections.emptyMap()), spiHelper); - assertThat(SpanExporterConfiguration.configureExporter("jaeger", spiExportersManager)) - .isInstanceOf(io.opentelemetry.exporter.jaeger.JaegerGrpcSpanExporter.class); assertThat(SpanExporterConfiguration.configureExporter("logging", spiExportersManager)) .isInstanceOf(LoggingSpanExporter.class); assertThat(SpanExporterConfiguration.configureExporter("logging-otlp", spiExportersManager)) diff --git a/sdk/common/src/main/java/io/opentelemetry/sdk/internal/DynamicPrimitiveLongList.java b/sdk/common/src/main/java/io/opentelemetry/sdk/internal/DynamicPrimitiveLongList.java new file mode 100644 index 00000000000..e7cf95d9e29 --- /dev/null +++ b/sdk/common/src/main/java/io/opentelemetry/sdk/internal/DynamicPrimitiveLongList.java @@ -0,0 +1,146 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.sdk.internal; + +import java.util.AbstractList; + +/** + * A resizable list for storing primitive `long` values. + * + *

This class implements a dynamically resizable list specifically for primitive long values. The + * values are stored in a chain of arrays (named sub-array), so it can grow efficiently, by adding + * more sub-arrays per its defined size. The backing array also helps avoid auto-boxing and helps + * provide access to values as primitives without boxing. + * + *

The list is designed to minimize memory allocations, by: + * + *

    + *
  1. Adding sub-arrays and not creating new arrays and copying. + *
  2. When the size is changing to a smaller size, arrays are not removed. + *
+ * + *

Supported {@code List} methods: + * + *

    + *
  • {@link #get(int)} - Retrieves the element at the specified position in this list as a + * {@code Long} object. + *
  • {@link #set(int, Long)} - Replaces the element at the specified position in this list with + * the specified {@code Long} object. + *
  • {@link #size()} - Returns the number of elements in this list. + *
+ * + *

Additional utility methods: + * + *

    + *
  • {@link #getLong(int)} - Retrieves the element at the specified position in this list as a + * primitive long. + *
  • {@link #setLong(int, long)} - Replaces the element at the specified position in this list + * with the specified primitive long element. + *
  • {@link #resizeAndClear(int)} - Resizes the list to the specified size, resetting all + * elements to zero. + *
+ * + *

This class is an internal part of the OpenTelemetry SDK and is not intended for public use. + * Its API is unstable and subject to change. + * + *

This class is not thread-safe. + */ +public class DynamicPrimitiveLongList extends AbstractList { + + private static final int DEFAULT_SUBARRAY_CAPACITY = 10; + private final int subarrayCapacity; + private long[][] arrays; + private int size; + private int arrayCount; + + public static DynamicPrimitiveLongList of(long... values) { + DynamicPrimitiveLongList list = new DynamicPrimitiveLongList(); + list.resizeAndClear(values.length); + for (int i = 0; i < values.length; i++) { + list.setLong(i, values[i]); + } + return list; + } + + public static DynamicPrimitiveLongList empty() { + return new DynamicPrimitiveLongList(); + } + + DynamicPrimitiveLongList() { + this(DEFAULT_SUBARRAY_CAPACITY); + } + + DynamicPrimitiveLongList(int subarrayCapacity) { + if (subarrayCapacity <= 0) { + throw new IllegalArgumentException("Subarray capacity must be positive"); + } + this.subarrayCapacity = subarrayCapacity; + arrays = new long[0][subarrayCapacity]; + arrayCount = 0; + size = 0; + } + + @Override + public Long get(int index) { + return getLong(index); + } + + public long getLong(int index) { + rangeCheck(index); + return arrays[index / subarrayCapacity][index % subarrayCapacity]; + } + + @Override + public Long set(int index, Long element) { + return setLong(index, element); + } + + public long setLong(int index, long element) { + rangeCheck(index); + long oldValue = arrays[index / subarrayCapacity][index % subarrayCapacity]; + arrays[index / subarrayCapacity][index % subarrayCapacity] = element; + return oldValue; + } + + @Override + public int size() { + return size; + } + + public void resizeAndClear(int newSize) { + if (newSize < 0) { + throw new IllegalArgumentException("New size must be non-negative"); + } + ensureCapacity(newSize); + size = newSize; + for (int i = 0; i < newSize; i++) { + setLong(i, 0); + } + } + + private void ensureCapacity(int minCapacity) { + // A faster way to do ceil(minCapacity/subArrayCapacity) + int requiredArrays = (minCapacity + subarrayCapacity - 1) / subarrayCapacity; + + if (requiredArrays > arrayCount) { + arrays = java.util.Arrays.copyOf(arrays, /* newLength= */ requiredArrays); + for (int i = arrayCount; i < requiredArrays; i++) { + arrays[i] = new long[subarrayCapacity]; + } + arrayCount = requiredArrays; + } + } + + private void rangeCheck(int index) { + if (index < 0 || index >= size) { + throw new IndexOutOfBoundsException(outOfBoundsMsg(index)); + } + } + + private String outOfBoundsMsg(int index) { + return "Index: " + index + ", Size: " + size; + } +} diff --git a/sdk/common/src/test/java/io/opentelemetry/sdk/internal/DynamicPrimitiveLongListTest.java b/sdk/common/src/test/java/io/opentelemetry/sdk/internal/DynamicPrimitiveLongListTest.java new file mode 100644 index 00000000000..a5f7bf50f2a --- /dev/null +++ b/sdk/common/src/test/java/io/opentelemetry/sdk/internal/DynamicPrimitiveLongListTest.java @@ -0,0 +1,132 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.sdk.internal; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import org.junit.jupiter.api.Test; + +class DynamicPrimitiveLongListTest { + + @Test + void subArrayCapacityMustBePositive() { + assertThatThrownBy( + () -> { + int subArrayCapacity = 0; + new DynamicPrimitiveLongList(subArrayCapacity); + }) + .isInstanceOf(IllegalArgumentException.class); + + assertThatThrownBy( + () -> { + int subArrayCapacity = -2; + new DynamicPrimitiveLongList(subArrayCapacity); + }) + .isInstanceOf(IllegalArgumentException.class); + } + + @Test + void newListIsEmpty() { + DynamicPrimitiveLongList list = new DynamicPrimitiveLongList(); + assertThat(list).isEmpty(); + assertThatThrownBy(() -> list.getLong(0)).isInstanceOf(IndexOutOfBoundsException.class); + } + + @Test + void resizeListAndSetElement() { + DynamicPrimitiveLongList list = new DynamicPrimitiveLongList(); + list.resizeAndClear(5); + list.setLong(3, 10L); + + for (int i = 0; i < 5; i++) { + if (i == 3) { + assertThat(list.getLong(i)).isEqualTo(10L); + } else { + assertThat(list.getLong(i)).isEqualTo(0L); + } + } + } + + @Test + void resizeAndFillThenResizeSmallerAndCheck() { + DynamicPrimitiveLongList list = new DynamicPrimitiveLongList(); + list.resizeAndClear(6); + + for (int i = 0; i < 6; i++) { + list.setLong(i, i + 1); + } + + list.resizeAndClear(3); + + for (int i = 0; i < 3; i++) { + assertThat(list.getLong(i)).isEqualTo(0L); + } + + assertThatThrownBy(() -> list.getLong(4)).isInstanceOf(IndexOutOfBoundsException.class); + + for (int i = 0; i < 3; i++) { + list.setLong(i, i + 10); + assertThat(list.getLong(i)).isEqualTo(i + 10); + } + } + + @Test + void resizeToNegativeNumber() { + assertThatThrownBy(() -> DynamicPrimitiveLongList.of(0, 10, 20).resizeAndClear(-2)) + .isInstanceOf(IllegalArgumentException.class); + } + + @Test + void resizeAndFillThenResizeLargerAndCheck() { + DynamicPrimitiveLongList list = new DynamicPrimitiveLongList(); + list.resizeAndClear(6); + + for (int i = 0; i < 6; i++) { + list.setLong(i, i + 1); + } + + list.resizeAndClear(8); + + for (int i = 0; i < 8; i++) { + assertThat(list.getLong(i)).isEqualTo(0L); + } + + assertThatThrownBy(() -> list.getLong(8)).isInstanceOf(IndexOutOfBoundsException.class); + } + + @Test + void of() { + DynamicPrimitiveLongList list = DynamicPrimitiveLongList.of(1, 4, 5, 6); + assertThat(list.getLong(0)).isEqualTo(1); + assertThat(list.getLong(1)).isEqualTo(4); + assertThat(list.getLong(2)).isEqualTo(5); + assertThat(list.getLong(3)).isEqualTo(6); + + list = DynamicPrimitiveLongList.of(); + assertThat(list).isEmpty(); + } + + @Test + void empty() { + DynamicPrimitiveLongList list = DynamicPrimitiveLongList.empty(); + assertThat(list).isEmpty(); + + // I can still add elements + list.resizeAndClear(1); + list.set(0, 10L); + assertThat(list.getLong(0)).isEqualTo(10L); + } + + @Test + void set() { + DynamicPrimitiveLongList list = DynamicPrimitiveLongList.of(0, 10, 20); + assertThat(list.get(1)).isEqualTo(10L); + + list.set(1, 100L); + assertThat(list.get(1)).isEqualTo(100L); + } +} diff --git a/sdk/metrics/src/jmh/java/io/opentelemetry/sdk/metrics/internal/aggregator/HistogramAggregationParam.java b/sdk/metrics/src/jmh/java/io/opentelemetry/sdk/metrics/internal/aggregator/HistogramAggregationParam.java index ddece4dbee2..efb8bfdc89c 100644 --- a/sdk/metrics/src/jmh/java/io/opentelemetry/sdk/metrics/internal/aggregator/HistogramAggregationParam.java +++ b/sdk/metrics/src/jmh/java/io/opentelemetry/sdk/metrics/internal/aggregator/HistogramAggregationParam.java @@ -5,6 +5,8 @@ package io.opentelemetry.sdk.metrics.internal.aggregator; +import static io.opentelemetry.sdk.common.export.MemoryMode.IMMUTABLE_DATA; + import io.opentelemetry.sdk.metrics.internal.exemplar.ExemplarReservoir; import java.util.Collections; @@ -21,9 +23,11 @@ public enum HistogramAggregationParam { ExplicitBucketHistogramUtils.createBoundaryArray(Collections.emptyList()), ExemplarReservoir::doubleNoSamples)), EXPONENTIAL_SMALL_CIRCULAR_BUFFER( - new DoubleBase2ExponentialHistogramAggregator(ExemplarReservoir::doubleNoSamples, 20, 0)), + new DoubleBase2ExponentialHistogramAggregator( + ExemplarReservoir::doubleNoSamples, 20, 0, IMMUTABLE_DATA)), EXPONENTIAL_CIRCULAR_BUFFER( - new DoubleBase2ExponentialHistogramAggregator(ExemplarReservoir::doubleNoSamples, 160, 0)); + new DoubleBase2ExponentialHistogramAggregator( + ExemplarReservoir::doubleNoSamples, 160, 0, IMMUTABLE_DATA)); private final Aggregator aggregator; diff --git a/sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/internal/aggregator/AggregatorFactory.java b/sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/internal/aggregator/AggregatorFactory.java index 9802256b3b1..071df0fca81 100644 --- a/sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/internal/aggregator/AggregatorFactory.java +++ b/sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/internal/aggregator/AggregatorFactory.java @@ -5,6 +5,7 @@ package io.opentelemetry.sdk.metrics.internal.aggregator; +import io.opentelemetry.sdk.common.export.MemoryMode; import io.opentelemetry.sdk.metrics.data.ExemplarData; import io.opentelemetry.sdk.metrics.data.PointData; import io.opentelemetry.sdk.metrics.internal.descriptor.InstrumentDescriptor; @@ -26,15 +27,18 @@ public interface AggregatorFactory { * @param instrumentDescriptor the descriptor of the {@code Instrument} that will record * measurements. * @param exemplarFilter the filter on which measurements should turn into exemplars + * @param memoryMode The {@link MemoryMode} the aggregator will use * @return a new {@link Aggregator}. {@link Aggregator#drop()} indicates no measurements should be * recorded. */ Aggregator createAggregator( - InstrumentDescriptor instrumentDescriptor, ExemplarFilter exemplarFilter); + InstrumentDescriptor instrumentDescriptor, + ExemplarFilter exemplarFilter, + MemoryMode memoryMode); /** * Determine if the {@link Aggregator} produced by {@link #createAggregator(InstrumentDescriptor, - * ExemplarFilter)} is compatible with the {@code instrumentDescriptor}. + * ExemplarFilter, MemoryMode)} is compatible with the {@code instrumentDescriptor}. */ boolean isCompatibleWithInstrument(InstrumentDescriptor instrumentDescriptor); } diff --git a/sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/internal/aggregator/DoubleBase2ExponentialHistogramAggregator.java b/sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/internal/aggregator/DoubleBase2ExponentialHistogramAggregator.java index 35f8289441e..18b5b60ee4b 100644 --- a/sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/internal/aggregator/DoubleBase2ExponentialHistogramAggregator.java +++ b/sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/internal/aggregator/DoubleBase2ExponentialHistogramAggregator.java @@ -5,25 +5,26 @@ package io.opentelemetry.sdk.metrics.internal.aggregator; -import com.google.auto.value.AutoValue; import io.opentelemetry.api.common.Attributes; import io.opentelemetry.sdk.common.InstrumentationScopeInfo; +import io.opentelemetry.sdk.common.export.MemoryMode; +import io.opentelemetry.sdk.internal.DynamicPrimitiveLongList; import io.opentelemetry.sdk.metrics.data.AggregationTemporality; import io.opentelemetry.sdk.metrics.data.DoubleExemplarData; import io.opentelemetry.sdk.metrics.data.ExponentialHistogramBuckets; import io.opentelemetry.sdk.metrics.data.ExponentialHistogramPointData; import io.opentelemetry.sdk.metrics.data.MetricData; +import io.opentelemetry.sdk.metrics.internal.data.EmptyExponentialHistogramBuckets; import io.opentelemetry.sdk.metrics.internal.data.ImmutableExponentialHistogramData; import io.opentelemetry.sdk.metrics.internal.data.ImmutableExponentialHistogramPointData; import io.opentelemetry.sdk.metrics.internal.data.ImmutableMetricData; +import io.opentelemetry.sdk.metrics.internal.data.MutableExponentialHistogramBuckets; +import io.opentelemetry.sdk.metrics.internal.data.MutableExponentialHistogramPointData; import io.opentelemetry.sdk.metrics.internal.descriptor.MetricDescriptor; import io.opentelemetry.sdk.metrics.internal.exemplar.ExemplarReservoir; import io.opentelemetry.sdk.resources.Resource; import java.util.Collection; -import java.util.Collections; import java.util.List; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; import java.util.function.Supplier; import javax.annotation.Nullable; @@ -39,6 +40,7 @@ public final class DoubleBase2ExponentialHistogramAggregator private final Supplier> reservoirSupplier; private final int maxBuckets; private final int maxScale; + private final MemoryMode memoryMode; /** * Constructs an exponential histogram aggregator. @@ -48,15 +50,17 @@ public final class DoubleBase2ExponentialHistogramAggregator public DoubleBase2ExponentialHistogramAggregator( Supplier> reservoirSupplier, int maxBuckets, - int maxScale) { + int maxScale, + MemoryMode memoryMode) { this.reservoirSupplier = reservoirSupplier; this.maxBuckets = maxBuckets; this.maxScale = maxScale; + this.memoryMode = memoryMode; } @Override public AggregatorHandle createHandle() { - return new Handle(reservoirSupplier.get(), maxBuckets, maxScale); + return new Handle(reservoirSupplier.get(), maxBuckets, maxScale, memoryMode); } @Override @@ -87,8 +91,16 @@ static final class Handle private double max; private long count; private int currentScale; + private final MemoryMode memoryMode; - Handle(ExemplarReservoir reservoir, int maxBuckets, int maxScale) { + // Used only when MemoryMode = REUSABLE_DATA + @Nullable private final MutableExponentialHistogramPointData reusablePoint; + + Handle( + ExemplarReservoir reservoir, + int maxBuckets, + int maxScale, + MemoryMode memoryMode) { super(reservoir); this.maxBuckets = maxBuckets; this.maxScale = maxScale; @@ -98,6 +110,11 @@ static final class Handle this.max = -1; this.count = 0; this.currentScale = maxScale; + this.reusablePoint = + (memoryMode == MemoryMode.REUSABLE_DATA) + ? new MutableExponentialHistogramPointData() + : null; + this.memoryMode = memoryMode; } @Override @@ -107,21 +124,46 @@ protected synchronized ExponentialHistogramPointData doAggregateThenMaybeReset( Attributes attributes, List exemplars, boolean reset) { - ExponentialHistogramPointData point = - ImmutableExponentialHistogramPointData.create( - currentScale, - sum, - zeroCount, - this.count > 0, - this.min, - this.count > 0, - this.max, - resolveBuckets(this.positiveBuckets, currentScale, reset), - resolveBuckets(this.negativeBuckets, currentScale, reset), - startEpochNanos, - epochNanos, - attributes, - exemplars); + + ExponentialHistogramPointData point; + if (reusablePoint == null) { + point = + ImmutableExponentialHistogramPointData.create( + currentScale, + sum, + zeroCount, + this.count > 0, + this.min, + this.count > 0, + this.max, + resolveBuckets( + this.positiveBuckets, currentScale, reset, /* reusableBuckets= */ null), + resolveBuckets( + this.negativeBuckets, currentScale, reset, /* reusableBuckets= */ null), + startEpochNanos, + epochNanos, + attributes, + exemplars); + } else /* REUSABLE_DATA */ { + point = + reusablePoint.set( + currentScale, + sum, + zeroCount, + this.count > 0, + this.min, + this.count > 0, + this.max, + resolveBuckets( + this.positiveBuckets, currentScale, reset, reusablePoint.getPositiveBuckets()), + resolveBuckets( + this.negativeBuckets, currentScale, reset, reusablePoint.getNegativeBuckets()), + startEpochNanos, + epochNanos, + attributes, + exemplars); + } + if (reset) { this.sum = 0; this.zeroCount = 0; @@ -134,11 +176,38 @@ protected synchronized ExponentialHistogramPointData doAggregateThenMaybeReset( } private ExponentialHistogramBuckets resolveBuckets( - @Nullable DoubleBase2ExponentialHistogramBuckets buckets, int scale, boolean reset) { + @Nullable DoubleBase2ExponentialHistogramBuckets buckets, + int scale, + boolean reset, + @Nullable ExponentialHistogramBuckets reusableBuckets) { if (buckets == null) { return EmptyExponentialHistogramBuckets.get(scale); } - ExponentialHistogramBuckets copy = buckets.copy(); + + ExponentialHistogramBuckets copy; + if (reusableBuckets == null) { + copy = buckets.copy(); + } else { + MutableExponentialHistogramBuckets mutableExponentialHistogramBuckets; + if (reusableBuckets instanceof MutableExponentialHistogramBuckets) { + mutableExponentialHistogramBuckets = (MutableExponentialHistogramBuckets) reusableBuckets; + } else /* EmptyExponentialHistogramBuckets */ { + mutableExponentialHistogramBuckets = new MutableExponentialHistogramBuckets(); + } + + DynamicPrimitiveLongList reusableBucketCountsList = + mutableExponentialHistogramBuckets.getReusableBucketCountsList(); + buckets.getBucketCountsIntoReusableList(reusableBucketCountsList); + + mutableExponentialHistogramBuckets.set( + buckets.getScale(), + buckets.getOffset(), + buckets.getTotalCount(), + reusableBucketCountsList); + + copy = mutableExponentialHistogramBuckets; + } + if (reset) { buckets.clear(maxScale); } @@ -166,13 +235,15 @@ protected synchronized void doRecordDouble(double value) { } else if (c > 0) { // Initialize positive buckets at current scale, if needed if (positiveBuckets == null) { - positiveBuckets = new DoubleBase2ExponentialHistogramBuckets(currentScale, maxBuckets); + positiveBuckets = + new DoubleBase2ExponentialHistogramBuckets(currentScale, maxBuckets, memoryMode); } buckets = positiveBuckets; } else { // Initialize negative buckets at current scale, if needed if (negativeBuckets == null) { - negativeBuckets = new DoubleBase2ExponentialHistogramBuckets(currentScale, maxBuckets); + negativeBuckets = + new DoubleBase2ExponentialHistogramBuckets(currentScale, maxBuckets, memoryMode); } buckets = negativeBuckets; } @@ -206,21 +277,4 @@ void downScale(int by) { } } } - - @AutoValue - abstract static class EmptyExponentialHistogramBuckets implements ExponentialHistogramBuckets { - - private static final Map ZERO_BUCKETS = - new ConcurrentHashMap<>(); - - EmptyExponentialHistogramBuckets() {} - - static ExponentialHistogramBuckets get(int scale) { - return ZERO_BUCKETS.computeIfAbsent( - scale, - scale1 -> - new AutoValue_DoubleBase2ExponentialHistogramAggregator_EmptyExponentialHistogramBuckets( - scale1, 0, Collections.emptyList(), 0)); - } - } } diff --git a/sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/internal/aggregator/DoubleBase2ExponentialHistogramBuckets.java b/sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/internal/aggregator/DoubleBase2ExponentialHistogramBuckets.java index 0b66fd74a85..94659d0ecc4 100644 --- a/sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/internal/aggregator/DoubleBase2ExponentialHistogramBuckets.java +++ b/sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/internal/aggregator/DoubleBase2ExponentialHistogramBuckets.java @@ -5,6 +5,11 @@ package io.opentelemetry.sdk.metrics.internal.aggregator; +import static io.opentelemetry.sdk.common.export.MemoryMode.IMMUTABLE_DATA; +import static io.opentelemetry.sdk.common.export.MemoryMode.REUSABLE_DATA; + +import io.opentelemetry.sdk.common.export.MemoryMode; +import io.opentelemetry.sdk.internal.DynamicPrimitiveLongList; import io.opentelemetry.sdk.internal.PrimitiveLongList; import io.opentelemetry.sdk.metrics.data.ExponentialHistogramBuckets; import java.util.Collections; @@ -20,12 +25,17 @@ */ final class DoubleBase2ExponentialHistogramBuckets implements ExponentialHistogramBuckets { + private final MemoryMode memoryMode; private AdaptingCircularBufferCounter counts; private int scale; private Base2ExponentialHistogramIndexer base2ExponentialHistogramIndexer; private long totalCount; - DoubleBase2ExponentialHistogramBuckets(int scale, int maxBuckets) { + // Only used when memory mode is REUSABLE_DATA + @Nullable private AdaptingCircularBufferCounter reusableCounts; + + DoubleBase2ExponentialHistogramBuckets(int scale, int maxBuckets, MemoryMode memoryMode) { + this.memoryMode = memoryMode; this.counts = new AdaptingCircularBufferCounter(maxBuckets); this.scale = scale; this.base2ExponentialHistogramIndexer = Base2ExponentialHistogramIndexer.get(this.scale); @@ -38,6 +48,8 @@ final class DoubleBase2ExponentialHistogramBuckets implements ExponentialHistogr this.scale = buckets.scale; this.base2ExponentialHistogramIndexer = buckets.base2ExponentialHistogramIndexer; this.totalCount = buckets.totalCount; + this.memoryMode = buckets.memoryMode; + this.reusableCounts = buckets.reusableCounts; } /** Returns a copy of this bucket. */ @@ -90,6 +102,31 @@ public List getBucketCounts() { return PrimitiveLongList.wrap(countsArr); } + /** + * Fills the given reusable list with the bucket counts. + * + *

NOTE: This is the same as {@link #getBucketCounts()} but instead of returning a List with + * the values is fill the values into {@code reusableLongList} + * + * @param reusableLongList The list to fill with the bucket counts + */ + void getBucketCountsIntoReusableList(DynamicPrimitiveLongList reusableLongList) { + if (counts.isEmpty()) { + reusableLongList.resizeAndClear(0); + return; + } + + int length = counts.getIndexEnd() - counts.getIndexStart() + 1; + + if (reusableLongList.size() != length) { + reusableLongList.resizeAndClear(length); + } + + for (int i = 0; i < length; i++) { + reusableLongList.setLong(i, counts.get(i + counts.getIndexStart())); + } + } + @Override public long getTotalCount() { return totalCount; @@ -107,7 +144,16 @@ void downscale(int by) { // We want to preserve other optimisations here as well, e.g. integer size. // Instead of creating a new counter, we copy the existing one (for bucket size // optimisations), and clear the values before writing the new ones. - AdaptingCircularBufferCounter newCounts = new AdaptingCircularBufferCounter(counts); + AdaptingCircularBufferCounter newCounts; + if (memoryMode == IMMUTABLE_DATA) { + newCounts = new AdaptingCircularBufferCounter(counts); + } else { + if (reusableCounts == null) { + reusableCounts = new AdaptingCircularBufferCounter(counts); + } + newCounts = reusableCounts; + } + newCounts.clear(); for (int i = counts.getIndexStart(); i <= counts.getIndexEnd(); i++) { @@ -119,7 +165,14 @@ void downscale(int by) { } } } - this.counts = newCounts; + + if (memoryMode == REUSABLE_DATA) { + AdaptingCircularBufferCounter existingCounts = this.counts; + this.counts = newCounts; + reusableCounts = existingCounts; + } else { + this.counts = newCounts; + } } this.scale = this.scale - by; diff --git a/sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/internal/data/EmptyExponentialHistogramBuckets.java b/sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/internal/data/EmptyExponentialHistogramBuckets.java new file mode 100644 index 00000000000..afcbddb52c5 --- /dev/null +++ b/sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/internal/data/EmptyExponentialHistogramBuckets.java @@ -0,0 +1,34 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.sdk.metrics.internal.data; + +import com.google.auto.value.AutoValue; +import io.opentelemetry.sdk.metrics.data.ExponentialHistogramBuckets; +import java.util.Collections; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * An empty {@link ExponentialHistogramBuckets} + * + *

This class is internal and is hence not for public use. Its APIs are unstable and can change + * at any time. + */ +@AutoValue +public abstract class EmptyExponentialHistogramBuckets implements ExponentialHistogramBuckets { + + private static final Map ZERO_BUCKETS = + new ConcurrentHashMap<>(); + + EmptyExponentialHistogramBuckets() {} + + public static ExponentialHistogramBuckets get(int scale) { + return ZERO_BUCKETS.computeIfAbsent( + scale, + scale1 -> + new AutoValue_EmptyExponentialHistogramBuckets(scale1, 0, Collections.emptyList(), 0)); + } +} diff --git a/sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/internal/data/MutableExponentialHistogramBuckets.java b/sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/internal/data/MutableExponentialHistogramBuckets.java new file mode 100644 index 00000000000..9c9f815426d --- /dev/null +++ b/sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/internal/data/MutableExponentialHistogramBuckets.java @@ -0,0 +1,102 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.sdk.metrics.internal.data; + +import io.opentelemetry.sdk.internal.DynamicPrimitiveLongList; +import io.opentelemetry.sdk.metrics.data.ExponentialHistogramBuckets; +import java.util.List; +import java.util.Objects; + +/** + * A mutable {@link ExponentialHistogramBuckets} + * + *

This class is internal and is hence not for public use. Its APIs are unstable and can change + * at any time. + * + *

This class is not thread-safe. + */ +public final class MutableExponentialHistogramBuckets implements ExponentialHistogramBuckets { + + private int scale; + private int offset; + private long totalCount; + private DynamicPrimitiveLongList bucketCounts = DynamicPrimitiveLongList.empty(); + + @Override + public int getScale() { + return scale; + } + + @Override + public int getOffset() { + return offset; + } + + @Override + public List getBucketCounts() { + return bucketCounts; + } + + public DynamicPrimitiveLongList getReusableBucketCountsList() { + return bucketCounts; + } + + @Override + public long getTotalCount() { + return totalCount; + } + + public MutableExponentialHistogramBuckets set( + int scale, int offset, long totalCount, DynamicPrimitiveLongList bucketCounts) { + this.scale = scale; + this.offset = offset; + this.totalCount = totalCount; + this.bucketCounts = bucketCounts; + + return this; + } + + @Override + public String toString() { + return "MutableExponentialHistogramBuckets{" + + "scale=" + + scale + + ", " + + "offset=" + + offset + + ", " + + "bucketCounts=" + + bucketCounts + + ", " + + "totalCount=" + + totalCount + + "}"; + } + + @Override + public boolean equals(Object o) { + if (o == this) { + return true; + } + if (o instanceof MutableExponentialHistogramBuckets) { + MutableExponentialHistogramBuckets that = (MutableExponentialHistogramBuckets) o; + return this.scale == that.getScale() + && this.offset == that.getOffset() + && this.totalCount == that.getTotalCount() + && Objects.equals(this.bucketCounts, that.bucketCounts); + } + return false; + } + + @Override + public int hashCode() { + int result = scale; + result = 31 * result + offset; + result = 31 * result + (int) (totalCount ^ (totalCount >>> 32)); + result = 31 * result + (bucketCounts != null ? bucketCounts.hashCode() : 0); + return result; + } +} diff --git a/sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/internal/data/MutableExponentialHistogramPointData.java b/sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/internal/data/MutableExponentialHistogramPointData.java new file mode 100644 index 00000000000..c32565af087 --- /dev/null +++ b/sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/internal/data/MutableExponentialHistogramPointData.java @@ -0,0 +1,248 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.sdk.metrics.internal.data; + +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.sdk.metrics.data.DoubleExemplarData; +import io.opentelemetry.sdk.metrics.data.ExponentialHistogramBuckets; +import io.opentelemetry.sdk.metrics.data.ExponentialHistogramPointData; +import java.util.Collections; +import java.util.List; + +/** + * A mutable {@link ExponentialHistogramPointData} + * + *

This class is internal and is hence not for public use. Its APIs are unstable and can change + * at any time. + * + *

This class is not thread-safe. + */ +public final class MutableExponentialHistogramPointData implements ExponentialHistogramPointData { + + private long startEpochNanos; + private long epochNanos; + private Attributes attributes = Attributes.empty(); + private int scale; + private double sum; + private long count; + private long zeroCount; + private boolean hasMin; + private double min; + private boolean hasMax; + private double max; + private ExponentialHistogramBuckets positiveBuckets = EmptyExponentialHistogramBuckets.get(0); + private ExponentialHistogramBuckets negativeBuckets = EmptyExponentialHistogramBuckets.get(0); + private List exemplars = Collections.emptyList(); + + @Override + public int getScale() { + return scale; + } + + @Override + public double getSum() { + return sum; + } + + @Override + public long getCount() { + return count; + } + + @Override + public long getZeroCount() { + return zeroCount; + } + + @Override + public boolean hasMin() { + return hasMin; + } + + @Override + public double getMin() { + return min; + } + + @Override + public boolean hasMax() { + return hasMax; + } + + @Override + public double getMax() { + return max; + } + + @Override + public ExponentialHistogramBuckets getPositiveBuckets() { + return positiveBuckets; + } + + @Override + public ExponentialHistogramBuckets getNegativeBuckets() { + return negativeBuckets; + } + + @Override + public long getStartEpochNanos() { + return startEpochNanos; + } + + @Override + public long getEpochNanos() { + return epochNanos; + } + + @Override + public Attributes getAttributes() { + return attributes; + } + + @Override + public List getExemplars() { + return exemplars; + } + + @SuppressWarnings("TooManyParameters") + public ExponentialHistogramPointData set( + int scale, + double sum, + long zeroCount, + boolean hasMin, + double min, + boolean hasMax, + double max, + ExponentialHistogramBuckets positiveBuckets, + ExponentialHistogramBuckets negativeBuckets, + long startEpochNanos, + long epochNanos, + Attributes attributes, + List exemplars) { + this.count = zeroCount + positiveBuckets.getTotalCount() + negativeBuckets.getTotalCount(); + this.scale = scale; + this.sum = sum; + this.zeroCount = zeroCount; + this.hasMin = hasMin; + this.min = min; + this.hasMax = hasMax; + this.max = max; + this.positiveBuckets = positiveBuckets; + this.negativeBuckets = negativeBuckets; + this.startEpochNanos = startEpochNanos; + this.epochNanos = epochNanos; + this.attributes = attributes; + this.exemplars = exemplars; + + return this; + } + + @Override + public String toString() { + return "MutableExponentialHistogramPointData{" + + "startEpochNanos=" + + startEpochNanos + + ", " + + "epochNanos=" + + epochNanos + + ", " + + "attributes=" + + attributes + + ", " + + "scale=" + + scale + + ", " + + "sum=" + + sum + + ", " + + "count=" + + count + + ", " + + "zeroCount=" + + zeroCount + + ", " + + "hasMin=" + + hasMin + + ", " + + "min=" + + min + + ", " + + "hasMax=" + + hasMax + + ", " + + "max=" + + max + + ", " + + "positiveBuckets=" + + positiveBuckets + + ", " + + "negativeBuckets=" + + negativeBuckets + + ", " + + "exemplars=" + + exemplars + + "}"; + } + + @Override + public boolean equals(Object o) { + if (o == this) { + return true; + } + if (o instanceof MutableExponentialHistogramPointData) { + MutableExponentialHistogramPointData that = (MutableExponentialHistogramPointData) o; + return this.startEpochNanos == that.startEpochNanos + && this.epochNanos == that.epochNanos + && this.attributes.equals(that.attributes) + && this.scale == that.scale + && Double.doubleToLongBits(this.sum) == Double.doubleToLongBits(that.sum) + && this.count == that.count + && this.zeroCount == that.zeroCount + && this.hasMin == that.hasMin + && Double.doubleToLongBits(this.min) == Double.doubleToLongBits(that.min) + && this.hasMax == that.hasMax + && Double.doubleToLongBits(this.max) == Double.doubleToLongBits(that.max) + && this.positiveBuckets.equals(that.positiveBuckets) + && this.negativeBuckets.equals(that.negativeBuckets) + && this.exemplars.equals(that.exemplars); + } + return false; + } + + @Override + public int hashCode() { + int hash = 1; + hash *= 1000003; + hash ^= (int) ((startEpochNanos >>> 32) ^ startEpochNanos); + hash *= 1000003; + hash ^= (int) ((epochNanos >>> 32) ^ epochNanos); + hash *= 1000003; + hash ^= attributes.hashCode(); + hash *= 1000003; + hash ^= scale; + hash *= 1000003; + hash ^= (int) ((Double.doubleToLongBits(sum) >>> 32) ^ Double.doubleToLongBits(sum)); + hash *= 1000003; + hash ^= (int) ((count >>> 32) ^ count); + hash *= 1000003; + hash ^= (int) ((zeroCount >>> 32) ^ zeroCount); + hash *= 1000003; + hash ^= hasMin ? 1231 : 1237; + hash *= 1000003; + hash ^= (int) ((Double.doubleToLongBits(min) >>> 32) ^ Double.doubleToLongBits(min)); + hash *= 1000003; + hash ^= hasMax ? 1231 : 1237; + hash *= 1000003; + hash ^= (int) ((Double.doubleToLongBits(max) >>> 32) ^ Double.doubleToLongBits(max)); + hash *= 1000003; + hash ^= positiveBuckets.hashCode(); + hash *= 1000003; + hash ^= negativeBuckets.hashCode(); + hash *= 1000003; + hash ^= exemplars.hashCode(); + return hash; + } +} diff --git a/exporters/jaeger-thrift/src/main/java/io/opentelemetry/exporter/jaeger/thrift/package-info.java b/sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/internal/package-info.java similarity index 65% rename from exporters/jaeger-thrift/src/main/java/io/opentelemetry/exporter/jaeger/thrift/package-info.java rename to sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/internal/package-info.java index 1173e8d8452..13b7ee33253 100644 --- a/exporters/jaeger-thrift/src/main/java/io/opentelemetry/exporter/jaeger/thrift/package-info.java +++ b/sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/internal/package-info.java @@ -3,7 +3,8 @@ * SPDX-License-Identifier: Apache-2.0 */ +/** Internal SDK implementation classes. */ @ParametersAreNonnullByDefault -package io.opentelemetry.exporter.jaeger.thrift; +package io.opentelemetry.sdk.metrics.internal; import javax.annotation.ParametersAreNonnullByDefault; diff --git a/sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/internal/state/AsynchronousMetricStorage.java b/sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/internal/state/AsynchronousMetricStorage.java index 4da3d653207..328710144da 100644 --- a/sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/internal/state/AsynchronousMetricStorage.java +++ b/sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/internal/state/AsynchronousMetricStorage.java @@ -110,7 +110,10 @@ static AsynchronousMetricStorage aggregator = ((AggregatorFactory) view.getAggregation()) - .createAggregator(instrumentDescriptor, ExemplarFilter.alwaysOff()); + .createAggregator( + instrumentDescriptor, + ExemplarFilter.alwaysOff(), + registeredReader.getReader().getMemoryMode()); return new AsynchronousMetricStorage<>( registeredReader, metricDescriptor, diff --git a/sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/internal/state/SynchronousMetricStorage.java b/sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/internal/state/SynchronousMetricStorage.java index 3b821367343..f743c26cbc7 100644 --- a/sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/internal/state/SynchronousMetricStorage.java +++ b/sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/internal/state/SynchronousMetricStorage.java @@ -46,7 +46,8 @@ static SynchronousMetricStorage cr MetricDescriptor.create(view, registeredView.getViewSourceInfo(), instrumentDescriptor); Aggregator aggregator = ((AggregatorFactory) view.getAggregation()) - .createAggregator(instrumentDescriptor, exemplarFilter); + .createAggregator( + instrumentDescriptor, exemplarFilter, registeredReader.getReader().getMemoryMode()); // We won't be storing this metric. if (Aggregator.drop() == aggregator) { return empty(); diff --git a/sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/internal/view/Base2ExponentialHistogramAggregation.java b/sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/internal/view/Base2ExponentialHistogramAggregation.java index 287ff589ea6..a4facefca7a 100644 --- a/sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/internal/view/Base2ExponentialHistogramAggregation.java +++ b/sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/internal/view/Base2ExponentialHistogramAggregation.java @@ -8,6 +8,7 @@ import static io.opentelemetry.api.internal.Utils.checkArgument; import io.opentelemetry.sdk.common.Clock; +import io.opentelemetry.sdk.common.export.MemoryMode; import io.opentelemetry.sdk.internal.RandomSupplier; import io.opentelemetry.sdk.metrics.Aggregation; import io.opentelemetry.sdk.metrics.data.ExemplarData; @@ -66,7 +67,9 @@ public static Aggregation create(int maxBuckets, int maxScale) { @Override @SuppressWarnings("unchecked") public Aggregator createAggregator( - InstrumentDescriptor instrumentDescriptor, ExemplarFilter exemplarFilter) { + InstrumentDescriptor instrumentDescriptor, + ExemplarFilter exemplarFilter, + MemoryMode memoryMode) { return (Aggregator) new DoubleBase2ExponentialHistogramAggregator( () -> @@ -78,7 +81,8 @@ public Aggregator createAggr Runtime.getRuntime().availableProcessors(), RandomSupplier.platformDefault()))), maxBuckets, - maxScale); + maxScale, + memoryMode); } @Override diff --git a/sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/internal/view/DefaultAggregation.java b/sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/internal/view/DefaultAggregation.java index 798c9ea11b4..bbcecc08e1d 100644 --- a/sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/internal/view/DefaultAggregation.java +++ b/sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/internal/view/DefaultAggregation.java @@ -5,6 +5,7 @@ package io.opentelemetry.sdk.metrics.internal.view; +import io.opentelemetry.sdk.common.export.MemoryMode; import io.opentelemetry.sdk.internal.ThrottlingLogger; import io.opentelemetry.sdk.metrics.Aggregation; import io.opentelemetry.sdk.metrics.data.ExemplarData; @@ -57,9 +58,11 @@ private static Aggregation resolve(InstrumentDescriptor instrument, boolean with @Override public Aggregator createAggregator( - InstrumentDescriptor instrumentDescriptor, ExemplarFilter exemplarFilter) { + InstrumentDescriptor instrumentDescriptor, + ExemplarFilter exemplarFilter, + MemoryMode memoryMode) { return ((AggregatorFactory) resolve(instrumentDescriptor, /* withAdvice= */ true)) - .createAggregator(instrumentDescriptor, exemplarFilter); + .createAggregator(instrumentDescriptor, exemplarFilter, memoryMode); } @Override diff --git a/sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/internal/view/DropAggregation.java b/sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/internal/view/DropAggregation.java index df02ce003db..2b27938c20a 100644 --- a/sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/internal/view/DropAggregation.java +++ b/sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/internal/view/DropAggregation.java @@ -5,6 +5,7 @@ package io.opentelemetry.sdk.metrics.internal.view; +import io.opentelemetry.sdk.common.export.MemoryMode; import io.opentelemetry.sdk.metrics.Aggregation; import io.opentelemetry.sdk.metrics.data.ExemplarData; import io.opentelemetry.sdk.metrics.data.PointData; @@ -32,7 +33,9 @@ private DropAggregation() {} @Override @SuppressWarnings("unchecked") public Aggregator createAggregator( - InstrumentDescriptor instrumentDescriptor, ExemplarFilter exemplarFilter) { + InstrumentDescriptor instrumentDescriptor, + ExemplarFilter exemplarFilter, + MemoryMode memoryMode) { return (Aggregator) Aggregator.drop(); } diff --git a/sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/internal/view/ExplicitBucketHistogramAggregation.java b/sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/internal/view/ExplicitBucketHistogramAggregation.java index 1ad32b698ba..3d87878efa4 100644 --- a/sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/internal/view/ExplicitBucketHistogramAggregation.java +++ b/sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/internal/view/ExplicitBucketHistogramAggregation.java @@ -6,6 +6,7 @@ package io.opentelemetry.sdk.metrics.internal.view; import io.opentelemetry.sdk.common.Clock; +import io.opentelemetry.sdk.common.export.MemoryMode; import io.opentelemetry.sdk.metrics.Aggregation; import io.opentelemetry.sdk.metrics.data.ExemplarData; import io.opentelemetry.sdk.metrics.data.PointData; @@ -50,7 +51,9 @@ private ExplicitBucketHistogramAggregation(List bucketBoundaries) { @Override @SuppressWarnings("unchecked") public Aggregator createAggregator( - InstrumentDescriptor instrumentDescriptor, ExemplarFilter exemplarFilter) { + InstrumentDescriptor instrumentDescriptor, + ExemplarFilter exemplarFilter, + MemoryMode memoryMode) { return (Aggregator) new DoubleExplicitBucketHistogramAggregator( bucketBoundaryArray, diff --git a/sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/internal/view/LastValueAggregation.java b/sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/internal/view/LastValueAggregation.java index 5f6039532e0..5a7579f3bf6 100644 --- a/sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/internal/view/LastValueAggregation.java +++ b/sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/internal/view/LastValueAggregation.java @@ -5,6 +5,7 @@ package io.opentelemetry.sdk.metrics.internal.view; +import io.opentelemetry.sdk.common.export.MemoryMode; import io.opentelemetry.sdk.metrics.Aggregation; import io.opentelemetry.sdk.metrics.InstrumentType; import io.opentelemetry.sdk.metrics.data.ExemplarData; @@ -36,7 +37,9 @@ private LastValueAggregation() {} @Override @SuppressWarnings("unchecked") public Aggregator createAggregator( - InstrumentDescriptor instrumentDescriptor, ExemplarFilter exemplarFilter) { + InstrumentDescriptor instrumentDescriptor, + ExemplarFilter exemplarFilter, + MemoryMode memoryMode) { // For the initial version we do not sample exemplars on gauges. switch (instrumentDescriptor.getValueType()) { diff --git a/sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/internal/view/SumAggregation.java b/sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/internal/view/SumAggregation.java index b689d9f58f1..a23b0bc4d7e 100644 --- a/sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/internal/view/SumAggregation.java +++ b/sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/internal/view/SumAggregation.java @@ -6,6 +6,7 @@ package io.opentelemetry.sdk.metrics.internal.view; import io.opentelemetry.sdk.common.Clock; +import io.opentelemetry.sdk.common.export.MemoryMode; import io.opentelemetry.sdk.internal.RandomSupplier; import io.opentelemetry.sdk.metrics.Aggregation; import io.opentelemetry.sdk.metrics.data.DoubleExemplarData; @@ -39,7 +40,9 @@ private SumAggregation() {} @Override @SuppressWarnings("unchecked") public Aggregator createAggregator( - InstrumentDescriptor instrumentDescriptor, ExemplarFilter exemplarFilter) { + InstrumentDescriptor instrumentDescriptor, + ExemplarFilter exemplarFilter, + MemoryMode memoryMode) { switch (instrumentDescriptor.getValueType()) { case LONG: { diff --git a/sdk/metrics/src/test/java/io/opentelemetry/sdk/metrics/internal/aggregator/DoubleBase2ExponentialHistogramAggregatorTest.java b/sdk/metrics/src/test/java/io/opentelemetry/sdk/metrics/internal/aggregator/DoubleBase2ExponentialHistogramAggregatorTest.java index cc405c5d8b1..e774939dd4c 100644 --- a/sdk/metrics/src/test/java/io/opentelemetry/sdk/metrics/internal/aggregator/DoubleBase2ExponentialHistogramAggregatorTest.java +++ b/sdk/metrics/src/test/java/io/opentelemetry/sdk/metrics/internal/aggregator/DoubleBase2ExponentialHistogramAggregatorTest.java @@ -14,16 +14,22 @@ import io.opentelemetry.api.trace.TraceState; import io.opentelemetry.context.Context; import io.opentelemetry.sdk.common.InstrumentationScopeInfo; +import io.opentelemetry.sdk.common.export.MemoryMode; import io.opentelemetry.sdk.metrics.data.AggregationTemporality; import io.opentelemetry.sdk.metrics.data.DoubleExemplarData; import io.opentelemetry.sdk.metrics.data.ExponentialHistogramBuckets; import io.opentelemetry.sdk.metrics.data.ExponentialHistogramPointData; import io.opentelemetry.sdk.metrics.data.MetricData; import io.opentelemetry.sdk.metrics.data.MetricDataType; +import io.opentelemetry.sdk.metrics.internal.data.EmptyExponentialHistogramBuckets; import io.opentelemetry.sdk.metrics.internal.data.ImmutableDoubleExemplarData; +import io.opentelemetry.sdk.metrics.internal.data.ImmutableExponentialHistogramPointData; +import io.opentelemetry.sdk.metrics.internal.data.MutableExponentialHistogramBuckets; +import io.opentelemetry.sdk.metrics.internal.data.MutableExponentialHistogramPointData; import io.opentelemetry.sdk.metrics.internal.descriptor.MetricDescriptor; import io.opentelemetry.sdk.metrics.internal.exemplar.ExemplarReservoir; import io.opentelemetry.sdk.resources.Resource; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; @@ -38,6 +44,7 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EnumSource; import org.junit.jupiter.params.provider.MethodSource; import org.mockito.Mock; import org.mockito.Mockito; @@ -47,10 +54,9 @@ class DoubleBase2ExponentialHistogramAggregatorTest { @Mock ExemplarReservoir reservoir; + private DoubleBase2ExponentialHistogramAggregator aggregator; private static final int MAX_SCALE = 20; - private static final DoubleBase2ExponentialHistogramAggregator aggregator = - new DoubleBase2ExponentialHistogramAggregator(ExemplarReservoir::doubleNoSamples, 160, 20); private static final Resource RESOURCE = Resource.getDefault(); private static final InstrumentationScopeInfo INSTRUMENTATION_SCOPE_INFO = InstrumentationScopeInfo.empty(); @@ -58,10 +64,16 @@ class DoubleBase2ExponentialHistogramAggregatorTest { MetricDescriptor.create("name", "description", "unit"); private static Stream provideAggregator() { - return Stream.of( - aggregator, - new DoubleBase2ExponentialHistogramAggregator( - ExemplarReservoir::doubleNoSamples, 160, MAX_SCALE)); + List parameters = new ArrayList<>(); + for (MemoryMode memoryMode : MemoryMode.values()) { + parameters.add( + new DoubleBase2ExponentialHistogramAggregator( + ExemplarReservoir::doubleNoSamples, 160, 20, memoryMode)); + parameters.add( + new DoubleBase2ExponentialHistogramAggregator( + ExemplarReservoir::doubleNoSamples, 160, MAX_SCALE, memoryMode)); + } + return parameters.stream(); } private static int valueToIndex(int scale, double value) { @@ -69,26 +81,34 @@ private static int valueToIndex(int scale, double value) { return (int) Math.ceil(Math.log(value) * scaleFactor) - 1; } - @Test - void createHandle() { + private void initialize(MemoryMode memoryMode) { + aggregator = + new DoubleBase2ExponentialHistogramAggregator( + ExemplarReservoir::doubleNoSamples, 160, 20, memoryMode); + } + + @ParameterizedTest + @EnumSource(MemoryMode.class) + void createHandle(MemoryMode memoryMode) { + initialize(memoryMode); + AggregatorHandle handle = aggregator.createHandle(); assertThat(handle).isInstanceOf(DoubleBase2ExponentialHistogramAggregator.Handle.class); ExponentialHistogramPointData point = ((DoubleBase2ExponentialHistogramAggregator.Handle) handle) .doAggregateThenMaybeReset( 0, 1, Attributes.empty(), Collections.emptyList(), /* reset= */ true); - assertThat(point.getPositiveBuckets()) - .isInstanceOf( - DoubleBase2ExponentialHistogramAggregator.EmptyExponentialHistogramBuckets.class); + assertThat(point.getPositiveBuckets()).isInstanceOf(EmptyExponentialHistogramBuckets.class); assertThat(point.getPositiveBuckets().getScale()).isEqualTo(MAX_SCALE); - assertThat(point.getNegativeBuckets()) - .isInstanceOf( - DoubleBase2ExponentialHistogramAggregator.EmptyExponentialHistogramBuckets.class); + assertThat(point.getNegativeBuckets()).isInstanceOf(EmptyExponentialHistogramBuckets.class); assertThat(point.getNegativeBuckets().getScale()).isEqualTo(MAX_SCALE); } - @Test - void testRecordings() { + @ParameterizedTest + @EnumSource(MemoryMode.class) + void testRecordings(MemoryMode memoryMode) { + initialize(memoryMode); + AggregatorHandle aggregatorHandle = aggregator.createHandle(); aggregatorHandle.recordDouble(0.5); @@ -130,11 +150,14 @@ void testRecordings() { assertThat(negativeCounts.get(valueToIndex(expectedScale, 1.0) - negOffset)).isEqualTo(1); } - @Test - void testInvalidRecording() { + @ParameterizedTest + @EnumSource(MemoryMode.class) + void testInvalidRecording(MemoryMode memoryMode) { + initialize(memoryMode); + AggregatorHandle aggregatorHandle = aggregator.createHandle(); - // Non finite recordings should be ignored + // Non-finite recordings should be ignored aggregatorHandle.recordDouble(Double.POSITIVE_INFINITY); aggregatorHandle.recordDouble(Double.NEGATIVE_INFINITY); aggregatorHandle.recordDouble(Double.NaN); @@ -192,10 +215,11 @@ void testRecordingsAtLimits(DoubleBase2ExponentialHistogramAggregator aggregator .isEqualTo(Double.POSITIVE_INFINITY); } - @Test - void aggregateThenMaybeReset_WithExemplars() { + @ParameterizedTest + @EnumSource(MemoryMode.class) + void aggregateThenMaybeReset_WithExemplars(MemoryMode memoryMode) { DoubleBase2ExponentialHistogramAggregator agg = - new DoubleBase2ExponentialHistogramAggregator(() -> reservoir, 160, MAX_SCALE); + new DoubleBase2ExponentialHistogramAggregator(() -> reservoir, 160, MAX_SCALE, memoryMode); Attributes attributes = Attributes.builder().put("test", "value").build(); DoubleExemplarData exemplar = @@ -223,8 +247,11 @@ void aggregateThenMaybeReset_WithExemplars() { .isEqualTo(exemplars); } - @Test - void aggregateThenMaybeReset() { + @ParameterizedTest + @EnumSource(MemoryMode.class) + void aggregateThenMaybeReset(MemoryMode memoryMode) { + initialize(memoryMode); + AggregatorHandle aggregatorHandle = aggregator.createHandle(); @@ -238,8 +265,11 @@ void aggregateThenMaybeReset() { .isEqualTo(Collections.singletonList(1L)); } - @Test - void testInsert1M() { + @ParameterizedTest + @EnumSource(MemoryMode.class) + void testInsert1M(MemoryMode memoryMode) { + initialize(memoryMode); + AggregatorHandle handle = aggregator.createHandle(); @@ -261,8 +291,11 @@ void testInsert1M() { assertThat(point.getPositiveBuckets().getTotalCount()).isEqualTo(n); } - @Test - void testDownScale() { + @ParameterizedTest + @EnumSource(MemoryMode.class) + void testDownScale(MemoryMode memoryMode) { + initialize(memoryMode); + DoubleBase2ExponentialHistogramAggregator.Handle handle = (DoubleBase2ExponentialHistogramAggregator.Handle) aggregator.createHandle(); // record a measurement to initialize positive buckets @@ -289,8 +322,9 @@ void testDownScale() { assertThat(buckets.getTotalCount()).isEqualTo(5); } - @Test - void testToMetricData() { + @ParameterizedTest + @EnumSource(MemoryMode.class) + void testToMetricData(MemoryMode memoryMode) { Attributes attributes = Attributes.builder().put("test", "value").build(); DoubleExemplarData exemplar = ImmutableDoubleExemplarData.create( @@ -310,7 +344,8 @@ void testToMetricData() { Mockito.when(reservoirSupplier.get()).thenReturn(reservoir); DoubleBase2ExponentialHistogramAggregator cumulativeAggregator = - new DoubleBase2ExponentialHistogramAggregator(reservoirSupplier, 160, MAX_SCALE); + new DoubleBase2ExponentialHistogramAggregator( + reservoirSupplier, 160, MAX_SCALE, memoryMode); AggregatorHandle aggregatorHandle = cumulativeAggregator.createHandle(); @@ -373,8 +408,11 @@ void testToMetricData() { .isEqualTo(AggregationTemporality.DELTA); } - @Test - void testMultithreadedUpdates() throws InterruptedException { + @ParameterizedTest + @EnumSource(MemoryMode.class) + void testMultithreadedUpdates(MemoryMode memoryMode) throws InterruptedException { + initialize(memoryMode); + AggregatorHandle aggregatorHandle = aggregator.createHandle(); ImmutableList updates = ImmutableList.of(0D, 0.1D, -0.1D, 1D, -1D, 100D); @@ -440,4 +478,92 @@ void testMultithreadedUpdates() throws InterruptedException { valueToIndex(point.getScale(), 1) - point.getPositiveBuckets().getOffset())) .isEqualTo(numberOfUpdates); } + + @Test + public void verifyMutableDataUsedInReusableDataMemoryMode() { + initialize(MemoryMode.REUSABLE_DATA); + + DoubleBase2ExponentialHistogramAggregator.Handle handle = + (DoubleBase2ExponentialHistogramAggregator.Handle) aggregator.createHandle(); + + // record a measurement to initialize positive buckets + handle.recordDouble(0.5); + // record a measurement to initialize negative buckets + handle.recordDouble(-13.2); + + ExponentialHistogramPointData point = + Objects.requireNonNull( + handle.aggregateThenMaybeReset(0, 1, Attributes.empty(), /* reset= */ false)); + + assertThat(point).isInstanceOf(MutableExponentialHistogramPointData.class); + assertThat(point.getPositiveBuckets()).isInstanceOf(MutableExponentialHistogramBuckets.class); + assertThat(point.getNegativeBuckets()).isInstanceOf(MutableExponentialHistogramBuckets.class); + assertThat(point.getPositiveBuckets().getBucketCounts()).isNotEmpty(); + assertThat(point.getNegativeBuckets().getBucketCounts()).isNotEmpty(); + + handle.recordDouble(0.6); + handle.recordDouble(-16.3); + + ExponentialHistogramPointData secondAggregatePoint = + Objects.requireNonNull( + handle.aggregateThenMaybeReset(1, 2, Attributes.empty(), /* reset= */ false)); + + // Mutable point should be reused across collections. + assertThat(secondAggregatePoint).isSameAs(point); + } + + @Test + public void verifyImmutableDataUsedInImmutableDataMemoryMode() { + initialize(MemoryMode.IMMUTABLE_DATA); + + DoubleBase2ExponentialHistogramAggregator.Handle handle = + (DoubleBase2ExponentialHistogramAggregator.Handle) aggregator.createHandle(); + + // record a measurement to initialize positive buckets + handle.recordDouble(0.5); + // record a measurement to initialize negative buckets + handle.recordDouble(-13.2); + + ExponentialHistogramPointData point = + Objects.requireNonNull( + handle.aggregateThenMaybeReset(0, 1, Attributes.empty(), /* reset= */ false)); + + assertThat(point).isInstanceOf(ImmutableExponentialHistogramPointData.class); + assertThat(point.getPositiveBuckets()) + .isInstanceOf(DoubleBase2ExponentialHistogramBuckets.class); + assertThat(point.getNegativeBuckets()) + .isInstanceOf(DoubleBase2ExponentialHistogramBuckets.class); + } + + @Test + public void reusablePoint_emptyFirstThenRecordAndCheck() { + initialize(MemoryMode.REUSABLE_DATA); + + DoubleBase2ExponentialHistogramAggregator.Handle handle = + (DoubleBase2ExponentialHistogramAggregator.Handle) aggregator.createHandle(); + + // Let's create a point without buckets + ExponentialHistogramPointData point = + Objects.requireNonNull( + handle.aggregateThenMaybeReset(0, 1, Attributes.empty(), /* reset= */ false)); + + assertThat(point).isInstanceOf(MutableExponentialHistogramPointData.class); + assertThat(point.getPositiveBuckets()).isInstanceOf(EmptyExponentialHistogramBuckets.class); + assertThat(point.getNegativeBuckets()).isInstanceOf(EmptyExponentialHistogramBuckets.class); + + // record a measurement to initialize positive buckets + handle.recordDouble(0.5); + // record a measurement to initialize negative buckets + handle.recordDouble(-13.2); + + point = + Objects.requireNonNull( + handle.aggregateThenMaybeReset(0, 1, Attributes.empty(), /* reset= */ false)); + + assertThat(point).isInstanceOf(MutableExponentialHistogramPointData.class); + assertThat(point.getPositiveBuckets()).isInstanceOf(MutableExponentialHistogramBuckets.class); + assertThat(point.getNegativeBuckets()).isInstanceOf(MutableExponentialHistogramBuckets.class); + assertThat(point.getPositiveBuckets().getBucketCounts()).isNotEmpty(); + assertThat(point.getNegativeBuckets().getBucketCounts()).isNotEmpty(); + } } diff --git a/sdk/metrics/src/test/java/io/opentelemetry/sdk/metrics/internal/aggregator/DoubleBase2ExponentialHistogramBucketsTest.java b/sdk/metrics/src/test/java/io/opentelemetry/sdk/metrics/internal/aggregator/DoubleBase2ExponentialHistogramBucketsTest.java index b839892da80..fa6bcc255f4 100644 --- a/sdk/metrics/src/test/java/io/opentelemetry/sdk/metrics/internal/aggregator/DoubleBase2ExponentialHistogramBucketsTest.java +++ b/sdk/metrics/src/test/java/io/opentelemetry/sdk/metrics/internal/aggregator/DoubleBase2ExponentialHistogramBucketsTest.java @@ -8,9 +8,13 @@ import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; +import io.opentelemetry.sdk.common.export.MemoryMode; +import io.opentelemetry.sdk.internal.DynamicPrimitiveLongList; import java.util.Arrays; import java.util.Collections; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EnumSource; /** * These are extra test cases for buckets. Much of this class is already tested via more complex @@ -18,11 +22,12 @@ */ class DoubleBase2ExponentialHistogramBucketsTest { - @Test - void record_Valid() { + @ParameterizedTest + @EnumSource(MemoryMode.class) + void record_Valid(MemoryMode memoryMode) { // Can only effectively test recording of one value here due to downscaling required. // More complex recording/downscaling operations are tested in the aggregator. - DoubleBase2ExponentialHistogramBuckets b = newBuckets(); + DoubleBase2ExponentialHistogramBuckets b = newBuckets(memoryMode); b.record(1); b.record(1); b.record(1); @@ -30,15 +35,17 @@ void record_Valid() { assertThat(b.getBucketCounts()).isEqualTo(Collections.singletonList(3L)); } - @Test - void record_Zero_Throws() { - DoubleBase2ExponentialHistogramBuckets b = newBuckets(); + @ParameterizedTest + @EnumSource(MemoryMode.class) + void record_Zero_Throws(MemoryMode memoryMode) { + DoubleBase2ExponentialHistogramBuckets b = newBuckets(memoryMode); assertThatThrownBy(() -> b.record(0)).isInstanceOf(IllegalStateException.class); } - @Test - void downscale_Valid() { - DoubleBase2ExponentialHistogramBuckets b = newBuckets(); + @ParameterizedTest + @EnumSource(MemoryMode.class) + void downscale_Valid(MemoryMode memoryMode) { + DoubleBase2ExponentialHistogramBuckets b = newBuckets(memoryMode); b.downscale(20); // scale of zero is easy to reason with without a calculator b.record(1); b.record(2); @@ -49,16 +56,18 @@ void downscale_Valid() { assertThat(b.getOffset()).isEqualTo(-1); } - @Test - void downscale_NegativeIncrement_Throws() { - DoubleBase2ExponentialHistogramBuckets b = newBuckets(); + @ParameterizedTest + @EnumSource(MemoryMode.class) + void downscale_NegativeIncrement_Throws(MemoryMode memoryMode) { + DoubleBase2ExponentialHistogramBuckets b = newBuckets(memoryMode); assertThatThrownBy(() -> b.downscale(-1)).isInstanceOf(IllegalStateException.class); } - @Test - void equalsAndHashCode() { - DoubleBase2ExponentialHistogramBuckets a = newBuckets(); - DoubleBase2ExponentialHistogramBuckets b = newBuckets(); + @ParameterizedTest + @EnumSource(MemoryMode.class) + void equalsAndHashCode(MemoryMode memoryMode) { + DoubleBase2ExponentialHistogramBuckets a = newBuckets(memoryMode); + DoubleBase2ExponentialHistogramBuckets b = newBuckets(memoryMode); assertThat(a).isNotNull(); assertThat(b).isEqualTo(a); @@ -76,9 +85,9 @@ void equalsAndHashCode() { assertThat(a).hasSameHashCodeAs(b); // Now we start to play with altering offset, but having same effective counts. - DoubleBase2ExponentialHistogramBuckets empty = newBuckets(); + DoubleBase2ExponentialHistogramBuckets empty = newBuckets(memoryMode); empty.downscale(20); - DoubleBase2ExponentialHistogramBuckets c = newBuckets(); + DoubleBase2ExponentialHistogramBuckets c = newBuckets(memoryMode); c.downscale(20); assertThat(c.record(1)).isTrue(); // Record can fail if scale is not set correctly. @@ -86,17 +95,85 @@ void equalsAndHashCode() { assertThat(c.getTotalCount()).isEqualTo(2); } - @Test - void toString_Valid() { + @ParameterizedTest + @EnumSource(MemoryMode.class) + void toString_Valid(MemoryMode memoryMode) { // Note this test may break once difference implementations for counts are developed since // the counts may have different toStrings(). - DoubleBase2ExponentialHistogramBuckets b = newBuckets(); + DoubleBase2ExponentialHistogramBuckets b = newBuckets(memoryMode); b.record(1); assertThat(b.toString()) .isEqualTo("DoubleExponentialHistogramBuckets{scale: 20, offset: -1, counts: {-1=1} }"); } - private static DoubleBase2ExponentialHistogramBuckets newBuckets() { - return new DoubleBase2ExponentialHistogramBuckets(20, 160); + @Test + void testGetBucketCountsWithReusableList() { + // Can only effectively test recording of one value here due to downscaling required. + // More complex recording/downscaling operations are tested in the aggregator. + DoubleBase2ExponentialHistogramBuckets b = newBuckets(MemoryMode.REUSABLE_DATA); + b.record(1); + b.record(1); + b.record(1); + assertThat(b.getBucketCounts()).isEqualTo(Collections.singletonList(3L)); + + DynamicPrimitiveLongList bucketCounts = DynamicPrimitiveLongList.empty(); + b.getBucketCountsIntoReusableList(bucketCounts); + assertThat(bucketCounts).isEqualTo(Collections.singletonList(3L)); + } + + @Test + public void testGetBucketCountsWithReusableListWithEmptyCounts() { + // Can only effectively test recording of one value here due to downscaling required. + // More complex recording/downscaling operations are tested in the aggregator. + DoubleBase2ExponentialHistogramBuckets b = newBuckets(MemoryMode.REUSABLE_DATA); + assertThat(b.getBucketCounts()).isEmpty(); + + DynamicPrimitiveLongList bucketCounts = DynamicPrimitiveLongList.empty(); + b.getBucketCountsIntoReusableList(bucketCounts); + assertThat(bucketCounts).isEmpty(); + } + + @Test + public void testDownScaleReusableCountIsOkWhenUsedForSecondTime() { + DoubleBase2ExponentialHistogramBuckets immutableDataBasedBuckets = + newBuckets(MemoryMode.IMMUTABLE_DATA); + immutableDataBasedBuckets.record(0.5); + immutableDataBasedBuckets.record(1); + immutableDataBasedBuckets.record(10); + immutableDataBasedBuckets.downscale( + 2); // scale of zero is easy to reason with without a calculator + + DoubleBase2ExponentialHistogramBuckets reusableDataBasedBuckets = + newBuckets(MemoryMode.REUSABLE_DATA); + reusableDataBasedBuckets.record(0.5); + reusableDataBasedBuckets.record(1); + reusableDataBasedBuckets.record(10); + reusableDataBasedBuckets.downscale( + 2); // scale of zero is easy to reason with without a calculator + + assertThat(immutableDataBasedBuckets.getScale()).isEqualTo(reusableDataBasedBuckets.getScale()); + assertThat(immutableDataBasedBuckets.getTotalCount()) + .isEqualTo(reusableDataBasedBuckets.getTotalCount()); + assertThat(immutableDataBasedBuckets.getBucketCounts()) + .isEqualTo(reusableDataBasedBuckets.getBucketCounts()); + assertThat(immutableDataBasedBuckets.getOffset()) + .isEqualTo(reusableDataBasedBuckets.getOffset()); + + immutableDataBasedBuckets.downscale( + 3); // scale of zero is easy to reason with without a calculator + reusableDataBasedBuckets.downscale( + 3); // scale of zero is easy to reason with without a calculator + + assertThat(immutableDataBasedBuckets.getScale()).isEqualTo(reusableDataBasedBuckets.getScale()); + assertThat(immutableDataBasedBuckets.getTotalCount()) + .isEqualTo(reusableDataBasedBuckets.getTotalCount()); + assertThat(immutableDataBasedBuckets.getBucketCounts()) + .isEqualTo(reusableDataBasedBuckets.getBucketCounts()); + assertThat(immutableDataBasedBuckets.getOffset()) + .isEqualTo(reusableDataBasedBuckets.getOffset()); + } + + private static DoubleBase2ExponentialHistogramBuckets newBuckets(MemoryMode memoryMode) { + return new DoubleBase2ExponentialHistogramBuckets(20, 160, memoryMode); } } diff --git a/sdk/metrics/src/test/java/io/opentelemetry/sdk/metrics/internal/data/MutableExponentialHistogramBucketsTest.java b/sdk/metrics/src/test/java/io/opentelemetry/sdk/metrics/internal/data/MutableExponentialHistogramBucketsTest.java new file mode 100644 index 00000000000..067f34a54c7 --- /dev/null +++ b/sdk/metrics/src/test/java/io/opentelemetry/sdk/metrics/internal/data/MutableExponentialHistogramBucketsTest.java @@ -0,0 +1,46 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.sdk.metrics.internal.data; + +import static org.assertj.core.api.Assertions.assertThat; + +import io.opentelemetry.sdk.internal.DynamicPrimitiveLongList; +import org.junit.jupiter.api.Test; + +class MutableExponentialHistogramBucketsTest { + + @Test + void testSanity() { + MutableExponentialHistogramBuckets buckets = new MutableExponentialHistogramBuckets(); + assertThat(buckets.getScale()).isEqualTo(0); + assertThat(buckets.getOffset()).isEqualTo(0); + assertThat(buckets.getTotalCount()).isEqualTo(0); + assertThat(buckets.getBucketCounts()).isEmpty(); + assertThat(buckets.getReusableBucketCountsList()).isEmpty(); + + DynamicPrimitiveLongList bucketCounts = DynamicPrimitiveLongList.of(1, 2, 3); + buckets.set(1, 2, 3, bucketCounts); + + assertThat(buckets.getScale()).isEqualTo(1); + assertThat(buckets.getOffset()).isEqualTo(2); + assertThat(buckets.getTotalCount()).isEqualTo(3); + assertThat(buckets.getBucketCounts()).containsExactly(1L, 2L, 3L); + assertThat(buckets.getReusableBucketCountsList()).containsExactly(1L, 2L, 3L); + + assertThat(buckets.toString()) + .isEqualTo( + "MutableExponentialHistogramBuckets{scale=1, offset=2, bucketCounts=[1, 2, 3], totalCount=3}"); + + MutableExponentialHistogramBuckets sameBuckets = new MutableExponentialHistogramBuckets(); + sameBuckets.set(1, 2, 3, DynamicPrimitiveLongList.of(1, 2, 3)); + assertThat(sameBuckets).isEqualTo(buckets); + assertThat(sameBuckets.hashCode()).isEqualTo(buckets.hashCode()); + + sameBuckets.set(1, 2, 3, DynamicPrimitiveLongList.of(1, 20, 3)); + assertThat(sameBuckets).isNotEqualTo(buckets); + assertThat(sameBuckets.hashCode()).isNotEqualTo(buckets.hashCode()); + } +} diff --git a/sdk/metrics/src/test/java/io/opentelemetry/sdk/metrics/internal/data/MutableExponentialHistogramPointDataTest.java b/sdk/metrics/src/test/java/io/opentelemetry/sdk/metrics/internal/data/MutableExponentialHistogramPointDataTest.java new file mode 100644 index 00000000000..0cd56aa6f72 --- /dev/null +++ b/sdk/metrics/src/test/java/io/opentelemetry/sdk/metrics/internal/data/MutableExponentialHistogramPointDataTest.java @@ -0,0 +1,114 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.sdk.metrics.internal.data; + +import static org.assertj.core.api.Assertions.assertThat; + +import io.opentelemetry.api.common.AttributeKey; +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.sdk.internal.DynamicPrimitiveLongList; +import java.util.Collections; +import org.junit.jupiter.api.Test; + +class MutableExponentialHistogramPointDataTest { + + @Test + public void testSanity() { + MutableExponentialHistogramPointData pointData = new MutableExponentialHistogramPointData(); + assertThat(pointData.getSum()).isEqualTo(0); + assertThat(pointData.getCount()).isEqualTo(0); + assertThat(pointData.getPositiveBuckets().getTotalCount()).isEqualTo(0); + assertThat(pointData.getNegativeBuckets().getTotalCount()).isEqualTo(0); + assertThat(pointData.getExemplars()).isEmpty(); + + MutableExponentialHistogramBuckets positiveBuckets = new MutableExponentialHistogramBuckets(); + positiveBuckets.set( + /* scale= */ 1, /* offset= */ 2, /* totalCount= */ 3, DynamicPrimitiveLongList.of(1, 2, 3)); + MutableExponentialHistogramBuckets negativeBuckets = new MutableExponentialHistogramBuckets(); + negativeBuckets.set(10, 20, 30, DynamicPrimitiveLongList.of(50, 60, 70)); + + pointData.set( + /* scale= */ 1, + /* sum= */ 2, + /* zeroCount= */ 10, + /* hasMin= */ true, + /* min= */ 100, + /* hasMax= */ true, + /* max= */ 1000, + positiveBuckets, + negativeBuckets, + /* startEpochNanos= */ 10, + /* epochNanos= */ 20, + Attributes.of(AttributeKey.stringKey("foo"), "bar"), + Collections.emptyList()); + + assertThat(pointData.getSum()).isEqualTo(2); + assertThat(pointData.getCount()).isEqualTo(10 + 30 + 3); + assertThat(pointData.getAttributes().get(AttributeKey.stringKey("foo"))).isEqualTo("bar"); + assertThat(pointData.getAttributes().size()).isEqualTo(1); + assertThat(pointData.getScale()).isEqualTo(1); + assertThat(pointData.getZeroCount()).isEqualTo(10); + assertThat(pointData.hasMin()).isTrue(); + assertThat(pointData.getMin()).isEqualTo(100); + assertThat(pointData.hasMax()).isTrue(); + assertThat(pointData.getMax()).isEqualTo(1000); + assertThat(pointData.getPositiveBuckets().getTotalCount()).isEqualTo(3); + assertThat(pointData.getNegativeBuckets().getTotalCount()).isEqualTo(30); + assertThat(pointData.getPositiveBuckets().getBucketCounts()).containsExactly(1L, 2L, 3L); + assertThat(pointData.getNegativeBuckets().getBucketCounts()).containsExactly(50L, 60L, 70L); + assertThat(pointData.getStartEpochNanos()).isEqualTo(10); + assertThat(pointData.getEpochNanos()).isEqualTo(20); + assertThat(pointData.getExemplars()).isEmpty(); + + assertThat(pointData.toString()) + .isEqualTo( + "MutableExponentialHistogramPointData{startEpochNanos=10, epochNanos=20, " + + "attributes={foo=\"bar\"}, scale=1, sum=2.0, count=43, zeroCount=10, hasMin=true, " + + "min=100.0, hasMax=true, max=1000.0, " + + "positiveBuckets=MutableExponentialHistogramBuckets{scale=1, offset=2, " + + "bucketCounts=[1, 2, 3], totalCount=3}, " + + "negativeBuckets=MutableExponentialHistogramBuckets{scale=10, offset=20, " + + "bucketCounts=[50, 60, 70], totalCount=30}, exemplars=[]}"); + + MutableExponentialHistogramPointData samePointData = new MutableExponentialHistogramPointData(); + samePointData.set( + /* scale= */ 1, + /* sum= */ 2, + /* zeroCount= */ 10, + /* hasMin= */ true, + /* min= */ 100, + /* hasMax= */ true, + /* max= */ 1000, + positiveBuckets, + negativeBuckets, + /* startEpochNanos= */ 10, + /* epochNanos= */ 20, + Attributes.of(AttributeKey.stringKey("foo"), "bar"), + Collections.emptyList()); + assertThat(samePointData).isEqualTo(pointData); + assertThat(samePointData.hashCode()).isEqualTo(pointData.hashCode()); + + MutableExponentialHistogramPointData differentPointData = + new MutableExponentialHistogramPointData(); + differentPointData.set( + /* scale= */ 1, + /* sum= */ 2, + /* zeroCount= */ 10000000, + /* hasMin= */ true, + /* min= */ 100, + /* hasMax= */ true, + /* max= */ 1000, + positiveBuckets, + negativeBuckets, + /* startEpochNanos= */ 10, + /* epochNanos= */ 20, + Attributes.of(AttributeKey.stringKey("foo"), "bar"), + Collections.emptyList()); + + assertThat(differentPointData).isNotEqualTo(pointData); + assertThat(differentPointData.hashCode()).isNotEqualTo(pointData.hashCode()); + } +} diff --git a/sdk/metrics/src/test/java/io/opentelemetry/sdk/metrics/internal/state/SynchronousMetricStorageTest.java b/sdk/metrics/src/test/java/io/opentelemetry/sdk/metrics/internal/state/SynchronousMetricStorageTest.java index 2d548acc169..0117af20b59 100644 --- a/sdk/metrics/src/test/java/io/opentelemetry/sdk/metrics/internal/state/SynchronousMetricStorageTest.java +++ b/sdk/metrics/src/test/java/io/opentelemetry/sdk/metrics/internal/state/SynchronousMetricStorageTest.java @@ -5,6 +5,7 @@ package io.opentelemetry.sdk.metrics.internal.state; +import static io.opentelemetry.sdk.common.export.MemoryMode.IMMUTABLE_DATA; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.attributeEntry; import static org.mockito.Mockito.never; @@ -103,7 +104,7 @@ private void initialize(MemoryMode memoryMode) { aggregator = spy( ((AggregatorFactory) Aggregation.sum()) - .createAggregator(DESCRIPTOR, ExemplarFilter.alwaysOff())); + .createAggregator(DESCRIPTOR, ExemplarFilter.alwaysOff(), memoryMode)); } @ParameterizedTest @@ -205,7 +206,7 @@ void recordAndCollect_CumulativeDoesNotReset(MemoryMode memoryMode) { @Test void recordAndCollect_DeltaResets_ImmutableData() { - initialize(MemoryMode.IMMUTABLE_DATA); + initialize(IMMUTABLE_DATA); DefaultSynchronousMetricStorage storage = new DefaultSynchronousMetricStorage<>( @@ -423,7 +424,7 @@ void recordAndCollect_CumulativeAtLimit(MemoryMode memoryMode) { @Test void recordAndCollect_DeltaAtLimit_ImmutableDataMemoryMode() { - initialize(MemoryMode.IMMUTABLE_DATA); + initialize(IMMUTABLE_DATA); DefaultSynchronousMetricStorage storage = new DefaultSynchronousMetricStorage<>( @@ -798,7 +799,7 @@ private static Stream concurrentStressTestArguments() { for (MemoryMode memoryMode : MemoryMode.values()) { Aggregator aggregator = ((AggregatorFactory) Aggregation.sum()) - .createAggregator(DESCRIPTOR, ExemplarFilter.alwaysOff()); + .createAggregator(DESCRIPTOR, ExemplarFilter.alwaysOff(), memoryMode); argumentsList.add( Arguments.of( diff --git a/sdk/metrics/src/test/java/io/opentelemetry/sdk/metrics/internal/view/Base2ExponentialHistogramAggregationTest.java b/sdk/metrics/src/test/java/io/opentelemetry/sdk/metrics/internal/view/Base2ExponentialHistogramAggregationTest.java index 1d02a33619d..0479baafde3 100644 --- a/sdk/metrics/src/test/java/io/opentelemetry/sdk/metrics/internal/view/Base2ExponentialHistogramAggregationTest.java +++ b/sdk/metrics/src/test/java/io/opentelemetry/sdk/metrics/internal/view/Base2ExponentialHistogramAggregationTest.java @@ -9,6 +9,7 @@ import static org.assertj.core.api.Assertions.assertThatThrownBy; import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.sdk.common.export.MemoryMode; import io.opentelemetry.sdk.metrics.Aggregation; import io.opentelemetry.sdk.metrics.InstrumentType; import io.opentelemetry.sdk.metrics.InstrumentValueType; @@ -56,7 +57,8 @@ void minimumBucketsCanAccommodateMaxRange() { InstrumentType.HISTOGRAM, InstrumentValueType.DOUBLE, Advice.empty()), - ExemplarFilter.alwaysOff()); + ExemplarFilter.alwaysOff(), + MemoryMode.IMMUTABLE_DATA); AggregatorHandle handle = aggregator.createHandle(); // Record max range diff --git a/sdk/trace/build.gradle.kts b/sdk/trace/build.gradle.kts index 2f19cea68ec..ae87071507a 100644 --- a/sdk/trace/build.gradle.kts +++ b/sdk/trace/build.gradle.kts @@ -39,7 +39,6 @@ dependencies { // dependencies. isTransitive = false } - jmh(project(":exporters:jaeger-thrift")) jmh(project(":exporters:otlp:all")) { // The opentelemetry-exporter-otlp depends on this project itself. So don't pull in // the transitive dependencies. diff --git a/sdk/trace/src/jmh/java/io/opentelemetry/sdk/trace/ExporterBenchmark.java b/sdk/trace/src/jmh/java/io/opentelemetry/sdk/trace/ExporterBenchmark.java index 28b06257911..19ec7edfdbd 100644 --- a/sdk/trace/src/jmh/java/io/opentelemetry/sdk/trace/ExporterBenchmark.java +++ b/sdk/trace/src/jmh/java/io/opentelemetry/sdk/trace/ExporterBenchmark.java @@ -38,7 +38,6 @@ public abstract static class AbstractProcessorBenchmark { private static final DockerImageName OTLP_COLLECTOR_IMAGE = DockerImageName.parse("otel/opentelemetry-collector-dev:latest"); protected static final int OTLP_PORT = 5678; - protected static final int JAEGER_PORT = 14268; private static final int HEALTH_CHECK_PORT = 13133; protected SdkSpanBuilder sdkSpanBuilder; @@ -49,7 +48,7 @@ public void setup() { // Configuring the collector test-container GenericContainer collector = new GenericContainer<>(OTLP_COLLECTOR_IMAGE) - .withExposedPorts(OTLP_PORT, HEALTH_CHECK_PORT, JAEGER_PORT) + .withExposedPorts(OTLP_PORT, HEALTH_CHECK_PORT) .waitingFor(Wait.forHttp("/").forPort(HEALTH_CHECK_PORT)) .withCopyFileToContainer( MountableFile.forClasspathResource("/otel.yaml"), "/etc/otel.yaml") @@ -92,17 +91,4 @@ protected OtlpGrpcSpanExporter createExporter(GenericContainer collector) { .build(); } } - - @SuppressWarnings("deprecation") // Benchmarking deprecated code - public static class JaegerBenchmark extends AbstractProcessorBenchmark { - @Override - protected io.opentelemetry.exporter.jaeger.thrift.JaegerThriftSpanExporter createExporter( - GenericContainer collector) { - String host = collector.getHost(); - int port = collector.getMappedPort(JAEGER_PORT); - return io.opentelemetry.exporter.jaeger.thrift.JaegerThriftSpanExporter.builder() - .setEndpoint("http://" + host + ":" + port + "/api/traces") - .build(); - } - } } diff --git a/sdk/trace/src/jmh/resources/otel.yaml b/sdk/trace/src/jmh/resources/otel.yaml index 316ff8da1ef..edd847f0c02 100644 --- a/sdk/trace/src/jmh/resources/otel.yaml +++ b/sdk/trace/src/jmh/resources/otel.yaml @@ -3,9 +3,6 @@ receivers: protocols: grpc: endpoint: 0.0.0.0:5678 - jaeger: - protocols: - thrift_http: processors: batch: @@ -20,6 +17,6 @@ service: extensions: [health_check] pipelines: traces: - receivers: [otlp, jaeger] + receivers: [otlp] processors: [batch] exporters: [logging] diff --git a/settings.gradle.kts b/settings.gradle.kts index 73b53ac9612..e54353813b3 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -36,9 +36,6 @@ include(":exporters:common") include(":exporters:sender:grpc-managed-channel") include(":exporters:sender:jdk") include(":exporters:sender:okhttp") -include(":exporters:jaeger") -include(":exporters:jaeger-proto") -include(":exporters:jaeger-thrift") include(":exporters:logging") include(":exporters:logging-otlp") include(":exporters:otlp:all")