diff --git a/RELEASE.md b/RELEASE.md index af859378..487543f9 100644 --- a/RELEASE.md +++ b/RELEASE.md @@ -61,10 +61,10 @@ project has a bom. $ mvn=$PWD/mvnw $ for p in ./bom .; do (cd $p - $mvn versions:set -DnewVersion=2.17.1-SNAPSHOT -DgenerateBackupPoms=false + $mvn versions:set -DnewVersion=3.0.1-SNAPSHOT -DgenerateBackupPoms=false $mvn -o clean install -DskipTests $mvn com.mycila:license-maven-plugin:format - $mvn versions:set -DnewVersion=2.17.0-SNAPSHOT -DgenerateBackupPoms=false) + $mvn versions:set -DnewVersion=3.0.0-SNAPSHOT -DgenerateBackupPoms=false) done $ git commit -asm"Adjusts copyright headers for this year" ``` diff --git a/activemq-client/pom.xml b/activemq-client/pom.xml index 09b1c73d..36804bc6 100644 --- a/activemq-client/pom.xml +++ b/activemq-client/pom.xml @@ -18,7 +18,7 @@ zipkin-reporter-parent io.zipkin.reporter2 - 2.17.3-SNAPSHOT + 3.0.0-SNAPSHOT 4.0.0 @@ -41,6 +41,14 @@ ${project.groupId} zipkin-reporter ${project.version} + + + + io.zipkin.zipkin2 + zipkin + + diff --git a/activemq-client/src/main/java/zipkin2/reporter/activemq/ActiveMQConn.java b/activemq-client/src/main/java/zipkin2/reporter/activemq/ActiveMQConn.java index 586db2f1..1d46571b 100644 --- a/activemq-client/src/main/java/zipkin2/reporter/activemq/ActiveMQConn.java +++ b/activemq-client/src/main/java/zipkin2/reporter/activemq/ActiveMQConn.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2019 The OpenZipkin Authors + * Copyright 2016-2024 The OpenZipkin Authors * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except * in compliance with the License. You may obtain a copy of the License at @@ -20,7 +20,7 @@ import javax.jms.QueueSession; import org.apache.activemq.ActiveMQConnection; import org.apache.activemq.transport.TransportListener; -import zipkin2.CheckResult; +import zipkin2.reporter.CheckResult; final class ActiveMQConn implements TransportListener, Closeable { static final CheckResult diff --git a/activemq-client/src/main/java/zipkin2/reporter/activemq/ActiveMQSender.java b/activemq-client/src/main/java/zipkin2/reporter/activemq/ActiveMQSender.java index 12262899..6735271a 100644 --- a/activemq-client/src/main/java/zipkin2/reporter/activemq/ActiveMQSender.java +++ b/activemq-client/src/main/java/zipkin2/reporter/activemq/ActiveMQSender.java @@ -19,13 +19,12 @@ import javax.jms.JMSException; import javax.jms.QueueSender; import org.apache.activemq.ActiveMQConnectionFactory; -import zipkin2.Call; -import zipkin2.Callback; -import zipkin2.CheckResult; -import zipkin2.codec.Encoding; -import zipkin2.reporter.AsyncReporter; import zipkin2.reporter.BytesMessageEncoder; +import zipkin2.reporter.Call; +import zipkin2.reporter.Callback; +import zipkin2.reporter.CheckResult; import zipkin2.reporter.ClosedSenderException; +import zipkin2.reporter.Encoding; import zipkin2.reporter.Sender; /** @@ -33,7 +32,8 @@ * *

Usage

*

- * This type is designed for {@link AsyncReporter.Builder#builder(Sender) the async reporter}. + * This type is designed for {@link zipkin2.reporter.AsyncReporter.Builder#builder(Sender) the async + * reporter}. * *

Here's a simple configuration, configured for json: * diff --git a/activemq-client/src/test/java/zipkin2/reporter/activemq/ITActiveMQSender.java b/activemq-client/src/test/java/zipkin2/reporter/activemq/ITActiveMQSender.java index 3b4a3e31..3c758391 100644 --- a/activemq-client/src/test/java/zipkin2/reporter/activemq/ITActiveMQSender.java +++ b/activemq-client/src/test/java/zipkin2/reporter/activemq/ITActiveMQSender.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2023 The OpenZipkin Authors + * Copyright 2016-2024 The OpenZipkin Authors * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except * in compliance with the License. You may obtain a copy of the License at @@ -22,13 +22,13 @@ import org.junit.jupiter.api.TestInstance; import org.junit.jupiter.api.Timeout; import org.junit.jupiter.api.extension.RegisterExtension; -import zipkin2.Call; -import zipkin2.CheckResult; import zipkin2.Span; -import zipkin2.codec.Encoding; import zipkin2.codec.SpanBytesDecoder; -import zipkin2.codec.SpanBytesEncoder; +import zipkin2.reporter.SpanBytesEncoder; import zipkin2.reporter.AsyncReporter; +import zipkin2.reporter.Call; +import zipkin2.reporter.CheckResult; +import zipkin2.reporter.Encoding; import zipkin2.reporter.Sender; import static java.util.stream.Collectors.toList; diff --git a/amqp-client/pom.xml b/amqp-client/pom.xml index 866c4678..f76e154c 100644 --- a/amqp-client/pom.xml +++ b/amqp-client/pom.xml @@ -20,7 +20,7 @@ io.zipkin.reporter2 zipkin-reporter-parent - 2.17.3-SNAPSHOT + 3.0.0-SNAPSHOT zipkin-sender-amqp-client @@ -41,6 +41,14 @@ ${project.groupId} zipkin-reporter ${project.version} + + + + io.zipkin.zipkin2 + zipkin + + diff --git a/amqp-client/src/main/java/zipkin2/reporter/amqp/RabbitMQSender.java b/amqp-client/src/main/java/zipkin2/reporter/amqp/RabbitMQSender.java index c69ad5a7..baed1fa1 100644 --- a/amqp-client/src/main/java/zipkin2/reporter/amqp/RabbitMQSender.java +++ b/amqp-client/src/main/java/zipkin2/reporter/amqp/RabbitMQSender.java @@ -21,23 +21,23 @@ import java.util.Arrays; import java.util.List; import java.util.concurrent.TimeoutException; -import zipkin2.Call; -import zipkin2.Callback; -import zipkin2.CheckResult; -import zipkin2.codec.Encoding; -import zipkin2.reporter.AsyncReporter; import zipkin2.reporter.BytesMessageEncoder; +import zipkin2.reporter.Call; +import zipkin2.reporter.Callback; +import zipkin2.reporter.CheckResult; import zipkin2.reporter.ClosedSenderException; +import zipkin2.reporter.Encoding; import zipkin2.reporter.Sender; -import static zipkin2.Call.propagateIfFatal; +import static zipkin2.reporter.Call.propagateIfFatal; /** * This sends (usually json v2) encoded spans to a RabbitMQ queue. * *

Usage

*

- * This type is designed for {@link AsyncReporter.Builder#builder(Sender) the async reporter}. + * This type is designed for {@link zipkin2.reporter.AsyncReporter.Builder#builder(Sender) the async + * reporter}. * *

Here's a simple configuration, configured for json: * @@ -189,7 +189,7 @@ public final RabbitMQSender build() { connectionFactory = builder.connectionFactory.clone(); } - public final Builder toBuilder() { + public Builder toBuilder() { return new Builder(this); } @@ -231,7 +231,7 @@ public final Builder toBuilder() { } } - @Override public final String toString() { + @Override public String toString() { return "RabbitMQSender{addresses=" + addresses + ", queue=" + queue + "}"; } diff --git a/amqp-client/src/test/java/zipkin2/reporter/amqp/ITRabbitMQSender.java b/amqp-client/src/test/java/zipkin2/reporter/amqp/ITRabbitMQSender.java index ac68e74b..65462940 100644 --- a/amqp-client/src/test/java/zipkin2/reporter/amqp/ITRabbitMQSender.java +++ b/amqp-client/src/test/java/zipkin2/reporter/amqp/ITRabbitMQSender.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2023 The OpenZipkin Authors + * Copyright 2016-2024 The OpenZipkin Authors * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except * in compliance with the License. You may obtain a copy of the License at @@ -25,11 +25,11 @@ import org.junit.jupiter.api.TestInstance; import org.junit.jupiter.api.Timeout; import org.junit.jupiter.api.extension.RegisterExtension; -import zipkin2.Call; import zipkin2.Span; -import zipkin2.codec.Encoding; import zipkin2.codec.SpanBytesDecoder; -import zipkin2.codec.SpanBytesEncoder; +import zipkin2.reporter.SpanBytesEncoder; +import zipkin2.reporter.Call; +import zipkin2.reporter.Encoding; import zipkin2.reporter.Sender; import static java.util.stream.Collectors.toList; diff --git a/amqp-client/src/test/java/zipkin2/reporter/amqp/RabbitMQExtension.java b/amqp-client/src/test/java/zipkin2/reporter/amqp/RabbitMQExtension.java index 31adce89..4f561248 100644 --- a/amqp-client/src/test/java/zipkin2/reporter/amqp/RabbitMQExtension.java +++ b/amqp-client/src/test/java/zipkin2/reporter/amqp/RabbitMQExtension.java @@ -25,7 +25,7 @@ import org.testcontainers.containers.wait.strategy.Wait; import static org.testcontainers.utility.DockerImageName.parse; -import static zipkin2.Call.propagateIfFatal; +import static zipkin2.reporter.Call.propagateIfFatal; class RabbitMQExtension implements BeforeAllCallback, AfterAllCallback { static final Logger LOGGER = LoggerFactory.getLogger(RabbitMQExtension.class); diff --git a/amqp-client/src/test/java/zipkin2/reporter/amqp/RabbitMQSenderTest.java b/amqp-client/src/test/java/zipkin2/reporter/amqp/RabbitMQSenderTest.java index 5ac41009..ec46540c 100644 --- a/amqp-client/src/test/java/zipkin2/reporter/amqp/RabbitMQSenderTest.java +++ b/amqp-client/src/test/java/zipkin2/reporter/amqp/RabbitMQSenderTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2023 The OpenZipkin Authors + * Copyright 2016-2024 The OpenZipkin Authors * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except * in compliance with the License. You may obtain a copy of the License at @@ -14,8 +14,8 @@ package zipkin2.reporter.amqp; import org.junit.jupiter.api.Test; -import zipkin2.CheckResult; import zipkin2.reporter.AsyncReporter; +import zipkin2.reporter.CheckResult; import zipkin2.reporter.ClosedSenderException; import zipkin2.reporter.Sender; @@ -27,20 +27,20 @@ class RabbitMQSenderTest { // We can be pretty certain RabbitMQ isn't running on localhost port 80 RabbitMQSender sender = RabbitMQSender.newBuilder() - .connectionTimeout(100).addresses("localhost:80").build(); + .connectionTimeout(100).addresses("localhost:80").build(); @Test void checkFalseWhenRabbitMQIsDown() { CheckResult check = sender.check(); assertThat(check.ok()).isFalse(); assertThat(check.error()) - .isInstanceOf(RuntimeException.class); + .isInstanceOf(RuntimeException.class); } @Test void illegalToSendWhenClosed() throws Exception { sender.close(); assertThatThrownBy(() -> send(sender, CLIENT_SPAN, CLIENT_SPAN)) - .isInstanceOf(ClosedSenderException.class); + .isInstanceOf(ClosedSenderException.class); } /** @@ -51,7 +51,7 @@ class RabbitMQSenderTest { */ @Test void toStringContainsOnlySummaryInformation() { assertThat(sender).hasToString( - "RabbitMQSender{addresses=[localhost:80], queue=zipkin}" + "RabbitMQSender{addresses=[localhost:80], queue=zipkin}" ); } } diff --git a/benchmarks/pom.xml b/benchmarks/pom.xml index 13903909..6769cfdf 100644 --- a/benchmarks/pom.xml +++ b/benchmarks/pom.xml @@ -20,7 +20,7 @@ io.zipkin.reporter2 zipkin-reporter-parent - 2.17.3-SNAPSHOT + 3.0.0-SNAPSHOT benchmarks diff --git a/benchmarks/src/main/java/zipkin2/reporter/HttpSenderBenchmarks.java b/benchmarks/src/main/java/zipkin2/reporter/HttpSenderBenchmarks.java index a03b1ece..74a7c3ed 100644 --- a/benchmarks/src/main/java/zipkin2/reporter/HttpSenderBenchmarks.java +++ b/benchmarks/src/main/java/zipkin2/reporter/HttpSenderBenchmarks.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2020 The OpenZipkin Authors + * Copyright 2016-2024 The OpenZipkin Authors * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except * in compliance with the License. You may obtain a copy of the License at @@ -18,6 +18,7 @@ import com.linecorp.armeria.server.Route; import com.linecorp.armeria.server.Server; import java.time.Duration; +import zipkin2.reporter.internal.SenderBenchmarks; import static com.linecorp.armeria.common.HttpMethod.POST; import static com.linecorp.armeria.common.MediaType.JSON; diff --git a/benchmarks/src/main/java/zipkin2/reporter/KafkaSenderBenchmarks.java b/benchmarks/src/main/java/zipkin2/reporter/KafkaSenderBenchmarks.java index 51c76b32..c8831324 100644 --- a/benchmarks/src/main/java/zipkin2/reporter/KafkaSenderBenchmarks.java +++ b/benchmarks/src/main/java/zipkin2/reporter/KafkaSenderBenchmarks.java @@ -29,6 +29,7 @@ import org.testcontainers.containers.InternetProtocol; import org.testcontainers.containers.output.Slf4jLogConsumer; import org.testcontainers.containers.wait.strategy.Wait; +import zipkin2.reporter.internal.SenderBenchmarks; import zipkin2.reporter.kafka.KafkaSender; import static org.apache.kafka.clients.consumer.ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG; diff --git a/benchmarks/src/main/java/zipkin2/reporter/amqp/RabbitMQSenderBenchmarks.java b/benchmarks/src/main/java/zipkin2/reporter/amqp/RabbitMQSenderBenchmarks.java index f6f7fbb0..0c2b5068 100644 --- a/benchmarks/src/main/java/zipkin2/reporter/amqp/RabbitMQSenderBenchmarks.java +++ b/benchmarks/src/main/java/zipkin2/reporter/amqp/RabbitMQSenderBenchmarks.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2023 The OpenZipkin Authors + * Copyright 2016-2024 The OpenZipkin Authors * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except * in compliance with the License. You may obtain a copy of the License at @@ -20,9 +20,9 @@ import org.openjdk.jmh.runner.RunnerException; import org.openjdk.jmh.runner.options.Options; import org.openjdk.jmh.runner.options.OptionsBuilder; -import zipkin2.CheckResult; +import zipkin2.reporter.CheckResult; import zipkin2.reporter.Sender; -import zipkin2.reporter.SenderBenchmarks; +import zipkin2.reporter.internal.SenderBenchmarks; public class RabbitMQSenderBenchmarks extends SenderBenchmarks { private Channel channel; diff --git a/benchmarks/src/main/java/zipkin2/reporter/AsyncReporterBenchmarks.java b/benchmarks/src/main/java/zipkin2/reporter/internal/AsyncReporterBenchmarks.java similarity index 87% rename from benchmarks/src/main/java/zipkin2/reporter/AsyncReporterBenchmarks.java rename to benchmarks/src/main/java/zipkin2/reporter/internal/AsyncReporterBenchmarks.java index 0c87878e..946a97ea 100644 --- a/benchmarks/src/main/java/zipkin2/reporter/AsyncReporterBenchmarks.java +++ b/benchmarks/src/main/java/zipkin2/reporter/internal/AsyncReporterBenchmarks.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2019 The OpenZipkin Authors + * Copyright 2016-2024 The OpenZipkin Authors * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except * in compliance with the License. You may obtain a copy of the License at @@ -11,9 +11,8 @@ * or implied. See the License for the specific language governing permissions and limitations under * the License. */ -package zipkin2.reporter; +package zipkin2.reporter.internal; -import java.io.IOException; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicLong; import org.openjdk.jmh.annotations.AuxCounters; @@ -34,7 +33,9 @@ import org.openjdk.jmh.annotations.Warmup; import zipkin2.Span; import zipkin2.TestObjects; -import zipkin2.codec.Encoding; +import zipkin2.reporter.Encoding; +import zipkin2.reporter.InMemoryReporterMetrics; +import zipkin2.reporter.SpanBytesEncoder; @Measurement(iterations = 5, time = 1) @Warmup(iterations = 10, time = 1) @@ -85,10 +86,10 @@ public void clean() { @Setup(Level.Trial) public void setup() { - reporter = AsyncReporter.builder(new NoopSender(encoding)) - .messageMaxBytes(1000000) // example default from Kafka message.max.bytes - .metrics(metrics) - .build(); + reporter = AsyncReporter.newBuilder(new NoopSender(encoding)) + .messageMaxBytes(1000000) // example default from Kafka message.max.bytes + .metrics(metrics) + .build(SpanBytesEncoder.JSON_V2); } @Benchmark @Group("no_contention") @GroupThreads(1) @@ -107,7 +108,7 @@ public void high_contention_report(InMemoryReporterMetricsAsCounters counters) { } @TearDown(Level.Iteration) - public void clear() throws IOException { + public void clear() { spanBacklog.addAndGet(((AsyncReporter.BoundedAsyncReporter) reporter).pending.clear()); } } diff --git a/benchmarks/src/main/java/zipkin2/reporter/ByteBoundedQueueBenchmarks.java b/benchmarks/src/main/java/zipkin2/reporter/internal/ByteBoundedQueueBenchmarks.java similarity index 95% rename from benchmarks/src/main/java/zipkin2/reporter/ByteBoundedQueueBenchmarks.java rename to benchmarks/src/main/java/zipkin2/reporter/internal/ByteBoundedQueueBenchmarks.java index 90572425..a0f51793 100644 --- a/benchmarks/src/main/java/zipkin2/reporter/ByteBoundedQueueBenchmarks.java +++ b/benchmarks/src/main/java/zipkin2/reporter/internal/ByteBoundedQueueBenchmarks.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2019 The OpenZipkin Authors + * Copyright 2016-2024 The OpenZipkin Authors * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except * in compliance with the License. You may obtain a copy of the License at @@ -11,7 +11,7 @@ * or implied. See the License for the specific language governing permissions and limitations under * the License. */ -package zipkin2.reporter; +package zipkin2.reporter.internal; import java.util.concurrent.TimeUnit; import org.openjdk.jmh.annotations.AuxCounters; @@ -143,8 +143,8 @@ public void emptyQ() { // Convenience main entry-point public static void main(String[] args) throws RunnerException { Options opt = new OptionsBuilder() - .include(".*" + ByteBoundedQueueBenchmarks.class.getSimpleName() + ".*") - .build(); + .include(".*" + ByteBoundedQueueBenchmarks.class.getSimpleName() + ".*") + .build(); new Runner(opt).run(); } diff --git a/benchmarks/src/main/java/zipkin2/reporter/NoopSender.java b/benchmarks/src/main/java/zipkin2/reporter/internal/NoopSender.java similarity index 86% rename from benchmarks/src/main/java/zipkin2/reporter/NoopSender.java rename to benchmarks/src/main/java/zipkin2/reporter/internal/NoopSender.java index 94da6519..7b444a68 100644 --- a/benchmarks/src/main/java/zipkin2/reporter/NoopSender.java +++ b/benchmarks/src/main/java/zipkin2/reporter/internal/NoopSender.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2019 The OpenZipkin Authors + * Copyright 2016-2024 The OpenZipkin Authors * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except * in compliance with the License. You may obtain a copy of the License at @@ -11,12 +11,14 @@ * or implied. See the License for the specific language governing permissions and limitations under * the License. */ -package zipkin2.reporter; +package zipkin2.reporter.internal; import java.util.List; -import zipkin2.Call; -import zipkin2.CheckResult; -import zipkin2.codec.Encoding; +import zipkin2.reporter.BytesMessageEncoder; +import zipkin2.reporter.Call; +import zipkin2.reporter.CheckResult; +import zipkin2.reporter.Encoding; +import zipkin2.reporter.Sender; final class NoopSender extends Sender { diff --git a/benchmarks/src/main/java/zipkin2/reporter/SenderBenchmarks.java b/benchmarks/src/main/java/zipkin2/reporter/internal/SenderBenchmarks.java similarity index 90% rename from benchmarks/src/main/java/zipkin2/reporter/SenderBenchmarks.java rename to benchmarks/src/main/java/zipkin2/reporter/internal/SenderBenchmarks.java index 2dbe92c0..f32c570d 100644 --- a/benchmarks/src/main/java/zipkin2/reporter/SenderBenchmarks.java +++ b/benchmarks/src/main/java/zipkin2/reporter/internal/SenderBenchmarks.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2023 The OpenZipkin Authors + * Copyright 2016-2024 The OpenZipkin Authors * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except * in compliance with the License. You may obtain a copy of the License at @@ -11,7 +11,7 @@ * or implied. See the License for the specific language governing permissions and limitations under * the License. */ -package zipkin2.reporter; +package zipkin2.reporter.internal; import java.util.concurrent.TimeUnit; import org.openjdk.jmh.annotations.AuxCounters; @@ -29,10 +29,12 @@ import org.openjdk.jmh.annotations.TearDown; import org.openjdk.jmh.annotations.Threads; import org.openjdk.jmh.annotations.Warmup; -import zipkin2.CheckResult; import zipkin2.Span; import zipkin2.TestObjects; -import zipkin2.codec.SpanBytesEncoder; +import zipkin2.reporter.CheckResult; +import zipkin2.reporter.InMemoryReporterMetrics; +import zipkin2.reporter.Sender; +import zipkin2.reporter.SpanBytesEncoder; /** * This benchmark reports spans as fast as possible. The sender clears the queue as fast as @@ -88,6 +90,7 @@ public void clean() { } Sender sender; + AsyncReporter.BoundedAsyncReporter reporter; @Setup(Level.Trial) @@ -97,17 +100,17 @@ public void setup() throws Throwable { CheckResult senderCheck = sender.check(); if (!senderCheck.ok()) throw senderCheck.error(); - reporter = (AsyncReporter.BoundedAsyncReporter) AsyncReporter.builder(sender) - .messageMaxBytes(messageMaxBytes) - .queuedMaxSpans(TARGET_BACKLOG) - .metrics(metrics).build(); + reporter = (AsyncReporter.BoundedAsyncReporter) AsyncReporter.newBuilder(sender) + .messageMaxBytes(messageMaxBytes) + .queuedMaxSpans(TARGET_BACKLOG) + .metrics(metrics).build(SpanBytesEncoder.JSON_V2); } protected abstract Sender createSender() throws Exception; @Setup(Level.Iteration) public void fillQueue() { - while (reporter.pending.offer(clientSpan, clientSpanBytes.length)); + while (reporter.pending.offer(clientSpan, clientSpanBytes.length)) ; } @TearDown(Level.Iteration) diff --git a/bom/pom.xml b/bom/pom.xml index a86e06aa..9ef30a99 100644 --- a/bom/pom.xml +++ b/bom/pom.xml @@ -20,7 +20,7 @@ io.zipkin.reporter2 zipkin-reporter-bom Zipkin Reporter BOM - 2.17.3-SNAPSHOT + 3.0.0-SNAPSHOT pom Bill Of Materials POM for all Zipkin reporter artifacts diff --git a/brave/pom.xml b/brave/pom.xml index 3508479b..71f4ee93 100644 --- a/brave/pom.xml +++ b/brave/pom.xml @@ -18,7 +18,7 @@ io.zipkin.reporter2 zipkin-reporter-parent - 2.17.3-SNAPSHOT + 3.0.0-SNAPSHOT 4.0.0 @@ -38,6 +38,22 @@ ${project.groupId} zipkin-reporter ${project.version} + + + + io.zipkin.zipkin2 + zipkin + + + + + + io.zipkin.zipkin2 + zipkin + ${zipkin.version} + true io.zipkin.brave @@ -67,6 +83,45 @@ + + + + maven-invoker-plugin + + + de.qaware.maven + go-offline-maven-plugin + + + package + + resolve-dependencies + + + + + + + + ${project.groupId} + zipkin-sender-okhttp3 + ${project.version} + MAIN + jar + + + com.squareup.okhttp3 + mockwebserver + ${okhttp.version} + MAIN + jar + + + + + + + release diff --git a/brave/src/it/no_zipkin_deps/README.md b/brave/src/it/no_zipkin_deps/README.md new file mode 100644 index 00000000..719ada81 --- /dev/null +++ b/brave/src/it/no_zipkin_deps/README.md @@ -0,0 +1,3 @@ +# no_zipkin_deps +This tests that the brave reporter module does not require the optional +zipkin dependency. diff --git a/brave/src/it/no_zipkin_deps/pom.xml b/brave/src/it/no_zipkin_deps/pom.xml new file mode 100644 index 00000000..dab90ac3 --- /dev/null +++ b/brave/src/it/no_zipkin_deps/pom.xml @@ -0,0 +1,99 @@ + + + + 4.0.0 + + @project.groupId@ + no_zipkin_deps + @project.version@ + no_zipkin_deps + + + UTF-8 + UTF-8 + + 1.8 + 1.8 + + + + + @project.groupId@ + zipkin-reporter-brave + @project.version@ + + + io.zipkin.brave + brave + @brave.version@ + + + + * + * + + + + + @project.groupId@ + zipkin-sender-okhttp3 + @project.version@ + + + + org.junit.jupiter + junit-jupiter + @junit-jupiter.version@ + + + + org.assertj + assertj-core + @assertj.version@ + + + + com.squareup.okhttp3 + mockwebserver + @okhttp.version@ + + + + + + org.apache.logging.log4j + log4j-core + @log4j.version@ + + + org.apache.logging.log4j + log4j-jul + @log4j.version@ + + + + + + + maven-surefire-plugin + @maven-surefire-plugin.version@ + + + + diff --git a/brave/src/it/no_zipkin_deps/src/test/java/zipkin2/reporter/brave/no_zipkin_deps/AsyncZipkinSpanHandlerTest.java b/brave/src/it/no_zipkin_deps/src/test/java/zipkin2/reporter/brave/no_zipkin_deps/AsyncZipkinSpanHandlerTest.java new file mode 100644 index 00000000..11e73ecd --- /dev/null +++ b/brave/src/it/no_zipkin_deps/src/test/java/zipkin2/reporter/brave/no_zipkin_deps/AsyncZipkinSpanHandlerTest.java @@ -0,0 +1,81 @@ +/* + * Copyright 2016-2024 The OpenZipkin Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ +package zipkin2.reporter.brave.no_zipkin_deps; + +import brave.handler.MutableSpan; +import brave.handler.SpanHandler; +import brave.propagation.B3SingleFormat; +import brave.propagation.TraceContext; +import java.io.IOException; +import java.util.concurrent.TimeUnit; +import okhttp3.mockwebserver.MockResponse; +import okhttp3.mockwebserver.MockWebServer; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Test; +import zipkin2.reporter.brave.AsyncZipkinSpanHandler; +import zipkin2.reporter.okhttp3.OkHttpSender; + +import static java.nio.charset.StandardCharsets.UTF_8; +import static org.assertj.core.api.Assertions.assertThat; + +class AsyncZipkinSpanHandlerTest { + MockWebServer server = new MockWebServer(); + + @AfterEach void closeServer() throws IOException { + server.close(); + } + + String endpoint = server.url("/api/v2/spans").toString(); + OkHttpSender sender = + OkHttpSender.newBuilder().endpoint(endpoint).compressionEnabled(false).build(); + + @Test void sendsSpans() throws Exception { + server.enqueue(new MockResponse()); + + try (AsyncZipkinSpanHandler zipkinSpanHandler = AsyncZipkinSpanHandler.newBuilder(sender) + .messageTimeout(0, TimeUnit.MILLISECONDS) // don't spawn a thread + .build()) { + + TraceContext context = + B3SingleFormat.parseB3SingleFormat("50d980fffa300f29-86154a4ba6e91385-1").context(); + + MutableSpan span = new MutableSpan(context, null); + + span.localServiceName("Aa"); + span.localIp("1.2.3.4"); + span.localPort(80); + + span.name("test"); + span.startTimestamp(1L); + span.error(new RuntimeException("this cake is a lie")); + span.finishTimestamp(3L); + + zipkinSpanHandler.begin(context, span, null); + zipkinSpanHandler.end(context, span, SpanHandler.Cause.FINISHED); + zipkinSpanHandler.flush(); + + assertThat(server.takeRequest().getBody().readString(UTF_8)).isEqualTo("" + + "[{\"traceId\":\"50d980fffa300f29\"," + + "\"id\":\"86154a4ba6e91385\"," + + "\"name\":\"test\"," + + "\"timestamp\":1," + + "\"duration\":2," + + "\"localEndpoint\":{" + + "\"serviceName\":\"Aa\"," + + "\"ipv4\":\"1.2.3.4\"," + + "\"port\":80}," + + "\"tags\":{\"error\":\"this cake is a lie\"}}]"); + } + } +} diff --git a/brave/src/main/java/zipkin2/reporter/brave/AsyncZipkinSpanHandler.java b/brave/src/main/java/zipkin2/reporter/brave/AsyncZipkinSpanHandler.java index 35a7ad39..ec8e02aa 100644 --- a/brave/src/main/java/zipkin2/reporter/brave/AsyncZipkinSpanHandler.java +++ b/brave/src/main/java/zipkin2/reporter/brave/AsyncZipkinSpanHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2020 The OpenZipkin Authors + * Copyright 2016-2024 The OpenZipkin Authors * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except * in compliance with the License. You may obtain a copy of the License at @@ -14,15 +14,14 @@ package zipkin2.reporter.brave; import brave.Tag; +import brave.handler.MutableSpan; import brave.handler.SpanHandler; -import java.io.Closeable; import java.io.Flushable; import java.util.concurrent.ThreadFactory; import java.util.concurrent.TimeUnit; -import zipkin2.reporter.AsyncReporter; import zipkin2.reporter.ReporterMetrics; import zipkin2.reporter.Sender; -import zipkin2.reporter.internal.InternalReporter; +import zipkin2.reporter.internal.AsyncReporter; /** * A {@link brave.handler.SpanHandler} that queues spans on {@link #end} to bundle and send as a @@ -40,8 +39,7 @@ * @see brave.Tracing.Builder#addSpanHandler(SpanHandler) * @since 2.14 */ -public final class AsyncZipkinSpanHandler extends ZipkinSpanHandler - implements Closeable, Flushable { +public final class AsyncZipkinSpanHandler extends ZipkinSpanHandler implements Flushable { /** @since 2.14 */ public static AsyncZipkinSpanHandler create(Sender sender) { return newBuilder(sender).build(); @@ -53,7 +51,7 @@ public static Builder newBuilder(Sender sender) { return new Builder(sender); } - @Override public Builder toBuilder() { + public Builder toBuilder() { return new Builder(this); } @@ -61,14 +59,14 @@ public static Builder newBuilder(Sender sender) { public static final class Builder extends ZipkinSpanHandler.Builder { final AsyncReporter.Builder delegate; - Builder(AsyncZipkinSpanHandler zipkinSpanHandler) { - super(zipkinSpanHandler); - delegate = InternalReporter.instance.toBuilder( - (AsyncReporter) zipkinSpanHandler.spanReporter); + Builder(AsyncZipkinSpanHandler handler) { + this.delegate = ((AsyncReporter) handler.spanReporter).toBuilder(); + this.alwaysReportSpans = handler.alwaysReportSpans; + this.errorTag = handler.errorTag; } Builder(Sender sender) { - this.delegate = AsyncReporter.builder(sender); + this.delegate = AsyncReporter.newBuilder(sender); } /** @@ -149,15 +147,18 @@ public AsyncZipkinSpanHandler build() { } AsyncZipkinSpanHandler(Builder builder) { - super(builder.delegate.build(new JsonV2Encoder(builder.errorTag)), - builder.errorTag, builder.alwaysReportSpans); + super( + builder.delegate.build(new JsonV2Encoder(builder.errorTag)), + builder.errorTag, + builder.alwaysReportSpans + ); } @Override public void flush() { - ((AsyncReporter) spanReporter).flush(); + ((AsyncReporter) spanReporter).flush(); } @Override public void close() { - ((AsyncReporter) spanReporter).close(); + ((AsyncReporter) spanReporter).close(); } } diff --git a/brave/src/main/java/zipkin2/reporter/brave/JsonV2Encoder.java b/brave/src/main/java/zipkin2/reporter/brave/JsonV2Encoder.java index 0f143ad5..760ab43f 100644 --- a/brave/src/main/java/zipkin2/reporter/brave/JsonV2Encoder.java +++ b/brave/src/main/java/zipkin2/reporter/brave/JsonV2Encoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2020 The OpenZipkin Authors + * Copyright 2016-2024 The OpenZipkin Authors * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except * in compliance with the License. You may obtain a copy of the License at @@ -16,9 +16,8 @@ import brave.Tag; import brave.handler.MutableSpan; import brave.handler.MutableSpanBytesEncoder; -import java.util.List; -import zipkin2.codec.BytesEncoder; -import zipkin2.codec.Encoding; +import zipkin2.reporter.BytesEncoder; +import zipkin2.reporter.Encoding; final class JsonV2Encoder implements BytesEncoder { final MutableSpanBytesEncoder delegate; @@ -38,8 +37,4 @@ final class JsonV2Encoder implements BytesEncoder { @Override public byte[] encode(MutableSpan span) { return delegate.encode(span); } - - @Override public byte[] encodeList(List spans) { - return delegate.encodeList(spans); - } } diff --git a/brave/src/main/java/zipkin2/reporter/brave/ZipkinSpanHandler.java b/brave/src/main/java/zipkin2/reporter/brave/ZipkinSpanHandler.java index 32d03d39..0b2e3273 100644 --- a/brave/src/main/java/zipkin2/reporter/brave/ZipkinSpanHandler.java +++ b/brave/src/main/java/zipkin2/reporter/brave/ZipkinSpanHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2020 The OpenZipkin Authors + * Copyright 2016-2024 The OpenZipkin Authors * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except * in compliance with the License. You may obtain a copy of the License at @@ -20,7 +20,7 @@ import brave.propagation.TraceContext; import java.io.Closeable; import zipkin2.Span; -import zipkin2.codec.SpanBytesEncoder; +import zipkin2.reporter.SpanBytesEncoder; import zipkin2.reporter.Reporter; /** @@ -83,11 +83,6 @@ public static abstract class Builder { Tag errorTag = Tags.ERROR; boolean alwaysReportSpans; - Builder(ZipkinSpanHandler zipkinSpanHandler) { - errorTag = zipkinSpanHandler.errorTag; - this.alwaysReportSpans = zipkinSpanHandler.alwaysReportSpans; - } - Builder() { // sealed } diff --git a/brave/src/test/java/zipkin2/reporter/brave/BasicUsageTest.java b/brave/src/test/java/zipkin2/reporter/brave/BasicUsageTest.java index 4a28b9e2..5b610522 100644 --- a/brave/src/test/java/zipkin2/reporter/brave/BasicUsageTest.java +++ b/brave/src/test/java/zipkin2/reporter/brave/BasicUsageTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2023 The OpenZipkin Authors + * Copyright 2016-2024 The OpenZipkin Authors * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except * in compliance with the License. You may obtain a copy of the License at @@ -36,11 +36,11 @@ abstract class BasicUsageTest { @BeforeEach void init() { zipkinSpanHandler = zipkinSpanHandler(spans); tracing = Tracing.newBuilder() - .localServiceName("Aa") - .localIp("1.2.3.4") - .localPort(80) - .addSpanHandler(zipkinSpanHandler) - .build(); + .localServiceName("Aa") + .localIp("1.2.3.4") + .localPort(80) + .addSpanHandler(zipkinSpanHandler) + .build(); } @AfterEach void close() { @@ -53,15 +53,15 @@ abstract class BasicUsageTest { zipkinSpanHandler = (H) zipkinSpanHandler.toBuilder().alwaysReportSpans(true).build(); tracing = Tracing.newBuilder() - .localServiceName("Aa") - .localIp("1.2.3.4") - .localPort(80) - .addSpanHandler(zipkinSpanHandler) - .alwaysSampleLocal() - .build(); + .localServiceName("Aa") + .localIp("1.2.3.4") + .localPort(80) + .addSpanHandler(zipkinSpanHandler) + .alwaysSampleLocal() + .build(); brave.Span unsampledRemote = - tracing.tracer().nextSpan(TraceContextOrSamplingFlags.NOT_SAMPLED).name("test").start(1L); + tracing.tracer().nextSpan(TraceContextOrSamplingFlags.NOT_SAMPLED).name("test").start(1L); assertThat(unsampledRemote.isNoop()).isFalse(); assertThat(unsampledRemote.context().sampled()).isFalse(); assertThat(unsampledRemote.context().sampledLocal()).isTrue(); @@ -76,27 +76,27 @@ abstract class BasicUsageTest { /** This mainly shows endpoints are taken from Brave, and error is back-filled. */ @Test void basicSpan() { TraceContext context = B3SingleFormat.parseB3SingleFormat( - "50d980fffa300f29-86154a4ba6e91385-1" + "50d980fffa300f29-86154a4ba6e91385-1" ).context(); tracing.tracer().toSpan(context).name("test") - .start(1L) - .error(new RuntimeException("this cake is a lie")) - .finish(3L); + .start(1L) + .error(new RuntimeException("this cake is a lie")) + .finish(3L); triggerReport(); assertThat(spans.get(0)).hasToString( - "{\"traceId\":\"50d980fffa300f29\"," - + "\"id\":\"86154a4ba6e91385\"," - + "\"name\":\"test\"," - + "\"timestamp\":1," - + "\"duration\":2," - + "\"localEndpoint\":{" - + "\"serviceName\":\"aa\"," - + "\"ipv4\":\"1.2.3.4\"," - + "\"port\":80}," - + "\"tags\":{\"error\":\"this cake is a lie\"}}" + "{\"traceId\":\"50d980fffa300f29\"," + + "\"id\":\"86154a4ba6e91385\"," + + "\"name\":\"test\"," + + "\"timestamp\":1," + + "\"duration\":2," + + "\"localEndpoint\":{" + + "\"serviceName\":\"aa\"," + + "\"ipv4\":\"1.2.3.4\"," + + "\"port\":80}," + + "\"tags\":{\"error\":\"this cake is a lie\"}}" ); } @@ -107,12 +107,12 @@ void triggerReport() { /** This shows that in practice, we don't report when the user tells us not to! */ @Test void abandonedSpan() { TraceContext context = B3SingleFormat.parseB3SingleFormat( - "50d980fffa300f29-86154a4ba6e91385-1" + "50d980fffa300f29-86154a4ba6e91385-1" ).context(); tracing.tracer().toSpan(context).name("test") - .start(1L) - .abandon(); // whoops.. don't need this one! + .start(1L) + .abandon(); // whoops.. don't need this one! triggerReport(); @@ -121,7 +121,7 @@ void triggerReport() { @Test void unsampledSpan() { brave.Span unsampledRemote = - tracing.tracer().nextSpan(TraceContextOrSamplingFlags.NOT_SAMPLED).name("test").start(1L); + tracing.tracer().nextSpan(TraceContextOrSamplingFlags.NOT_SAMPLED).name("test").start(1L); assertThat(unsampledRemote.isNoop()).isTrue(); assertThat(unsampledRemote.context().sampled()).isFalse(); assertThat(unsampledRemote.context().sampledLocal()).isFalse(); diff --git a/build-bin/test b/build-bin/test index d6611183..0f4782e9 100755 --- a/build-bin/test +++ b/build-bin/test @@ -4,4 +4,6 @@ # # See [README.md] for an explanation of this and how CI should use it. -./mvnw -T1C verify -nsu "$@" +# We use install, not verify, because maven-invoker-tests need modules in this +# project installed into the local repository before tests run. +./mvnw -T1C install -nsu "$@" diff --git a/core/pom.xml b/core/pom.xml index 5cbd13c6..c21cf515 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -20,7 +20,7 @@ io.zipkin.reporter2 zipkin-reporter-parent - 2.17.3-SNAPSHOT + 3.0.0-SNAPSHOT zipkin-reporter @@ -34,6 +34,8 @@ + io.zipkin.zipkin2 zipkin diff --git a/core/src/main/java/zipkin2/reporter/AsyncReporter.java b/core/src/main/java/zipkin2/reporter/AsyncReporter.java index ea68b21b..a655afe4 100644 --- a/core/src/main/java/zipkin2/reporter/AsyncReporter.java +++ b/core/src/main/java/zipkin2/reporter/AsyncReporter.java @@ -13,27 +13,10 @@ */ package zipkin2.reporter; -import static java.lang.String.format; -import static java.util.logging.Level.FINE; -import static java.util.logging.Level.WARNING; - import java.io.Flushable; -import java.util.ArrayList; import java.util.List; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.Executors; import java.util.concurrent.ThreadFactory; import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.logging.Level; -import java.util.logging.Logger; - -import zipkin2.Call; -import zipkin2.CheckResult; -import zipkin2.Component; -import zipkin2.Span; -import zipkin2.codec.BytesEncoder; -import zipkin2.codec.SpanBytesEncoder; /** * As spans are reported, they are encoded and added to a pending queue. The task of sending spans @@ -50,7 +33,9 @@ * * @param type of the span, usually {@link zipkin2.Span} */ -public abstract class AsyncReporter extends Component implements Reporter, Flushable { +// This is effectively, but not explicitly final as it was not final in version 2.x. +public class AsyncReporter extends Component implements Reporter, Flushable { + /** * Builds a json reporter for Zipkin V2. If http, * the endpoint of the sender is usually "http://zipkinhost:9411/api/v2/spans". @@ -67,6 +52,16 @@ public static Builder builder(Sender sender) { return new Builder(sender); } + final zipkin2.reporter.internal.AsyncReporter delegate; + + AsyncReporter(zipkin2.reporter.internal.AsyncReporter delegate) { + this.delegate = delegate; + } + + @Override public void report(S span) { + delegate.report(span); + } + /** * Calling this will flush any pending spans to the transport on the current thread. * @@ -75,50 +70,33 @@ public static Builder builder(Sender sender) { * * @throws IllegalStateException if closed */ - @Override public abstract void flush(); + @Override public void flush() { + delegate.flush(); + } /** Shuts down the sender thread, and increments drop metrics if there were any unsent spans. */ - @Override public abstract void close(); - - public static final class Builder { - final Sender sender; - ThreadFactory threadFactory = Executors.defaultThreadFactory(); - ReporterMetrics metrics = ReporterMetrics.NOOP_METRICS; - int messageMaxBytes; - long messageTimeoutNanos = TimeUnit.SECONDS.toNanos(1); - long closeTimeoutNanos = TimeUnit.SECONDS.toNanos(1); - int queuedMaxSpans = 10000; - int queuedMaxBytes = onePercentOfMemory(); + @Override public void close() { + delegate.close(); + } - Builder(BoundedAsyncReporter asyncReporter) { - this.sender = asyncReporter.sender; - this.threadFactory = asyncReporter.threadFactory; - this.metrics = asyncReporter.metrics; - this.messageMaxBytes = asyncReporter.messageMaxBytes; - this.messageTimeoutNanos = asyncReporter.messageTimeoutNanos; - this.closeTimeoutNanos = asyncReporter.closeTimeoutNanos; - this.queuedMaxSpans = asyncReporter.pending.maxSize; - this.queuedMaxBytes = asyncReporter.pending.maxBytes; - } + @Override public String toString() { + return delegate.toString(); + } - static int onePercentOfMemory() { - long result = (long) (Runtime.getRuntime().totalMemory() * 0.01); - // don't overflow in the rare case 1% of memory is larger than 2 GiB! - return (int) Math.max(Math.min(Integer.MAX_VALUE, result), Integer.MIN_VALUE); - } + public static final class Builder { + final zipkin2.reporter.internal.AsyncReporter.Builder delegate; + final Encoding encoding; Builder(Sender sender) { - if (sender == null) throw new NullPointerException("sender == null"); - this.sender = sender; - this.messageMaxBytes = sender.messageMaxBytes(); + this.delegate = zipkin2.reporter.internal.AsyncReporter.newBuilder(sender); + this.encoding = sender.encoding(); } /** - * Launches the flush thread when {@link #messageTimeoutNanos} is greater than zero. + * Launches the flush thread when {@link #messageTimeout} is greater than zero. */ public Builder threadFactory(ThreadFactory threadFactory) { - if (threadFactory == null) throw new NullPointerException("threadFactory == null"); - this.threadFactory = threadFactory; + this.delegate.threadFactory(threadFactory); return this; } @@ -126,8 +104,7 @@ public Builder threadFactory(ThreadFactory threadFactory) { * Aggregates and reports reporter metrics to a monitoring system. Defaults to no-op. */ public Builder metrics(ReporterMetrics metrics) { - if (metrics == null) throw new NullPointerException("metrics == null"); - this.metrics = metrics; + this.delegate.metrics(metrics); return this; } @@ -136,10 +113,7 @@ public Builder metrics(ReporterMetrics metrics) { * Sender#messageMaxBytes()}. */ public Builder messageMaxBytes(int messageMaxBytes) { - if (messageMaxBytes < 0) { - throw new IllegalArgumentException("messageMaxBytes < 0: " + messageMaxBytes); - } - this.messageMaxBytes = Math.min(messageMaxBytes, sender.messageMaxBytes()); + this.delegate.messageMaxBytes(messageMaxBytes); return this; } @@ -153,35 +127,31 @@ public Builder messageMaxBytes(int messageMaxBytes) { *

Note: this timeout starts when the first unsent span is reported. */ public Builder messageTimeout(long timeout, TimeUnit unit) { - if (timeout < 0) throw new IllegalArgumentException("messageTimeout < 0: " + timeout); - if (unit == null) throw new NullPointerException("unit == null"); - this.messageTimeoutNanos = unit.toNanos(timeout); + this.delegate.messageTimeout(timeout, unit); return this; } /** How long to block for in-flight spans to send out-of-process on close. Default 1 second */ public Builder closeTimeout(long timeout, TimeUnit unit) { - if (timeout < 0) throw new IllegalArgumentException("closeTimeout < 0: " + timeout); - if (unit == null) throw new NullPointerException("unit == null"); - this.closeTimeoutNanos = unit.toNanos(timeout); + this.delegate.closeTimeout(timeout, unit); return this; } /** Maximum backlog of spans reported vs sent. Default 10000 */ public Builder queuedMaxSpans(int queuedMaxSpans) { - this.queuedMaxSpans = queuedMaxSpans; + this.delegate.queuedMaxSpans(queuedMaxSpans); return this; } /** Maximum backlog of span bytes reported vs sent. Default 1% of heap */ public Builder queuedMaxBytes(int queuedMaxBytes) { - this.queuedMaxBytes = queuedMaxBytes; + this.delegate.queuedMaxBytes(queuedMaxBytes); return this; } /** Builds an async reporter that encodes zipkin spans as they are reported. */ - public AsyncReporter build() { - switch (sender.encoding()) { + public AsyncReporter build() { + switch (encoding) { case JSON: return build(SpanBytesEncoder.JSON_V2); case PROTO3: @@ -189,213 +159,37 @@ public AsyncReporter build() { case THRIFT: return build(SpanBytesEncoder.THRIFT); default: - throw new UnsupportedOperationException(sender.encoding().name()); + throw new UnsupportedOperationException(encoding.name()); } } /** Builds an async reporter that encodes arbitrary spans as they are reported. */ public AsyncReporter build(BytesEncoder encoder) { if (encoder == null) throw new NullPointerException("encoder == null"); - - if (encoder.encoding() != sender.encoding()) { - throw new IllegalArgumentException(String.format( - "Encoder doesn't match Sender: %s %s", encoder.encoding(), sender.encoding())); - } - - return new BoundedAsyncReporter(this, encoder); + return new AsyncReporter(delegate.build(new BytesEncoderAdapter(encoder))); } } - static final class BoundedAsyncReporter extends AsyncReporter { - static final Logger logger = Logger.getLogger(BoundedAsyncReporter.class.getName()); - final AtomicBoolean started, closed; - final BytesEncoder encoder; - final ByteBoundedQueue pending; - final Sender sender; - final int messageMaxBytes; - final long messageTimeoutNanos, closeTimeoutNanos; - final CountDownLatch close; - final ReporterMetrics metrics; - final ThreadFactory threadFactory; - - /** Tracks if we should log the first instance of an exception in flush(). */ - private boolean shouldWarnException = true; - - BoundedAsyncReporter(Builder builder, BytesEncoder encoder) { - this.pending = new ByteBoundedQueue(builder.queuedMaxSpans, builder.queuedMaxBytes); - this.sender = builder.sender; - this.messageMaxBytes = builder.messageMaxBytes; - this.messageTimeoutNanos = builder.messageTimeoutNanos; - this.closeTimeoutNanos = builder.closeTimeoutNanos; - this.closed = new AtomicBoolean(false); - // pretend we already started when config implies no thread that flushes the queue in a loop. - this.started = new AtomicBoolean(builder.messageTimeoutNanos == 0); - this.close = new CountDownLatch(builder.messageTimeoutNanos > 0 ? 1 : 0); - this.metrics = builder.metrics; - this.threadFactory = builder.threadFactory; - this.encoder = encoder; - } - - void startFlusherThread() { - BufferNextMessage consumer = - BufferNextMessage.create(encoder.encoding(), messageMaxBytes, messageTimeoutNanos); - Thread flushThread = threadFactory.newThread(new Flusher(this, consumer)); - flushThread.setName("AsyncReporter{" + sender + "}"); - flushThread.setDaemon(true); - flushThread.start(); - } - - @Override public void report(S next) { - if (next == null) throw new NullPointerException("span == null"); - // Lazy start so that reporters never used don't spawn threads - if (started.compareAndSet(false, true)) startFlusherThread(); - metrics.incrementSpans(1); - int nextSizeInBytes = encoder.sizeInBytes(next); - int messageSizeOfNextSpan = sender.messageSizeInBytes(nextSizeInBytes); - metrics.incrementSpanBytes(nextSizeInBytes); - if (closed.get() || - // don't enqueue something larger than we can drain - messageSizeOfNextSpan > messageMaxBytes || - !pending.offer(next, nextSizeInBytes)) { - metrics.incrementSpansDropped(1); - } - } - - @Override public final void flush() { - if (closed.get()) throw new ClosedSenderException(); - flush(BufferNextMessage.create(encoder.encoding(), messageMaxBytes, 0)); - } - - void flush(BufferNextMessage bundler) { - pending.drainTo(bundler, bundler.remainingNanos()); - - // record after flushing reduces the amount of gauge events vs on doing this on report - metrics.updateQueuedSpans(pending.count); - metrics.updateQueuedBytes(pending.sizeInBytes); - - // loop around if we are running, and the bundle isn't full - // if we are closed, try to send what's pending - if (!bundler.isReady() && !closed.get()) return; - - // Signal that we are about to send a message of a known size in bytes - metrics.incrementMessages(); - metrics.incrementMessageBytes(bundler.sizeInBytes()); - - // Create the next message. Since we are outside the lock shared with writers, we can encode - final ArrayList nextMessage = new ArrayList(bundler.count()); - bundler.drain(new SpanWithSizeConsumer() { - @Override public boolean offer(S next, int nextSizeInBytes) { - nextMessage.add(encoder.encode(next)); // speculatively add to the pending message - if (sender.messageSizeInBytes(nextMessage) > messageMaxBytes) { - // if we overran the message size, remove the encoded message. - nextMessage.remove(nextMessage.size() - 1); - return false; - } - return true; - } - }); - - try { - sender.sendSpans(nextMessage).execute(); - } catch (Throwable t) { - // In failure case, we increment messages and spans dropped. - int count = nextMessage.size(); - Call.propagateIfFatal(t); - metrics.incrementMessagesDropped(t); - metrics.incrementSpansDropped(count); - - Level logLevel = FINE; - - if (shouldWarnException) { - logger.log(WARNING, "Spans were dropped due to exceptions. " - + "All subsequent errors will be logged at FINE level."); - logLevel = WARNING; - shouldWarnException = false; - } - - if (logger.isLoggable(logLevel)) { - logger.log(logLevel, - format("Dropped %s spans due to %s(%s)", count, t.getClass().getSimpleName(), - t.getMessage() == null ? "" : t.getMessage()), t); - } - - // Raise in case the sender was closed out-of-band. - if (t instanceof ClosedSenderException) throw (ClosedSenderException) t; - - // Old senders in other artifacts may be using this less precise way of indicating they've been closed - // out-of-band. - if (t instanceof IllegalStateException && t.getMessage().equals("closed")) - throw (IllegalStateException) t; - } - } - - @Override public CheckResult check() { - return sender.check(); - } - - @Override public void close() { - if (!closed.compareAndSet(false, true)) return; // already closed - started.set(true); // prevent anything from starting the thread after close! - try { - // wait for in-flight spans to send - if (!close.await(closeTimeoutNanos, TimeUnit.NANOSECONDS)) { - logger.warning("Timed out waiting for in-flight spans to send"); - } - } catch (InterruptedException e) { - logger.warning("Interrupted waiting for in-flight spans to send"); - Thread.currentThread().interrupt(); - } - int count = pending.clear(); - if (count > 0) { - metrics.incrementSpansDropped(count); - logger.warning("Dropped " + count + " spans due to AsyncReporter.close()"); - } - } - - Builder toBuilder() { - return new Builder(this); + static final class BytesEncoderAdapterimplements BytesEncoder { + final BytesEncoder delegate; + BytesEncoderAdapter(BytesEncoder delegate) { + this.delegate = delegate; } - @Override public String toString() { - return "AsyncReporter{" + sender + "}"; + @Override public Encoding encoding() { + return delegate.encoding(); } - } - - static final class Flusher implements Runnable { - static final Logger logger = Logger.getLogger(Flusher.class.getName()); - final BoundedAsyncReporter result; - final BufferNextMessage consumer; - - Flusher(BoundedAsyncReporter result, BufferNextMessage consumer) { - this.result = result; - this.consumer = consumer; + @Override public int sizeInBytes(S input) { + return delegate.sizeInBytes(input); } - @Override public void run() { - try { - while (!result.closed.get()) { - result.flush(consumer); - } - } catch (RuntimeException e) { - logger.log(Level.WARNING, "Unexpected error flushing spans", e); - throw e; - } catch (Error e) { - logger.log(Level.WARNING, "Unexpected error flushing spans", e); - throw e; - } finally { - int count = consumer.count(); - if (count > 0) { - result.metrics.incrementSpansDropped(count); - logger.warning("Dropped " + count + " spans due to AsyncReporter.close()"); - } - result.close.countDown(); - } + @Override public byte[] encode(S input) { + return delegate.encode(input); } @Override public String toString() { - return "AsyncReporter{" + result.sender + "}"; + return delegate.toString(); } } - } diff --git a/core/src/main/java/zipkin2/reporter/AwaitableCallback.java b/core/src/main/java/zipkin2/reporter/AwaitableCallback.java index f5ac5c56..34bfae83 100644 --- a/core/src/main/java/zipkin2/reporter/AwaitableCallback.java +++ b/core/src/main/java/zipkin2/reporter/AwaitableCallback.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2019 The OpenZipkin Authors + * Copyright 2016-2024 The OpenZipkin Authors * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except * in compliance with the License. You may obtain a copy of the License at @@ -14,7 +14,6 @@ package zipkin2.reporter; import java.util.concurrent.CountDownLatch; -import zipkin2.Callback; /** * Blocks until {@link Callback#onSuccess(Object)} or {@link Callback#onError(Throwable)}. diff --git a/core/src/main/java/zipkin2/reporter/BytesEncoder.java b/core/src/main/java/zipkin2/reporter/BytesEncoder.java new file mode 100644 index 00000000..7591f764 --- /dev/null +++ b/core/src/main/java/zipkin2/reporter/BytesEncoder.java @@ -0,0 +1,29 @@ +/* + * Copyright 2016-2024 The OpenZipkin Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ +package zipkin2.reporter; + +/** + * Utility for encoding one or more elements of a type into a byte array. + * + * @param type of the object to encode + * @since 3.0 + */ +public interface BytesEncoder { + Encoding encoding(); + + int sizeInBytes(S input); + + /** Serializes an object into its binary form. */ + byte[] encode(S input); +} diff --git a/core/src/main/java/zipkin2/reporter/BytesMessageEncoder.java b/core/src/main/java/zipkin2/reporter/BytesMessageEncoder.java index f39ba491..f7a2d325 100644 --- a/core/src/main/java/zipkin2/reporter/BytesMessageEncoder.java +++ b/core/src/main/java/zipkin2/reporter/BytesMessageEncoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2019 The OpenZipkin Authors + * Copyright 2016-2024 The OpenZipkin Authors * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except * in compliance with the License. You may obtain a copy of the License at @@ -14,8 +14,6 @@ package zipkin2.reporter; import java.util.List; -import zipkin2.codec.BytesEncoder; -import zipkin2.codec.Encoding; /** * Senders like Kafka use byte[] message encoding. This provides helpers to concatenate spans into a diff --git a/core/src/main/java/zipkin2/reporter/Call.java b/core/src/main/java/zipkin2/reporter/Call.java new file mode 100644 index 00000000..a15a777c --- /dev/null +++ b/core/src/main/java/zipkin2/reporter/Call.java @@ -0,0 +1,429 @@ +/* + * Copyright 2016-2024 The OpenZipkin Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ +package zipkin2.reporter; + +import java.io.IOException; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.atomic.AtomicReference; + +/** + * This captures a (usually remote) request and can be used once, either {@link #execute() + * synchronously} or {@link #enqueue(Callback) asynchronously}. At any time, from any thread, you + * can call {@linkplain #cancel()}, which might stop an in-flight request or prevent one from + * occurring. + * + *

Implementations should prepare a call such that there's little or no likelihood of late + * runtime exceptions. For example, if the call is to get a trace, the call to {@code listSpans} + * should propagate input errors vs delay them until a call to {@linkplain #execute()} or + * {@linkplain #enqueue(Callback)}. + * + *

Ex. + *

{@code
+ * // Any translation of an input request to remote parameters should happen here, and any related
+ * // errors should propagate here.
+ * Call>> listTraces = spanStore.listTraces(request);
+ * // When this executes, it should simply run the remote request.
+ * List trace = getTraceCall.execute();
+ * }
+ * + *

An instance of call cannot be invoked more than once, but you can {@linkplain #clone()} an + * instance if you need to replay the call. There is no relationship between a call and a number of + * remote requests. For example, an implementation that stores spans may make hundreds of remote + * requests, possibly retrying on your behalf. + * + *

This type owes its design to {@code retrofit2.Call}, which is nearly the same, except limited + * to HTTP transports. + * + * @param the success type, typically not null except when {@code V} is {@linkplain Void}. + * @since 3.0 + */ +public abstract class Call implements Cloneable { + /** + * Returns a completed call which has the supplied value. This is useful when input parameters + * imply there's no call needed. For example, an empty input might always result in an empty + * output. + */ + public static Call create(V v) { + return new Constant(v); + } + + @SuppressWarnings("unchecked") + public static Call> emptyList() { + return Call.create(Collections.emptyList()); + } + + public interface Mapper { + V2 map(V1 input); + } + + /** + * Maps the result of this call into a different shape, as defined by the {@code mapper} function. + * This is used to convert values from one type to another. For example, you could use this to + * convert between zipkin v1 and v2 span format. + * + *

{@code
+   * getTracesV1Call = getTracesV2Call.map(traces -> v2TracesConverter);
+   * }
+ * + *

This method intends to be used for chaining. That means "this" instance should be discarded + * in favor of the result of this method. + */ + public final Call map(Mapper mapper) { + return new Mapping(mapper, this); + } + + public interface FlatMapper { + Call map(V1 input); + } + + /** + * Maps the result of this call into another, as defined by the {@code flatMapper} function. This + * is used to chain two remote calls together. For example, you could use this to chain a list IDs + * call to a get by IDs call. + * + *

{@code
+   * getTracesCall = getIdsCall.flatMap(ids -> getTraces(ids));
+   *
+   * // this would now invoke the chain
+   * traces = getTracesCall.enqueue(tracesCallback);
+   * }
+ *

+ * Cancelation propagates to the mapped call. + * + *

This method intends to be used for chaining. That means "this" instance should be discarded + * in favor of the result of this method. + */ + public final Call flatMap(FlatMapper flatMapper) { + return new FlatMapping(flatMapper, this); + } + + public interface ErrorHandler { + /** Attempts to resolve an error. The user must call the callback. */ + void onErrorReturn(Throwable error, Callback callback); + } + + /** + * Returns a call which can attempt to resolve an exception. This is useful when a remote call + * returns an error when a resource is not found. + * + *

Here's an example of coercing 404 to empty: + *

{@code
+   * call.handleError((error, callback) -> {
+   *   if (error instanceof HttpException && ((HttpException) error).code == 404) {
+   *     callback.onSuccess(Collections.emptyList());
+   *   } else {
+   *     callback.onError(error);
+   *   }
+   * });
+   * }
+ */ + public final Call handleError(ErrorHandler errorHandler) { + return new ErrorHandling(errorHandler, this); + } + + // Taken from RxJava throwIfFatal, which was taken from scala + public static void propagateIfFatal(Throwable t) { + if (t instanceof VirtualMachineError) { + throw (VirtualMachineError) t; + } else if (t instanceof ThreadDeath) { + throw (ThreadDeath) t; + } else if (t instanceof LinkageError) { + throw (LinkageError) t; + } + } + + /** + * Invokes a request, returning a success value or propagating an error to the caller. Invoking + * this more than once will result in an error. To repeat a call, make a copy with {@linkplain + * #clone()}. + * + *

Eventhough this is a blocking call, implementations may honor calls to {@linkplain + * #cancel()} from a different thread. + * + * @return a success value. Null is unexpected, except when {@code V} is {@linkplain Void}. + */ + public abstract V execute() throws IOException; + + /** + * Invokes a request asynchronously, signaling the {@code callback} when complete. Invoking this + * more than once will result in an error. To repeat a call, make a copy with {@linkplain + * #clone()}. + */ + public abstract void enqueue(Callback callback); + + /** + * Requests to cancel this call, even if some implementations may not support it. For example, a + * blocking call is sometimes not cancelable. + */ + // Boolean isn't returned because some implementations may cancel asynchronously. + // Implementing might throw an IOException on execute or callback.onError(IOException) + public abstract void cancel(); + + /** + * Returns true if {@linkplain #cancel()} was called. + * + *

Calls can fail before being canceled, so true does always mean cancelation caused a call to + * fail. That said, successful cancellation does result in a failure. + */ + // isCanceled exists while isExecuted does not because you do not need the latter to implement + // asynchronous bindings, such as rxjava2 + public abstract boolean isCanceled(); + + /** Returns a copy of this object, so you can make an identical follow-up request. */ + @Override public abstract Call clone(); + + static class Constant extends Base { // not final for mock testing + final V v; + + Constant(V v) { + this.v = v; + } + + @Override protected V doExecute() { + return v; + } + + @Override protected void doEnqueue(Callback callback) { + callback.onSuccess(v); + } + + @Override public Call clone() { + return new Constant(v); + } + + @Override public String toString() { + return "ConstantCall{value=" + v + "}"; + } + + @Override public boolean equals(Object o) { + if (o == this) return true; + if (o instanceof Constant) { + Constant that = (Constant) o; + return ((this.v == null) ? (that.v == null) : this.v.equals(that.v)); + } + return false; + } + + @Override + public int hashCode() { + int h = 1; + h *= 1000003; + h ^= (v == null) ? 0 : v.hashCode(); + return h; + } + } + + static final class Mapping extends Base { + final Mapper mapper; + final Call delegate; + + Mapping(Mapper mapper, Call delegate) { + this.mapper = mapper; + this.delegate = delegate; + } + + @Override protected R doExecute() throws IOException { + return mapper.map(delegate.execute()); + } + + @Override protected void doEnqueue(final Callback callback) { + delegate.enqueue(new Callback() { + @Override public void onSuccess(V value) { + try { + callback.onSuccess(mapper.map(value)); + } catch (Throwable t) { + callback.onError(t); + } + } + + @Override public void onError(Throwable t) { + callback.onError(t); + } + }); + } + + @Override public String toString() { + return "Mapping{call=" + delegate + ", mapper=" + mapper + "}"; + } + + @Override public Call clone() { + return new Mapping(mapper, delegate.clone()); + } + } + + static final class FlatMapping extends Base { + final FlatMapper flatMapper; + final Call delegate; + volatile Call mapped; + + FlatMapping(FlatMapper flatMapper, Call delegate) { + this.flatMapper = flatMapper; + this.delegate = delegate; + } + + @Override protected R doExecute() throws IOException { + return (mapped = flatMapper.map(delegate.execute())).execute(); + } + + @Override protected void doEnqueue(final Callback callback) { + delegate.enqueue(new Callback() { + @Override public void onSuccess(V value) { + try { + (mapped = flatMapper.map(value)).enqueue(callback); + } catch (Throwable t) { + propagateIfFatal(t); + callback.onError(t); + } + } + + @Override public void onError(Throwable t) { + callback.onError(t); + } + }); + } + + @Override protected void doCancel() { + delegate.cancel(); + if (mapped != null) mapped.cancel(); + } + + @Override public String toString() { + return "FlatMapping{call=" + delegate + ", flatMapper=" + flatMapper + "}"; + } + + @Override public Call clone() { + return new FlatMapping(flatMapper, delegate.clone()); + } + } + + static final class ErrorHandling extends Base { + static final Object SENTINEL = new Object(); // to differentiate from null + final ErrorHandler errorHandler; + final Call delegate; + + ErrorHandling(ErrorHandler errorHandler, Call delegate) { + this.errorHandler = errorHandler; + this.delegate = delegate; + } + + @Override protected V doExecute() throws IOException { + try { + return delegate.execute(); + } catch (IOException e) { + return handleError(e); + } catch (RuntimeException e) { + return handleError(e); + } catch (Error e) { + Call.propagateIfFatal(e); + return handleError(e); + } + } + + V handleError(T e) throws T { + final AtomicReference ref = new AtomicReference(SENTINEL); + errorHandler.onErrorReturn(e, new Callback() { + @Override + public void onSuccess(V value) { + ref.set(value); + } + + @Override + public void onError(Throwable t) { + } + }); + Object result = ref.get(); + if (SENTINEL == result) throw e; + return (V) result; + } + + @Override protected void doEnqueue(final Callback callback) { + delegate.enqueue(new Callback() { + @Override public void onSuccess(V value) { + callback.onSuccess(value); + } + + @Override public void onError(Throwable t) { + errorHandler.onErrorReturn(t, callback); + } + }); + } + + @Override protected void doCancel() { + delegate.cancel(); + } + + @Override public String toString() { + return "ErrorHandling{call=" + delegate + ", errorHandler=" + errorHandler + "}"; + } + + @Override public Call clone() { + return new ErrorHandling(errorHandler, delegate.clone()); + } + } + + public static abstract class Base extends Call { + volatile boolean canceled; + boolean executed; + + protected Base() { + } + + @Override public final V execute() throws IOException { + synchronized (this) { + if (this.executed) throw new IllegalStateException("Already Executed"); + this.executed = true; + } + + if (isCanceled()) { + throw new IOException("Canceled"); + } else { + return this.doExecute(); + } + } + + protected abstract V doExecute() throws IOException; + + @Override public final void enqueue(Callback callback) { + synchronized (this) { + if (this.executed) throw new IllegalStateException("Already Executed"); + this.executed = true; + } + + if (isCanceled()) { + callback.onError(new IOException("Canceled")); + } else { + this.doEnqueue(callback); + } + } + + protected abstract void doEnqueue(Callback callback); + + @Override public final void cancel() { + this.canceled = true; + doCancel(); + } + + protected void doCancel() { + } + + @Override public final boolean isCanceled() { + return this.canceled || doIsCanceled(); + } + + protected boolean doIsCanceled() { + return false; + } + } +} diff --git a/core/src/main/java/zipkin2/reporter/Callback.java b/core/src/main/java/zipkin2/reporter/Callback.java new file mode 100644 index 00000000..58ed3847 --- /dev/null +++ b/core/src/main/java/zipkin2/reporter/Callback.java @@ -0,0 +1,42 @@ +/* + * Copyright 2016-2024 The OpenZipkin Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ +package zipkin2.reporter; + +import zipkin2.reporter.internal.Nullable; + +/** + * A callback of a single result or error. + * + *

This is a bridge to async libraries such as CompletableFuture complete, completeExceptionally. + * + *

Implementations will call either {@link #onSuccess} or {@link #onError}, but not both. + * + * @since 3.0 + */ +public interface Callback { + + /** + * Invoked when computation produces its potentially null value successfully. + * + *

When this is called, {@link #onError} won't be. + */ + void onSuccess(@Nullable V value); + + /** + * Invoked when computation produces a possibly null value successfully. + * + *

When this is called, {@link #onSuccess} won't be. + */ + void onError(Throwable t); +} diff --git a/core/src/main/java/zipkin2/reporter/CheckResult.java b/core/src/main/java/zipkin2/reporter/CheckResult.java new file mode 100644 index 00000000..7ef4f17e --- /dev/null +++ b/core/src/main/java/zipkin2/reporter/CheckResult.java @@ -0,0 +1,58 @@ +/* + * Copyright 2016-2024 The OpenZipkin Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ +package zipkin2.reporter; + +import zipkin2.reporter.internal.Nullable; + +/** + * Answers the question: Are operations on this component likely to succeed? + * + *

Implementations should initialize the component if necessary. It should test a remote + * connection, or consult a trusted source to derive the result. They should use least resources + * possible to establish a meaningful result, and be safe to call many times, even concurrently. + * + * @see CheckResult#OK + * @since 3.0 + */ +// @Immutable +public final class CheckResult { + public static final CheckResult OK = new CheckResult(true, null); + + public static CheckResult failed(Throwable error) { + return new CheckResult(false, error); + } + + public boolean ok() { + return ok; + } + + /** Present when not ok */ + @Nullable + public Throwable error() { + return error; + } + + final boolean ok; + final Throwable error; + + CheckResult(boolean ok, @Nullable Throwable error) { + this.ok = ok; + this.error = error; + } + + @Override + public String toString() { + return "CheckResult{ok=" + ok + ", " + "error=" + error + "}"; + } +} diff --git a/core/src/main/java/zipkin2/reporter/Component.java b/core/src/main/java/zipkin2/reporter/Component.java new file mode 100644 index 00000000..1eac9481 --- /dev/null +++ b/core/src/main/java/zipkin2/reporter/Component.java @@ -0,0 +1,51 @@ +/* + * Copyright 2016-2024 The OpenZipkin Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ +package zipkin2.reporter; + +import java.io.Closeable; +import java.io.IOException; + +/** + * Components are object graphs used to compose a zipkin service or client. For example, a storage + * component might return a query api. + * + *

Components are lazy in regard to I/O. They can be injected directly to other components, to + * avoid crashing the application graph if a network service is unavailable. + * + * @since 3.0 + */ +public abstract class Component implements Closeable { + + /** + * Answers the question: Are operations on this component likely to succeed? + * + *

Implementations should initialize the component if necessary. It should test a remote + * connection, or consult a trusted source to derive the result. They should use least resources + * possible to establish a meaningful result, and be safe to call many times, even concurrently. + * + * @see CheckResult#OK + */ + public CheckResult check() { + return CheckResult.OK; + } + + /** + * Closes any network resources created implicitly by the component. + * + *

For example, if this created a connection, it would close it. If it was provided one, this + * would close any sessions, but leave the connection open. + */ + @Override public void close() throws IOException { + } +} diff --git a/core/src/main/java/zipkin2/reporter/Encoding.java b/core/src/main/java/zipkin2/reporter/Encoding.java new file mode 100644 index 00000000..eba1fe2a --- /dev/null +++ b/core/src/main/java/zipkin2/reporter/Encoding.java @@ -0,0 +1,91 @@ +/* + * Copyright 2016-2024 The OpenZipkin Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ +package zipkin2.reporter; + +import java.util.List; + +/** + * This includes the formats Zipkin server accepts. + * + * @since 3.0 + */ +public enum Encoding { + JSON { + /** Encoding overhead of a single element is brackets */ + @Override public int listSizeInBytes(int encodedSizeInBytes) { + return 2 + encodedSizeInBytes; + } + + /** Encoding overhead is brackets and a comma for each span over 1 */ + @Override public int listSizeInBytes(List values) { + int sizeInBytes = 2; // brackets + for (int i = 0, length = values.size(); i < length; ) { + sizeInBytes += values.get(i++).length; + if (i < length) sizeInBytes++; + } + return sizeInBytes; + } + }, + /** + * The first format of Zipkin was TBinaryProtocol, big-endian thrift. It is no longer used, but + * defined here to allow collectors to support reading old data. + * + *

The message's binary data includes a list header followed by N spans serialized in + * TBinaryProtocol + * + * @deprecated this format is deprecated in favor of json or proto3 + */ + @Deprecated + THRIFT { + /** Encoding overhead is thrift type plus 32-bit length prefix */ + @Override public int listSizeInBytes(int encodedSizeInBytes) { + return 5 + encodedSizeInBytes; + } + + /** Encoding overhead is thrift type plus 32-bit length prefix */ + @Override public int listSizeInBytes(List values) { + int sizeInBytes = 5; + for (int i = 0, length = values.size(); i < length; i++) { + sizeInBytes += values.get(i).length; + } + return sizeInBytes; + } + }, + /** + * Repeated (type 2) fields are length-prefixed. A list is a concatenation of fields with no + * additional overhead. + * + *

See https://developers.google.com/protocol-buffers/docs/encoding#optional + */ + PROTO3 { + /** Returns the input as it is assumed to be length-prefixed field from a protobuf message */ + @Override public int listSizeInBytes(int encodedSizeInBytes) { + return encodedSizeInBytes; + } + + /** Returns a concatenation of sizes */ + @Override public int listSizeInBytes(List values) { + int sizeInBytes = 0; + for (int i = 0, length = values.size(); i < length; ) { + sizeInBytes += values.get(i++).length; + } + return sizeInBytes; + } + }; + + /** Like {@link #listSizeInBytes(List)}, except for a single element. */ + public abstract int listSizeInBytes(int encodedSizeInBytes); + + public abstract int listSizeInBytes(List values); +} diff --git a/core/src/main/java/zipkin2/reporter/Reporter.java b/core/src/main/java/zipkin2/reporter/Reporter.java index e363ae8d..147510fe 100644 --- a/core/src/main/java/zipkin2/reporter/Reporter.java +++ b/core/src/main/java/zipkin2/reporter/Reporter.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2019 The OpenZipkin Authors + * Copyright 2016-2024 The OpenZipkin Authors * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except * in compliance with the License. You may obtain a copy of the License at @@ -19,7 +19,7 @@ * Spans are created in instrumentation, transported out-of-band, and eventually persisted. * Reporters sends spans (or encoded spans) recorded by instrumentation out of process. * - * Type of span to report, usually {@link zipkin2.Span}, but extracted for reporting other java + * Type of span to report, usually {@link Span}, but extracted for reporting other java * types like HTrace spans to zipkin, and to allow future Zipkin model types to be reported (ex. * zipkin2.Span). */ diff --git a/core/src/main/java/zipkin2/reporter/Sender.java b/core/src/main/java/zipkin2/reporter/Sender.java index ddf615e6..071bc65c 100644 --- a/core/src/main/java/zipkin2/reporter/Sender.java +++ b/core/src/main/java/zipkin2/reporter/Sender.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2020 The OpenZipkin Authors + * Copyright 2016-2024 The OpenZipkin Authors * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except * in compliance with the License. You may obtain a copy of the License at @@ -15,19 +15,14 @@ import java.util.Collections; import java.util.List; -import zipkin2.Call; -import zipkin2.Component; -import zipkin2.codec.BytesEncoder; -import zipkin2.codec.Encoding; -import zipkin2.reporter.internal.InternalReporter; /** * Sends a list of encoded spans to a transport such as http or Kafka. Usually, this involves * encoding them into a message and enqueueing them for transport over http or Kafka. The typical * end recipient is a zipkin collector. * - *

Unless mentioned otherwise, senders are not thread-safe. They were designed to be used by - * {@link AsyncReporter}, which has a single reporting thread. + *

Unless mentioned otherwise, senders are not thread-safe. They were designed to be used by a + * single reporting thread. * *

Those looking to initialize eagerly should call {@link #check()}. This can be used to reduce * latency on the first send operation, or to fail fast. @@ -40,9 +35,11 @@ * scribe will likely write each span as a separate log line. * *

This accepts a list of {@link BytesEncoder#encode(Object) encoded spans}, as opposed a list of - * spans like {@link zipkin2.Span}. This allows senders to be re-usable as model shapes change. This + * spans like {@code zipkin2.Span}. This allows senders to be re-usable as model shapes change. This * also allows them to use their most natural message type. For example, kafka would more naturally * send messages as byte arrays. + * + * @since 3.0 */ public abstract class Sender extends Component { @@ -52,7 +49,7 @@ public abstract class Sender extends Component { /** * Maximum bytes sendable per message including overhead. This can be calculated using {@link * #messageSizeInBytes(List)} - * + *

* Defaults to 500KB as a conservative default. You may get better or reduced performance * by changing this value based on, e.g., machine size or network bandwidth in your * infrastructure. Finding a perfect value will require trying out different values in production, @@ -77,7 +74,6 @@ public abstract class Sender extends Component { *

Always override this, which is only abstract as added after version 2.0 * * @param encodedSizeInBytes the {@link BytesEncoder#sizeInBytes(Object) encoded size} of a span - * @since 2.2 */ public int messageSizeInBytes(int encodedSizeInBytes) { return messageSizeInBytes(Collections.singletonList(new byte[encodedSizeInBytes])); @@ -90,12 +86,4 @@ public int messageSizeInBytes(int encodedSizeInBytes) { * @throws IllegalStateException if {@link #close() close} was called. */ public abstract Call sendSpans(List encodedSpans); - - static { - InternalReporter.instance = new InternalReporter() { - @Override public AsyncReporter.Builder toBuilder(AsyncReporter asyncReporter) { - return ((AsyncReporter.BoundedAsyncReporter) asyncReporter).toBuilder(); - } - }; - } } diff --git a/core/src/main/java/zipkin2/reporter/SpanBytesEncoder.java b/core/src/main/java/zipkin2/reporter/SpanBytesEncoder.java new file mode 100644 index 00000000..060014c3 --- /dev/null +++ b/core/src/main/java/zipkin2/reporter/SpanBytesEncoder.java @@ -0,0 +1,76 @@ +/* + * Copyright 2016-2024 The OpenZipkin Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ +package zipkin2.reporter; + +import zipkin2.Span; + +/** Includes built-in formats used in Zipkin. */ +@SuppressWarnings("ImmutableEnumChecker") // because span is immutable +public enum SpanBytesEncoder implements BytesEncoder { + /** Corresponds to the Zipkin v1 thrift format */ + THRIFT { + @Override public Encoding encoding() { + return Encoding.THRIFT; + } + + @Override public int sizeInBytes(Span input) { + return zipkin2.codec.SpanBytesEncoder.THRIFT.sizeInBytes(input); + } + + @Override public byte[] encode(Span input) { + return zipkin2.codec.SpanBytesEncoder.THRIFT.encode(input); + } + }, + /** Corresponds to the Zipkin v1 json format */ + JSON_V1 { + @Override public Encoding encoding() { + return Encoding.JSON; + } + + @Override public int sizeInBytes(Span input) { + return zipkin2.codec.SpanBytesEncoder.JSON_V1.sizeInBytes(input); + } + + @Override public byte[] encode(Span input) { + return zipkin2.codec.SpanBytesEncoder.JSON_V1.encode(input); + } + }, + /** Corresponds to the Zipkin v2 json format */ + JSON_V2 { + @Override public Encoding encoding() { + return Encoding.JSON; + } + + @Override public int sizeInBytes(Span input) { + return zipkin2.codec.SpanBytesEncoder.JSON_V2.sizeInBytes(input); + } + + @Override public byte[] encode(Span input) { + return zipkin2.codec.SpanBytesEncoder.JSON_V2.encode(input); + } + }, + PROTO3 { + @Override public Encoding encoding() { + return Encoding.PROTO3; + } + + @Override public int sizeInBytes(Span input) { + return zipkin2.codec.SpanBytesEncoder.PROTO3.sizeInBytes(input); + } + + @Override public byte[] encode(Span input) { + return zipkin2.codec.SpanBytesEncoder.PROTO3.encode(input); + } + }; +} diff --git a/core/src/main/java/zipkin2/reporter/internal/AsyncReporter.java b/core/src/main/java/zipkin2/reporter/internal/AsyncReporter.java new file mode 100644 index 00000000..6b8330fb --- /dev/null +++ b/core/src/main/java/zipkin2/reporter/internal/AsyncReporter.java @@ -0,0 +1,378 @@ +/* + * Copyright 2016-2024 The OpenZipkin Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ +package zipkin2.reporter.internal; + +import java.io.Flushable; +import java.util.ArrayList; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.Executors; +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.logging.Level; +import java.util.logging.Logger; +import zipkin2.reporter.BytesEncoder; +import zipkin2.reporter.Call; +import zipkin2.reporter.CheckResult; +import zipkin2.reporter.ClosedSenderException; +import zipkin2.reporter.Component; +import zipkin2.reporter.Reporter; +import zipkin2.reporter.ReporterMetrics; +import zipkin2.reporter.Sender; + +import static java.lang.String.format; +import static java.util.logging.Level.FINE; +import static java.util.logging.Level.WARNING; + +/** + * As spans are reported, they are encoded and added to a pending queue. The task of sending spans + * happens on a separate thread which calls {@link #flush()}. By doing so, callers are protected + * from latency or exceptions possible when exporting spans out of process. + * + *

Spans are bundled into messages based on size in bytes or a timeout, whichever happens first. + * + *

The thread that sends flushes spans to the {@linkplain Sender} does so in a synchronous loop. + * This means that even asynchronous transports will wait for an ack before sending a next message. + * We do this so that a surge of spans doesn't overrun memory or bandwidth via hundreds or + * thousands of in-flight messages. The downside of this is that reporting is limited in speed to + * what a single thread can clear. When a thread cannot clear the backlog, new spans are dropped. + * + * @param type of the span, usually {@code zipkin2.Span} + * @since 3.0 + */ +public abstract class AsyncReporter extends Component implements Reporter, Flushable { + public static Builder newBuilder(Sender sender) { + return new Builder(sender); + } + + /** + * Calling this will flush any pending spans to the transport on the current thread. + * + *

Note: If you set {@link Builder#messageTimeout(long, TimeUnit) message timeout} to zero, you + * must call this externally as otherwise spans will never be sent. + * + * @throws IllegalStateException if closed + */ + @Override public abstract void flush(); + + /** Shuts down the sender thread, and increments drop metrics if there were any unsent spans. */ + @Override public abstract void close(); + + public abstract Builder toBuilder(); + + public static final class Builder { + final Sender sender; + ThreadFactory threadFactory = Executors.defaultThreadFactory(); + ReporterMetrics metrics = ReporterMetrics.NOOP_METRICS; + int messageMaxBytes; + long messageTimeoutNanos = TimeUnit.SECONDS.toNanos(1); + long closeTimeoutNanos = TimeUnit.SECONDS.toNanos(1); + int queuedMaxSpans = 10000; + int queuedMaxBytes = onePercentOfMemory(); + + Builder(BoundedAsyncReporter asyncReporter) { + this.sender = asyncReporter.sender; + this.threadFactory = asyncReporter.threadFactory; + this.metrics = asyncReporter.metrics; + this.messageMaxBytes = asyncReporter.messageMaxBytes; + this.messageTimeoutNanos = asyncReporter.messageTimeoutNanos; + this.closeTimeoutNanos = asyncReporter.closeTimeoutNanos; + this.queuedMaxSpans = asyncReporter.pending.maxSize; + this.queuedMaxBytes = asyncReporter.pending.maxBytes; + } + + static int onePercentOfMemory() { + long result = (long) (Runtime.getRuntime().totalMemory() * 0.01); + // don't overflow in the rare case 1% of memory is larger than 2 GiB! + return (int) Math.max(Math.min(Integer.MAX_VALUE, result), Integer.MIN_VALUE); + } + + Builder(Sender sender) { + if (sender == null) throw new NullPointerException("sender == null"); + this.sender = sender; + this.messageMaxBytes = sender.messageMaxBytes(); + } + + /** + * Launches the flush thread when {@link #messageTimeoutNanos} is greater than zero. + */ + public Builder threadFactory(ThreadFactory threadFactory) { + if (threadFactory == null) throw new NullPointerException("threadFactory == null"); + this.threadFactory = threadFactory; + return this; + } + + /** + * Aggregates and reports reporter metrics to a monitoring system. Defaults to no-op. + */ + public Builder metrics(ReporterMetrics metrics) { + if (metrics == null) throw new NullPointerException("metrics == null"); + this.metrics = metrics; + return this; + } + + /** + * Maximum bytes sendable per message including overhead. Defaults to, and is limited by {@link + * Sender#messageMaxBytes()}. + */ + public Builder messageMaxBytes(int messageMaxBytes) { + if (messageMaxBytes < 0) { + throw new IllegalArgumentException("messageMaxBytes < 0: " + messageMaxBytes); + } + this.messageMaxBytes = Math.min(messageMaxBytes, sender.messageMaxBytes()); + return this; + } + + /** + * Default 1 second. 0 implies spans are {@link #flush() flushed} externally. + * + *

Instead of sending one message at a time, spans are bundled into messages, up to {@link + * Sender#messageMaxBytes()}. This timeout ensures that spans are not stuck in an incomplete + * message. + * + *

Note: this timeout starts when the first unsent span is reported. + */ + public Builder messageTimeout(long timeout, TimeUnit unit) { + if (timeout < 0) throw new IllegalArgumentException("messageTimeout < 0: " + timeout); + if (unit == null) throw new NullPointerException("unit == null"); + this.messageTimeoutNanos = unit.toNanos(timeout); + return this; + } + + /** How long to block for in-flight spans to send out-of-process on close. Default 1 second */ + public Builder closeTimeout(long timeout, TimeUnit unit) { + if (timeout < 0) throw new IllegalArgumentException("closeTimeout < 0: " + timeout); + if (unit == null) throw new NullPointerException("unit == null"); + this.closeTimeoutNanos = unit.toNanos(timeout); + return this; + } + + /** Maximum backlog of spans reported vs sent. Default 10000 */ + public Builder queuedMaxSpans(int queuedMaxSpans) { + this.queuedMaxSpans = queuedMaxSpans; + return this; + } + + /** Maximum backlog of span bytes reported vs sent. Default 1% of heap */ + public Builder queuedMaxBytes(int queuedMaxBytes) { + this.queuedMaxBytes = queuedMaxBytes; + return this; + } + + /** Builds an async reporter that encodes arbitrary spans as they are reported. */ + public AsyncReporter build(BytesEncoder encoder) { + if (encoder == null) throw new NullPointerException("encoder == null"); + + if (encoder.encoding() != sender.encoding()) { + throw new IllegalArgumentException(String.format( + "Encoder doesn't match Sender: %s %s", encoder.encoding(), sender.encoding())); + } + + return new BoundedAsyncReporter(this, encoder); + } + } + + static final class BoundedAsyncReporter extends AsyncReporter { + static final Logger logger = Logger.getLogger(BoundedAsyncReporter.class.getName()); + final AtomicBoolean started, closed; + final BytesEncoder encoder; + final ByteBoundedQueue pending; + final Sender sender; + final int messageMaxBytes; + final long messageTimeoutNanos, closeTimeoutNanos; + final CountDownLatch close; + final ReporterMetrics metrics; + final ThreadFactory threadFactory; + + /** Tracks if we should log the first instance of an exception in flush(). */ + private boolean shouldWarnException = true; + + BoundedAsyncReporter(Builder builder, BytesEncoder encoder) { + this.pending = new ByteBoundedQueue(builder.queuedMaxSpans, builder.queuedMaxBytes); + this.sender = builder.sender; + this.messageMaxBytes = builder.messageMaxBytes; + this.messageTimeoutNanos = builder.messageTimeoutNanos; + this.closeTimeoutNanos = builder.closeTimeoutNanos; + this.closed = new AtomicBoolean(false); + // pretend we already started when config implies no thread that flushes the queue in a loop. + this.started = new AtomicBoolean(builder.messageTimeoutNanos == 0); + this.close = new CountDownLatch(builder.messageTimeoutNanos > 0 ? 1 : 0); + this.metrics = builder.metrics; + this.threadFactory = builder.threadFactory; + this.encoder = encoder; + } + + void startFlusherThread() { + BufferNextMessage consumer = + BufferNextMessage.create(encoder.encoding(), messageMaxBytes, messageTimeoutNanos); + Thread flushThread = threadFactory.newThread(new Flusher(this, consumer)); + flushThread.setName("AsyncReporter{" + sender + "}"); + flushThread.setDaemon(true); + flushThread.start(); + } + + @Override public void report(S next) { + if (next == null) throw new NullPointerException("span == null"); + // Lazy start so that reporters never used don't spawn threads + if (started.compareAndSet(false, true)) startFlusherThread(); + metrics.incrementSpans(1); + int nextSizeInBytes = encoder.sizeInBytes(next); + int messageSizeOfNextSpan = sender.messageSizeInBytes(nextSizeInBytes); + metrics.incrementSpanBytes(nextSizeInBytes); + if (closed.get() || + // don't enqueue something larger than we can drain + messageSizeOfNextSpan > messageMaxBytes || + !pending.offer(next, nextSizeInBytes)) { + metrics.incrementSpansDropped(1); + } + } + + @Override public void flush() { + if (closed.get()) throw new ClosedSenderException(); + flush(BufferNextMessage.create(encoder.encoding(), messageMaxBytes, 0)); + } + + void flush(BufferNextMessage bundler) { + pending.drainTo(bundler, bundler.remainingNanos()); + + // record after flushing reduces the amount of gauge events vs on doing this on report + metrics.updateQueuedSpans(pending.count); + metrics.updateQueuedBytes(pending.sizeInBytes); + + // loop around if we are running, and the bundle isn't full + // if we are closed, try to send what's pending + if (!bundler.isReady() && !closed.get()) return; + + // Signal that we are about to send a message of a known size in bytes + metrics.incrementMessages(); + metrics.incrementMessageBytes(bundler.sizeInBytes()); + + // Create the next message. Since we are outside the lock shared with writers, we can encode + final ArrayList nextMessage = new ArrayList(bundler.count()); + bundler.drain(new SpanWithSizeConsumer() { + @Override public boolean offer(S next, int nextSizeInBytes) { + nextMessage.add(encoder.encode(next)); // speculatively add to the pending message + if (sender.messageSizeInBytes(nextMessage) > messageMaxBytes) { + // if we overran the message size, remove the encoded message. + nextMessage.remove(nextMessage.size() - 1); + return false; + } + return true; + } + }); + + try { + sender.sendSpans(nextMessage).execute(); + } catch (Throwable t) { + // In failure case, we increment messages and spans dropped. + int count = nextMessage.size(); + Call.propagateIfFatal(t); + metrics.incrementMessagesDropped(t); + metrics.incrementSpansDropped(count); + + Level logLevel = FINE; + + if (shouldWarnException) { + logger.log(WARNING, "Spans were dropped due to exceptions. " + + "All subsequent errors will be logged at FINE level."); + logLevel = WARNING; + shouldWarnException = false; + } + + if (logger.isLoggable(logLevel)) { + logger.log(logLevel, + format("Dropped %s spans due to %s(%s)", count, t.getClass().getSimpleName(), + t.getMessage() == null ? "" : t.getMessage()), t); + } + + // Raise in case the sender was closed out-of-band. + if (t instanceof ClosedSenderException) throw (ClosedSenderException) t; + + // Old senders in other artifacts may be using this less precise way of indicating they've been closed + // out-of-band. + if (t instanceof IllegalStateException && t.getMessage().equals("closed")) { + throw (IllegalStateException) t; + } + } + } + + @Override public CheckResult check() { + return sender.check(); + } + + @Override public void close() { + if (!closed.compareAndSet(false, true)) return; // already closed + started.set(true); // prevent anything from starting the thread after close! + try { + // wait for in-flight spans to send + if (!close.await(closeTimeoutNanos, TimeUnit.NANOSECONDS)) { + logger.warning("Timed out waiting for in-flight spans to send"); + } + } catch (InterruptedException e) { + logger.warning("Interrupted waiting for in-flight spans to send"); + Thread.currentThread().interrupt(); + } + int count = pending.clear(); + if (count > 0) { + metrics.incrementSpansDropped(count); + logger.warning("Dropped " + count + " spans due to AsyncReporter.close()"); + } + } + + @Override public Builder toBuilder() { + return new Builder(this); + } + + @Override public String toString() { + return "AsyncReporter{" + sender + "}"; + } + } + + static final class Flusher implements Runnable { + static final Logger logger = Logger.getLogger(Flusher.class.getName()); + + final BoundedAsyncReporter result; + final BufferNextMessage consumer; + + Flusher(BoundedAsyncReporter result, BufferNextMessage consumer) { + this.result = result; + this.consumer = consumer; + } + + @Override public void run() { + try { + while (!result.closed.get()) { + result.flush(consumer); + } + } catch (RuntimeException e) { + logger.log(Level.WARNING, "Unexpected error flushing spans", e); + throw e; + } catch (Error e) { + logger.log(Level.WARNING, "Unexpected error flushing spans", e); + throw e; + } finally { + int count = consumer.count(); + if (count > 0) { + result.metrics.incrementSpansDropped(count); + logger.warning("Dropped " + count + " spans due to AsyncReporter.close()"); + } + result.close.countDown(); + } + } + + @Override public String toString() { + return "AsyncReporter{" + result.sender + "}"; + } + } +} diff --git a/core/src/main/java/zipkin2/reporter/BufferNextMessage.java b/core/src/main/java/zipkin2/reporter/internal/BufferNextMessage.java similarity index 98% rename from core/src/main/java/zipkin2/reporter/BufferNextMessage.java rename to core/src/main/java/zipkin2/reporter/internal/BufferNextMessage.java index 4811f2b6..663b25fe 100644 --- a/core/src/main/java/zipkin2/reporter/BufferNextMessage.java +++ b/core/src/main/java/zipkin2/reporter/internal/BufferNextMessage.java @@ -11,11 +11,11 @@ * or implied. See the License for the specific language governing permissions and limitations under * the License. */ -package zipkin2.reporter; +package zipkin2.reporter.internal; import java.util.ArrayList; import java.util.Iterator; -import zipkin2.codec.Encoding; +import zipkin2.reporter.Encoding; /** Use of this type happens off the application's main thread. This type is not thread-safe */ abstract class BufferNextMessage implements SpanWithSizeConsumer { diff --git a/core/src/main/java/zipkin2/reporter/ByteBoundedQueue.java b/core/src/main/java/zipkin2/reporter/internal/ByteBoundedQueue.java similarity index 97% rename from core/src/main/java/zipkin2/reporter/ByteBoundedQueue.java rename to core/src/main/java/zipkin2/reporter/internal/ByteBoundedQueue.java index d92f69d7..660fc8aa 100644 --- a/core/src/main/java/zipkin2/reporter/ByteBoundedQueue.java +++ b/core/src/main/java/zipkin2/reporter/internal/ByteBoundedQueue.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2019 The OpenZipkin Authors + * Copyright 2016-2024 The OpenZipkin Authors * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except * in compliance with the License. You may obtain a copy of the License at @@ -11,7 +11,7 @@ * or implied. See the License for the specific language governing permissions and limitations under * the License. */ -package zipkin2.reporter; +package zipkin2.reporter.internal; import java.util.Arrays; import java.util.concurrent.locks.Condition; @@ -129,4 +129,4 @@ int doDrain(SpanWithSizeConsumer consumer) { interface SpanWithSizeConsumer { /** Returns true if the element could be added or false if it could not due to its size. */ boolean offer(S next, int nextSizeInBytes); -} \ No newline at end of file +} diff --git a/core/src/main/java/zipkin2/reporter/internal/InternalReporter.java b/core/src/main/java/zipkin2/reporter/internal/InternalReporter.java deleted file mode 100644 index 33bd8f7a..00000000 --- a/core/src/main/java/zipkin2/reporter/internal/InternalReporter.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright 2016-2020 The OpenZipkin Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ -package zipkin2.reporter.internal; - -import zipkin2.reporter.AsyncReporter; -import zipkin2.reporter.Sender; - -/** - * Escalate internal APIs in {@code zipkin2.reporter} so they can be used from outside packages. The - * only implementation is in {@link Sender}. - * - *

Inspired by {@code okhttp3.internal.Internal}. - */ -public abstract class InternalReporter { - public static InternalReporter instance; - - /** - * Internal utility that allows a reporter to be reconfigured. - * - *

Note:Call {@link AsyncReporter#close()} if you no longer need this instance, as - * otherwise it can leak its reporting thread. - * - * @since 2.15 - */ - public abstract AsyncReporter.Builder toBuilder(AsyncReporter asyncReporter); -} diff --git a/core/src/main/java/zipkin2/reporter/internal/Nullable.java b/core/src/main/java/zipkin2/reporter/internal/Nullable.java new file mode 100644 index 00000000..f2d8ca63 --- /dev/null +++ b/core/src/main/java/zipkin2/reporter/internal/Nullable.java @@ -0,0 +1,26 @@ +/* + * Copyright 2016-2024 The OpenZipkin Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ +package zipkin2.reporter.internal; + +/** + * Libraries such as Guice and AutoValue will process any annotation named {@code Nullable}. This + * avoids a dependency on one of the many jsr305 jars, causes problems in OSGi and Java 9 projects + * (where a project is also using jax-ws). + * + * @since 3.0 + */ +@java.lang.annotation.Documented +@java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.RUNTIME) +public @interface Nullable { +} diff --git a/core/src/test/java/zipkin2/reporter/BytesMessageEncoderTest.java b/core/src/test/java/zipkin2/reporter/BytesMessageEncoderTest.java index b9ee3e3a..347ac7b0 100644 --- a/core/src/test/java/zipkin2/reporter/BytesMessageEncoderTest.java +++ b/core/src/test/java/zipkin2/reporter/BytesMessageEncoderTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2023 The OpenZipkin Authors + * Copyright 2016-2024 The OpenZipkin Authors * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except * in compliance with the License. You may obtain a copy of the License at @@ -23,49 +23,49 @@ class BytesMessageEncoderTest { @Test void emptyList_json() { List encoded = Arrays.asList(); assertThat(BytesMessageEncoder.JSON.encode(encoded)) - .containsExactly('[', ']'); + .containsExactly('[', ']'); } @Test void singletonList_json() { List encoded = Arrays.asList(new byte[] {'{', '}'}); assertThat(BytesMessageEncoder.JSON.encode(encoded)) - .containsExactly('[', '{', '}', ']'); + .containsExactly('[', '{', '}', ']'); } @Test void multiItemList_json() { List encoded = Arrays.asList( - "{\"k\":\"1\"}".getBytes(), - "{\"k\":\"2\"}".getBytes(), - "{\"k\":\"3\"}".getBytes() + "{\"k\":\"1\"}".getBytes(), + "{\"k\":\"2\"}".getBytes(), + "{\"k\":\"3\"}".getBytes() ); assertThat(new String(BytesMessageEncoder.JSON.encode(encoded))) - .isEqualTo("[{\"k\":\"1\"},{\"k\":\"2\"},{\"k\":\"3\"}]"); + .isEqualTo("[{\"k\":\"1\"},{\"k\":\"2\"},{\"k\":\"3\"}]"); } @Test void emptyList_proto3() { List encoded = Arrays.asList(); assertThat(BytesMessageEncoder.PROTO3.encode(encoded)) - .isEmpty(); + .isEmpty(); } @Test void singletonList_proto3() { List encoded = Arrays.asList(new byte[] {1, 1, 'a'}); assertThat(BytesMessageEncoder.PROTO3.encode(encoded)) - .containsExactly(1, 1, 'a'); + .containsExactly(1, 1, 'a'); } @Test void multiItemList_proto3() { List encoded = Arrays.asList( - new byte[] {1, 1, 'a'}, - new byte[] {1, 1, 'b'}, - new byte[] {1, 1, 'c'} + new byte[] {1, 1, 'a'}, + new byte[] {1, 1, 'b'}, + new byte[] {1, 1, 'c'} ); assertThat(BytesMessageEncoder.PROTO3.encode(encoded)).containsExactly( - 1, 1, 'a', - 1, 1, 'b', - 1, 1, 'c' + 1, 1, 'a', + 1, 1, 'b', + 1, 1, 'c' ); } } diff --git a/core/src/test/java/zipkin2/reporter/InMemoryReporterMetricsTest.java b/core/src/test/java/zipkin2/reporter/InMemoryReporterMetricsTest.java index afa78d52..ca3d392e 100644 --- a/core/src/test/java/zipkin2/reporter/InMemoryReporterMetricsTest.java +++ b/core/src/test/java/zipkin2/reporter/InMemoryReporterMetricsTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2023 The OpenZipkin Authors + * Copyright 2016-2024 The OpenZipkin Authors * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except * in compliance with the License. You may obtain a copy of the License at @@ -25,8 +25,11 @@ class InMemoryReporterMetricsTest { inMemoryReporterMetrics.incrementMessagesDropped(new RuntimeException()); inMemoryReporterMetrics.incrementMessagesDropped(new IllegalStateException()); - assertThat(inMemoryReporterMetrics.messagesDroppedByCause().get(RuntimeException.class)).isEqualTo(2); - assertThat(inMemoryReporterMetrics.messagesDroppedByCause().get(IllegalStateException.class)).isEqualTo(1); + assertThat( + inMemoryReporterMetrics.messagesDroppedByCause().get(RuntimeException.class)).isEqualTo(2); + assertThat( + inMemoryReporterMetrics.messagesDroppedByCause().get(IllegalStateException.class)).isEqualTo( + 1); assertThat(inMemoryReporterMetrics.messagesDroppedByCause().size()).isEqualTo(2); } diff --git a/core/src/test/java/zipkin2/reporter/AsyncReporterTest.java b/core/src/test/java/zipkin2/reporter/internal/AsyncReporterTest.java similarity index 66% rename from core/src/test/java/zipkin2/reporter/AsyncReporterTest.java rename to core/src/test/java/zipkin2/reporter/internal/AsyncReporterTest.java index 59647b5c..81c96857 100644 --- a/core/src/test/java/zipkin2/reporter/AsyncReporterTest.java +++ b/core/src/test/java/zipkin2/reporter/internal/AsyncReporterTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2023 The OpenZipkin Authors + * Copyright 2016-2024 The OpenZipkin Authors * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except * in compliance with the License. You may obtain a copy of the License at @@ -11,7 +11,7 @@ * or implied. See the License for the specific language governing permissions and limitations under * the License. */ -package zipkin2.reporter; +package zipkin2.reporter.internal; import java.util.ArrayList; import java.util.Collections; @@ -29,10 +29,12 @@ import org.junit.jupiter.api.Test; import zipkin2.Span; import zipkin2.TestObjects; -import zipkin2.codec.BytesEncoder; -import zipkin2.codec.Encoding; -import zipkin2.codec.SpanBytesEncoder; -import zipkin2.reporter.AsyncReporter.BoundedAsyncReporter; +import zipkin2.reporter.BytesEncoder; +import zipkin2.reporter.ClosedSenderException; +import zipkin2.reporter.Encoding; +import zipkin2.reporter.InMemoryReporterMetrics; +import zipkin2.reporter.SpanBytesEncoder; +import zipkin2.reporter.internal.AsyncReporter.BoundedAsyncReporter; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.failBecauseExceptionWasNotThrown; @@ -42,8 +44,8 @@ class AsyncReporterTest { Span span = TestObjects.CLIENT_SPAN; int sizeInBytesOfSingleSpanMessage = - Encoding.JSON.listSizeInBytes( - Collections.singletonList(SpanBytesEncoder.JSON_V2.encode(span))); + Encoding.JSON.listSizeInBytes( + Collections.singletonList(SpanBytesEncoder.JSON_V2.encode(span))); AsyncReporter reporter; InMemoryReporterMetrics metrics = new InMemoryReporterMetrics(); @@ -54,11 +56,11 @@ class AsyncReporterTest { @Test void messageMaxBytes_defaultsToSender() { AtomicInteger sentSpans = new AtomicInteger(); - reporter = AsyncReporter.builder(FakeSender.create() + reporter = AsyncReporter.newBuilder(FakeSender.create() .onSpans(spans -> sentSpans.addAndGet(spans.size())) .messageMaxBytes(sizeInBytesOfSingleSpanMessage)) - .messageTimeout(0, TimeUnit.MILLISECONDS) - .build(); + .messageTimeout(0, TimeUnit.MILLISECONDS) + .build(SpanBytesEncoder.JSON_V2); reporter.report(span); reporter.report(span); // drops @@ -69,11 +71,11 @@ class AsyncReporterTest { @Test void messageMaxBytes_dropsWhenOverqueuing() { AtomicInteger sentSpans = new AtomicInteger(); - reporter = AsyncReporter.builder(FakeSender.create() + reporter = AsyncReporter.newBuilder(FakeSender.create() .onSpans(spans -> sentSpans.addAndGet(spans.size()))) - .messageMaxBytes(sizeInBytesOfSingleSpanMessage) - .messageTimeout(0, TimeUnit.MILLISECONDS) - .build(); + .messageMaxBytes(sizeInBytesOfSingleSpanMessage) + .messageTimeout(0, TimeUnit.MILLISECONDS) + .build(SpanBytesEncoder.JSON_V2); reporter.report(span); reporter.report(span); // dropped the one that queued more than allowed bytes @@ -84,11 +86,11 @@ class AsyncReporterTest { @Test void messageMaxBytes_dropsWhenTooLarge() { AtomicInteger sentSpans = new AtomicInteger(); - reporter = AsyncReporter.builder(FakeSender.create() + reporter = AsyncReporter.newBuilder(FakeSender.create() .onSpans(spans -> sentSpans.addAndGet(spans.size()))) - .messageMaxBytes(sizeInBytesOfSingleSpanMessage) - .messageTimeout(0, TimeUnit.MILLISECONDS) - .build(); + .messageMaxBytes(sizeInBytesOfSingleSpanMessage) + .messageTimeout(0, TimeUnit.MILLISECONDS) + .build(SpanBytesEncoder.JSON_V2); reporter.report(span.toBuilder().addAnnotation(1L, "fooooo").build()); reporter.flush(); @@ -98,11 +100,11 @@ class AsyncReporterTest { @Test void queuedMaxSpans_dropsWhenOverqueuing() { AtomicInteger sentSpans = new AtomicInteger(); - reporter = AsyncReporter.builder(FakeSender.create() + reporter = AsyncReporter.newBuilder(FakeSender.create() .onSpans(spans -> sentSpans.addAndGet(spans.size()))) - .queuedMaxSpans(1) - .messageTimeout(0, TimeUnit.MILLISECONDS) - .build(); + .queuedMaxSpans(1) + .messageTimeout(0, TimeUnit.MILLISECONDS) + .build(SpanBytesEncoder.JSON_V2); reporter.report(span); reporter.report(span); // dropped the one that queued more than allowed count @@ -112,10 +114,10 @@ class AsyncReporterTest { } @Test void report_incrementsMetrics() { - reporter = AsyncReporter.builder(FakeSender.create()) - .metrics(metrics) - .messageTimeout(0, TimeUnit.MILLISECONDS) - .build(); + reporter = AsyncReporter.newBuilder(FakeSender.create()) + .metrics(metrics) + .messageTimeout(0, TimeUnit.MILLISECONDS) + .build(SpanBytesEncoder.JSON_V2); reporter.report(span); reporter.report(span); @@ -124,11 +126,11 @@ class AsyncReporterTest { } @Test void report_incrementsSpansDropped() { - reporter = AsyncReporter.builder(FakeSender.create()) - .queuedMaxSpans(1) - .metrics(metrics) - .messageTimeout(0, TimeUnit.MILLISECONDS) - .build(); + reporter = AsyncReporter.newBuilder(FakeSender.create()) + .queuedMaxSpans(1) + .metrics(metrics) + .messageTimeout(0, TimeUnit.MILLISECONDS) + .build(SpanBytesEncoder.JSON_V2); reporter.report(span); reporter.report(span); @@ -138,11 +140,11 @@ class AsyncReporterTest { } @Test void flush_incrementsMetrics() { - reporter = AsyncReporter.builder(FakeSender.create()) - .metrics(metrics) - .messageMaxBytes(sizeInBytesOfSingleSpanMessage) - .messageTimeout(0, TimeUnit.MILLISECONDS) - .build(); + reporter = AsyncReporter.newBuilder(FakeSender.create()) + .metrics(metrics) + .messageMaxBytes(sizeInBytesOfSingleSpanMessage) + .messageTimeout(0, TimeUnit.MILLISECONDS) + .build(SpanBytesEncoder.JSON_V2); // Queue up 2 spans reporter.report(span); @@ -162,13 +164,13 @@ class AsyncReporterTest { } @Test void flush_incrementsMessagesDropped() { - reporter = AsyncReporter.builder(FakeSender.create() + reporter = AsyncReporter.newBuilder(FakeSender.create() .onSpans(spans -> { throw new RuntimeException(); })) - .metrics(metrics) - .messageTimeout(0, TimeUnit.MILLISECONDS) - .build(); + .metrics(metrics) + .messageTimeout(0, TimeUnit.MILLISECONDS) + .build(SpanBytesEncoder.JSON_V2); reporter.report(span); @@ -185,21 +187,23 @@ public void publish(LogRecord record) { } @Override - public void flush() {} + public void flush() { + } @Override - public void close() throws SecurityException {} + public void close() throws SecurityException { + } }; Logger logger = Logger.getLogger(BoundedAsyncReporter.class.getName()); logger.addHandler(testHandler); logger.setLevel(Level.FINE); - reporter = AsyncReporter.builder(FakeSender.create() - .onSpans(spans -> { - throw new RuntimeException(); - })) - .build(); + reporter = AsyncReporter.newBuilder(FakeSender.create() + .onSpans(spans -> { + throw new RuntimeException(); + })) + .build(SpanBytesEncoder.JSON_V2); reporter.report(span); reporter.flush(); @@ -220,51 +224,51 @@ public void close() throws SecurityException {} /** It can take up to the messageTimeout past the first span to send */ @Test void messageTimeout_flushesWhenTimeoutExceeded() throws InterruptedException { CountDownLatch sentSpans = new CountDownLatch(1); - reporter = AsyncReporter.builder(FakeSender.create() + reporter = AsyncReporter.newBuilder(FakeSender.create() .onSpans(spans -> sentSpans.countDown())) - .messageTimeout(10, TimeUnit.MILLISECONDS) - .build(); + .messageTimeout(10, TimeUnit.MILLISECONDS) + .build(SpanBytesEncoder.JSON_V2); reporter.report(span); assertThat(sentSpans.await(5, TimeUnit.MILLISECONDS)) - .isFalse(); + .isFalse(); assertThat(sentSpans.await(10, TimeUnit.MILLISECONDS)) - .isTrue(); + .isTrue(); } @Test void messageTimeout_disabled() throws InterruptedException { CountDownLatch sentSpans = new CountDownLatch(1); - reporter = AsyncReporter.builder(FakeSender.create() + reporter = AsyncReporter.newBuilder(FakeSender.create() .onSpans(spans -> sentSpans.countDown())) - .messageTimeout(0, TimeUnit.NANOSECONDS) - .build(); + .messageTimeout(0, TimeUnit.NANOSECONDS) + .build(SpanBytesEncoder.JSON_V2); reporter.report(span); assertThat(sentSpans.getCount()).isEqualTo(1); // Since no threads started, the above lingers assertThat(sentSpans.await(10, TimeUnit.MILLISECONDS)) - .isFalse(); + .isFalse(); } @Test void senderThread_threadHasAPrettyName() throws InterruptedException { BlockingQueue threadName = new LinkedBlockingQueue<>(); - reporter = AsyncReporter.builder(FakeSender.create() + reporter = AsyncReporter.newBuilder(FakeSender.create() .onSpans(spans -> threadName.offer(Thread.currentThread().getName()))) - .build(); + .build(SpanBytesEncoder.JSON_V2); reporter.report(span); // check name is pretty assertThat(threadName.take()) - .isEqualTo("AsyncReporter{FakeSender}"); + .isEqualTo("AsyncReporter{FakeSender}"); } @Test void close_close_stopsFlushThread() throws InterruptedException { - reporter = AsyncReporter.builder(FakeSender.create()) - .metrics(metrics) - .messageTimeout(2, TimeUnit.MILLISECONDS) - .build(); + reporter = AsyncReporter.newBuilder(FakeSender.create()) + .metrics(metrics) + .messageTimeout(2, TimeUnit.MILLISECONDS) + .build(SpanBytesEncoder.JSON_V2); // Reporter thread is lazy assertThat(((BoundedAsyncReporter) reporter).started).isFalse(); @@ -276,15 +280,15 @@ public void close() throws SecurityException {} // the close latch counts down when the thread is stopped BoundedAsyncReporter impl = (BoundedAsyncReporter) reporter; assertThat(impl.close.await(3, TimeUnit.MILLISECONDS)) - .isTrue(); + .isTrue(); } @Test void flush_throwsOnClose() { assertThrows(IllegalStateException.class, () -> { - reporter = AsyncReporter.builder(FakeSender.create()) - .metrics(metrics) - .messageTimeout(0, TimeUnit.MILLISECONDS) - .build(); + reporter = AsyncReporter.newBuilder(FakeSender.create()) + .metrics(metrics) + .messageTimeout(0, TimeUnit.MILLISECONDS) + .build(SpanBytesEncoder.JSON_V2); reporter.report(span); reporter.close(); // close while there's a pending span @@ -293,10 +297,10 @@ public void close() throws SecurityException {} } @Test void report_doesntThrowWhenClosed() { - reporter = AsyncReporter.builder(FakeSender.create()) - .metrics(metrics) - .messageTimeout(0, TimeUnit.MILLISECONDS) - .build(); + reporter = AsyncReporter.newBuilder(FakeSender.create()) + .metrics(metrics) + .messageTimeout(0, TimeUnit.MILLISECONDS) + .build(SpanBytesEncoder.JSON_V2); reporter.close(); @@ -314,10 +318,10 @@ public void close() throws SecurityException {} }); @Test void senderThread_dropsOnSenderClose_flushThread() throws InterruptedException { - reporter = AsyncReporter.builder(sleepingSender) - .metrics(metrics) - .messageMaxBytes(sizeInBytesOfSingleSpanMessage) - .build(); + reporter = AsyncReporter.newBuilder(sleepingSender) + .metrics(metrics) + .messageMaxBytes(sizeInBytesOfSingleSpanMessage) + .build(SpanBytesEncoder.JSON_V2); reporter.report(span); Thread.sleep(1); // flush thread got the first span, but still waiting for more @@ -328,13 +332,13 @@ public void close() throws SecurityException {} assertThat(metrics.spansDropped()).isEqualTo(1); assertThat(metrics.messagesDropped()).isEqualTo(1); assertThat(metrics.messagesDroppedByCause().keySet().iterator().next()) - .isEqualTo(ClosedSenderException.class); + .isEqualTo(ClosedSenderException.class); } @Test void senderThread_dropsOnReporterClose_flushThread() throws InterruptedException { CountDownLatch received = new CountDownLatch(1); CountDownLatch sent = new CountDownLatch(1); - reporter = AsyncReporter.builder(FakeSender.create() + reporter = AsyncReporter.newBuilder(FakeSender.create() .onSpans(spans -> { received.countDown(); try { @@ -343,9 +347,9 @@ public void close() throws SecurityException {} e.printStackTrace(); } })) - .metrics(metrics) - .messageMaxBytes(sizeInBytesOfSingleSpanMessage) - .build(); + .metrics(metrics) + .messageMaxBytes(sizeInBytesOfSingleSpanMessage) + .build(SpanBytesEncoder.JSON_V2); reporter.report(span); reporter.report(span); // pending as the flush thread is blocked @@ -357,10 +361,10 @@ public void close() throws SecurityException {} } @Test void blocksToClearPendingSpans() throws InterruptedException { - reporter = AsyncReporter.builder(FakeSender.create()) - .metrics(metrics) - .messageTimeout(30, TimeUnit.SECONDS) - .build(); + reporter = AsyncReporter.newBuilder(FakeSender.create()) + .metrics(metrics) + .messageTimeout(30, TimeUnit.SECONDS) + .build(SpanBytesEncoder.JSON_V2); reporter.report(span); Thread.sleep(500); // wait for the thread to start @@ -372,7 +376,7 @@ public void close() throws SecurityException {} } @Test void quitsBlockingWhenOverTimeout() throws InterruptedException { - reporter = AsyncReporter.builder(FakeSender.create() + reporter = AsyncReporter.newBuilder(FakeSender.create() .onSpans(spans -> { // note: we don't yet have a hook to cancel a sender, so this will remain in-flight // eventhough we are unblocking close. A later close on sender usually will kill in-flight @@ -382,10 +386,10 @@ public void close() throws SecurityException {} e.printStackTrace(); } })) - .metrics(metrics) - .closeTimeout(1, TimeUnit.NANOSECONDS) - .messageTimeout(30, TimeUnit.SECONDS) - .build(); + .metrics(metrics) + .closeTimeout(1, TimeUnit.NANOSECONDS) + .messageTimeout(30, TimeUnit.SECONDS) + .build(SpanBytesEncoder.JSON_V2); Thread.sleep(500); // wait for the thread to start @@ -394,14 +398,14 @@ public void close() throws SecurityException {} long start = System.nanoTime(); reporter.close(); // close while there's a pending span assertThat(System.nanoTime() - start) - .isLessThan(TimeUnit.MILLISECONDS.toNanos(10)); // give wiggle room + .isLessThan(TimeUnit.MILLISECONDS.toNanos(10)); // give wiggle room } @Test void flush_incrementsMetricsAndThrowsWhenClosed() { - reporter = AsyncReporter.builder(sleepingSender) - .metrics(metrics) - .messageTimeout(0, TimeUnit.MILLISECONDS) - .build(); + reporter = AsyncReporter.newBuilder(sleepingSender) + .metrics(metrics) + .messageTimeout(0, TimeUnit.MILLISECONDS) + .build(SpanBytesEncoder.JSON_V2); reporter.report(span); @@ -415,10 +419,10 @@ public void close() throws SecurityException {} } @Test void flush_incrementsMetricsAndThrowsWhenSenderClosed() { - reporter = AsyncReporter.builder(sleepingSender) - .metrics(metrics) - .messageTimeout(0, TimeUnit.MILLISECONDS) - .build(); + reporter = AsyncReporter.newBuilder(sleepingSender) + .metrics(metrics) + .messageTimeout(0, TimeUnit.MILLISECONDS) + .build(SpanBytesEncoder.JSON_V2); reporter.report(span); @@ -434,9 +438,9 @@ public void close() throws SecurityException {} @Test void build_threadFactory() { Thread thread = new Thread(); - reporter = AsyncReporter.builder(FakeSender.create()) + reporter = AsyncReporter.newBuilder(FakeSender.create()) .threadFactory(r -> thread) - .build(); + .build(SpanBytesEncoder.JSON_V2); // Reporter thread is lazy assertThat(thread.isAlive()).isFalse(); @@ -450,60 +454,50 @@ public void close() throws SecurityException {} } @Test void build_proto3() { - AsyncReporter.builder(FakeSender.create().encoding(Encoding.PROTO3)) - .messageTimeout(0, TimeUnit.MILLISECONDS) - .build(); + AsyncReporter.newBuilder(FakeSender.create().encoding(Encoding.PROTO3)) + .messageTimeout(0, TimeUnit.MILLISECONDS) + .build(SpanBytesEncoder.PROTO3); } @Test void build_proto3_withCustomBytesEncoder() { - AsyncReporter.builder(FakeSender.create().encoding(Encoding.PROTO3)) - .messageTimeout(0, TimeUnit.MILLISECONDS) - // there's no builtin protobuf format of zipkin spans, yet, so there's no encoder - .build(new BytesEncoder() { - @Override public Encoding encoding() { - return Encoding.PROTO3; - } - - @Override public int sizeInBytes(Span input) { - return 0; - } - - @Override public byte[] encode(Span input) { - return new byte[0]; - } - - @Override public byte[] encodeList(List input) { - return new byte[0]; - } - }); + AsyncReporter.newBuilder(FakeSender.create().encoding(Encoding.PROTO3)) + .messageTimeout(0, TimeUnit.MILLISECONDS) + .build(new BytesEncoder() { + @Override public Encoding encoding() { + return Encoding.PROTO3; + } + + @Override public int sizeInBytes(Span input) { + return 0; + } + + @Override public byte[] encode(Span input) { + return new byte[0]; + } + }); } @Test void build_thrift() { - AsyncReporter.builder(FakeSender.create().encoding(Encoding.THRIFT)) - .messageTimeout(0, TimeUnit.MILLISECONDS) - .build(); + AsyncReporter.newBuilder(FakeSender.create().encoding(Encoding.THRIFT)) + .messageTimeout(0, TimeUnit.MILLISECONDS) + .build(SpanBytesEncoder.THRIFT); } @Test void build_thrift_withCustomBytesEncoder() { - AsyncReporter.builder(FakeSender.create().encoding(Encoding.THRIFT)) - .messageTimeout(0, TimeUnit.MILLISECONDS) - // there's no builtin protobuf format of zipkin spans, yet, so there's no encoder - .build(new BytesEncoder() { - @Override public Encoding encoding() { - return Encoding.THRIFT; - } - - @Override public int sizeInBytes(Span input) { - return 0; - } - - @Override public byte[] encode(Span input) { - return new byte[0]; - } - - @Override public byte[] encodeList(List input) { - return new byte[0]; - } - }); + AsyncReporter.newBuilder(FakeSender.create().encoding(Encoding.THRIFT)) + .messageTimeout(0, TimeUnit.MILLISECONDS) + .build(new BytesEncoder() { + @Override public Encoding encoding() { + return Encoding.THRIFT; + } + + @Override public int sizeInBytes(Span input) { + return 0; + } + + @Override public byte[] encode(Span input) { + return new byte[0]; + } + }); } } diff --git a/core/src/test/java/zipkin2/reporter/BufferNextMessageTest.java b/core/src/test/java/zipkin2/reporter/internal/BufferNextMessageTest.java similarity index 79% rename from core/src/test/java/zipkin2/reporter/BufferNextMessageTest.java rename to core/src/test/java/zipkin2/reporter/internal/BufferNextMessageTest.java index 4ff65e6d..bb748909 100644 --- a/core/src/test/java/zipkin2/reporter/BufferNextMessageTest.java +++ b/core/src/test/java/zipkin2/reporter/internal/BufferNextMessageTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2023 The OpenZipkin Authors + * Copyright 2016-2024 The OpenZipkin Authors * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except * in compliance with the License. You may obtain a copy of the License at @@ -11,10 +11,10 @@ * or implied. See the License for the specific language governing permissions and limitations under * the License. */ -package zipkin2.reporter; +package zipkin2.reporter.internal; import org.junit.jupiter.api.Test; -import zipkin2.codec.Encoding; +import zipkin2.reporter.Encoding; import static org.assertj.core.api.Assertions.assertThat; @@ -23,9 +23,9 @@ class BufferNextMessageTest { BufferNextMessage pending = BufferNextMessage.create(Encoding.JSON, 10, 0L); assertThat(pending.bufferFull) - .isFalse(); + .isFalse(); assertThat(pending.messageSizeInBytes) - .isEqualTo(2 /* [] */); + .isEqualTo(2 /* [] */); } @Test void offer_json() { @@ -33,34 +33,34 @@ class BufferNextMessageTest { pending.offer(1, 1); assertThat(pending.bufferFull) - .isFalse(); + .isFalse(); assertThat(pending.messageSizeInBytes) - .isEqualTo(3 /* [1] */); + .isEqualTo(3 /* [1] */); pending.offer(2, 1); assertThat(pending.bufferFull) - .isFalse(); + .isFalse(); assertThat(pending.messageSizeInBytes) - .isEqualTo(5 /* [1,2] */); + .isEqualTo(5 /* [1,2] */); } @Test void offerWhenFull_json() { BufferNextMessage pending = BufferNextMessage.create(Encoding.JSON, 10, 0L); for (int i = 0; i < 4; i++) { assertThat(pending.offer(i, 1)) - .isTrue(); + .isTrue(); } // buffer is not quite full assertThat(pending.bufferFull) - .isFalse(); + .isFalse(); assertThat(pending.messageSizeInBytes) - .isEqualTo(9 /* [0,1,2,3] */); + .isEqualTo(9 /* [0,1,2,3] */); // but another element will put it over the edge, so drops assertThat(pending.offer(4, 1)) - // should drop because 4 implies ",4" which makes the total length 11 - .isFalse(); + // should drop because 4 implies ",4" which makes the total length 11 + .isFalse(); // then we should consider buffer is full and drain all assertThat(pending.bufferFull).isTrue(); } @@ -76,7 +76,7 @@ class BufferNextMessageTest { // back to initial state assertThat(pending).usingRecursiveComparison().isEqualTo( - BufferNextMessage.create(Encoding.JSON, 10, 0L) + BufferNextMessage.create(Encoding.JSON, 10, 0L) ); } @@ -90,26 +90,26 @@ class BufferNextMessageTest { pending.drain((s, n) -> s < 2); assertThat(pending.spans) - .containsExactly(2, 3); + .containsExactly(2, 3); assertThat(pending.messageSizeInBytes) - .isEqualTo(5 /* [2,3] */); + .isEqualTo(5 /* [2,3] */); // partial drain again pending.drain((s, n) -> s < 3); assertThat(pending.spans) - .containsExactly(3); + .containsExactly(3); assertThat(pending.messageSizeInBytes) - .isEqualTo(3 /* [3] */); + .isEqualTo(3 /* [3] */); } @Test void empty_proto3() { BufferNextMessage pending = BufferNextMessage.create(Encoding.PROTO3, 10, 0L); assertThat(pending.bufferFull) - .isFalse(); + .isFalse(); assertThat(pending.messageSizeInBytes) - .isZero(); + .isZero(); } @Test void offer_proto3() { @@ -117,34 +117,34 @@ class BufferNextMessageTest { pending.offer('a', 1); assertThat(pending.bufferFull) - .isFalse(); + .isFalse(); assertThat(pending.messageSizeInBytes) - .isEqualTo(1 /* a */); + .isEqualTo(1 /* a */); pending.offer('b', 1); assertThat(pending.bufferFull) - .isFalse(); + .isFalse(); assertThat(pending.messageSizeInBytes) - .isEqualTo(2 /* ab */); + .isEqualTo(2 /* ab */); } @Test void offerWhenFull_proto3() { BufferNextMessage pending = BufferNextMessage.create(Encoding.PROTO3, 10, 0L); for (int i = 0; i < 3; i++) { assertThat(pending.offer(i, 3)) - .isTrue(); + .isTrue(); } // buffer is not quite full assertThat(pending.bufferFull) - .isFalse(); + .isFalse(); assertThat(pending.messageSizeInBytes) - .isEqualTo(9 /* 012 */); + .isEqualTo(9 /* 012 */); // but another element will put it over the edge, so drops assertThat(pending.offer(3, 3)) - // should drop because this implies adding 3 bytes which makes the total length 12 - .isFalse(); + // should drop because this implies adding 3 bytes which makes the total length 12 + .isFalse(); // then we should consider buffer is full and drain all assertThat(pending.bufferFull).isTrue(); } @@ -160,7 +160,7 @@ class BufferNextMessageTest { // back to initial state assertThat(pending).usingRecursiveComparison().isEqualTo( - BufferNextMessage.create(Encoding.PROTO3, 10, 0L) + BufferNextMessage.create(Encoding.PROTO3, 10, 0L) ); } @@ -174,16 +174,16 @@ class BufferNextMessageTest { pending.drain((s, n) -> s < 2); assertThat(pending.spans) - .containsExactly(2, 3); + .containsExactly(2, 3); assertThat(pending.messageSizeInBytes) - .isEqualTo(2 /* 23 */); + .isEqualTo(2 /* 23 */); // partial drain again pending.drain((s, n) -> s < 3); assertThat(pending.spans) - .containsExactly(3); + .containsExactly(3); assertThat(pending.messageSizeInBytes) - .isEqualTo(1 /* 3 */); + .isEqualTo(1 /* 3 */); } } diff --git a/core/src/test/java/zipkin2/reporter/ByteBoundedQueueTest.java b/core/src/test/java/zipkin2/reporter/internal/ByteBoundedQueueTest.java similarity index 92% rename from core/src/test/java/zipkin2/reporter/ByteBoundedQueueTest.java rename to core/src/test/java/zipkin2/reporter/internal/ByteBoundedQueueTest.java index 3124f397..3c5787d9 100644 --- a/core/src/test/java/zipkin2/reporter/ByteBoundedQueueTest.java +++ b/core/src/test/java/zipkin2/reporter/internal/ByteBoundedQueueTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2023 The OpenZipkin Authors + * Copyright 2016-2024 The OpenZipkin Authors * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except * in compliance with the License. You may obtain a copy of the License at @@ -11,7 +11,7 @@ * or implied. See the License for the specific language governing permissions and limitations under * the License. */ -package zipkin2.reporter; +package zipkin2.reporter.internal; import java.util.ArrayList; import java.util.List; @@ -62,6 +62,6 @@ class ByteBoundedQueueTest { // ensure we have all of the spans assertThat(polled) - .containsExactly(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14); + .containsExactly(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14); } } diff --git a/core/src/test/java/zipkin2/reporter/FakeSender.java b/core/src/test/java/zipkin2/reporter/internal/FakeSender.java similarity index 61% rename from core/src/test/java/zipkin2/reporter/FakeSender.java rename to core/src/test/java/zipkin2/reporter/internal/FakeSender.java index f7b07e59..4ca9b8bf 100644 --- a/core/src/test/java/zipkin2/reporter/FakeSender.java +++ b/core/src/test/java/zipkin2/reporter/internal/FakeSender.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2020 The OpenZipkin Authors + * Copyright 2016-2024 The OpenZipkin Authors * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except * in compliance with the License. You may obtain a copy of the License at @@ -11,31 +11,29 @@ * or implied. See the License for the specific language governing permissions and limitations under * the License. */ -package zipkin2.reporter; +package zipkin2.reporter.internal; import java.util.List; import java.util.function.Consumer; import java.util.stream.Collectors; -import zipkin2.Call; -import zipkin2.CheckResult; import zipkin2.Span; import zipkin2.codec.BytesDecoder; -import zipkin2.codec.BytesEncoder; -import zipkin2.codec.Encoding; import zipkin2.codec.SpanBytesDecoder; -import zipkin2.codec.SpanBytesEncoder; +import zipkin2.reporter.BytesEncoder; +import zipkin2.reporter.BytesMessageEncoder; +import zipkin2.reporter.Call; +import zipkin2.reporter.ClosedSenderException; +import zipkin2.reporter.Encoding; +import zipkin2.reporter.Sender; +import zipkin2.reporter.SpanBytesEncoder; public final class FakeSender extends Sender { + public static FakeSender create() { - return new FakeSender( - Encoding.JSON, - Integer.MAX_VALUE, - BytesMessageEncoder.forEncoding(Encoding.JSON), - SpanBytesEncoder.JSON_V2, - SpanBytesDecoder.JSON_V2, - spans -> { - } - ); + return new FakeSender(Encoding.JSON, Integer.MAX_VALUE, + BytesMessageEncoder.forEncoding(Encoding.JSON), SpanBytesEncoder.JSON_V2, + SpanBytesDecoder.JSON_V2, spans -> { + }); } final Encoding encoding; @@ -45,14 +43,8 @@ public static FakeSender create() { final BytesDecoder decoder; final Consumer> onSpans; - FakeSender( - Encoding encoding, - int messageMaxBytes, - BytesMessageEncoder messageEncoder, - BytesEncoder encoder, - BytesDecoder decoder, - Consumer> onSpans - ) { + FakeSender(Encoding encoding, int messageMaxBytes, BytesMessageEncoder messageEncoder, + BytesEncoder encoder, BytesDecoder decoder, Consumer> onSpans) { this.encoding = encoding; this.messageMaxBytes = messageMaxBytes; this.messageEncoder = messageEncoder; @@ -62,36 +54,18 @@ public static FakeSender create() { } FakeSender encoding(Encoding encoding) { - return new FakeSender( - encoding, - messageMaxBytes, - messageEncoder, // invalid but not needed, yet - encoder, // invalid but not needed, yet - decoder, // invalid but not needed, yet - onSpans - ); + return new FakeSender(encoding, messageMaxBytes, messageEncoder, // invalid but not needed, yet + encoder, // invalid but not needed, yet + decoder, // invalid but not needed, yet + onSpans); } FakeSender onSpans(Consumer> onSpans) { - return new FakeSender( - encoding, - messageMaxBytes, - messageEncoder, - encoder, - decoder, - onSpans - ); + return new FakeSender(encoding, messageMaxBytes, messageEncoder, encoder, decoder, onSpans); } FakeSender messageMaxBytes(int messageMaxBytes) { - return new FakeSender( - encoding, - messageMaxBytes, - messageEncoder, - encoder, - decoder, - onSpans - ); + return new FakeSender(encoding, messageMaxBytes, messageEncoder, encoder, decoder, onSpans); } @Override public Encoding encoding() { @@ -115,17 +89,11 @@ FakeSender messageMaxBytes(int messageMaxBytes) { @Override public Call sendSpans(List encodedSpans) { if (closeCalled) throw new ClosedSenderException(); - List decoded = encodedSpans.stream() - .map(s -> decoder.decodeOne(s)) - .collect(Collectors.toList()); + List decoded = encodedSpans.stream().map(decoder::decodeOne).collect(Collectors.toList()); onSpans.accept(decoded); return Call.create(null); } - @Override public CheckResult check() { - return CheckResult.OK; - } - @Override public void close() { closeCalled = true; } diff --git a/core/src/test/java/zipkin2/reporter/internal/InternalReporterTest.java b/core/src/test/java/zipkin2/reporter/internal/InternalReporterTest.java deleted file mode 100644 index 8ddb4a56..00000000 --- a/core/src/test/java/zipkin2/reporter/internal/InternalReporterTest.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright 2016-2023 The OpenZipkin Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ -package zipkin2.reporter.internal; - -import java.util.List; -import org.junit.jupiter.api.Test; -import zipkin2.codec.BytesEncoder; -import zipkin2.codec.Encoding; -import zipkin2.reporter.AsyncReporter; -import zipkin2.reporter.FakeSender; -import zipkin2.reporter.Sender; - -import static org.assertj.core.api.Assertions.assertThat; - -class InternalReporterTest { - Sender sender = FakeSender.create(); - BytesEncoder bytesEncoder = new BytesEncoder() { - @Override public Encoding encoding() { - return Encoding.JSON; - } - - @Override public int sizeInBytes(String s) { - return s.length(); - } - - @Override public byte[] encode(String s) { - throw new UnsupportedOperationException(); - } - - @Override public byte[] encodeList(List list) { - throw new UnsupportedOperationException(); - } - }; - - /** - * Shows usage for {@code AsyncZipkinSpanHandler} which internally builds an async reporter with a - * custom bytes encoder. This allows {@code AsyncZipkinSpanHandler.toBuilder()} to be safely - * created as the there is no ambiguity on whether {@link AsyncReporter.Builder#build()} or {@link - * AsyncReporter.Builder#build(BytesEncoder)} was called. - */ - @Test void toBuilder() { - AsyncReporter input = AsyncReporter.builder(sender).build(bytesEncoder); - assertThat(InternalReporter.instance.toBuilder(input).build(bytesEncoder)) - .usingRecursiveComparison() - .ignoringFields("close", "pending") // for JRE 21 - .isEqualTo(input); - } -} diff --git a/kafka/pom.xml b/kafka/pom.xml index 58692c41..74ccc9ba 100644 --- a/kafka/pom.xml +++ b/kafka/pom.xml @@ -20,7 +20,7 @@ io.zipkin.reporter2 zipkin-reporter-parent - 2.17.3-SNAPSHOT + 3.0.0-SNAPSHOT zipkin-sender-kafka @@ -38,6 +38,14 @@ ${project.groupId} zipkin-reporter ${project.version} + + + + io.zipkin.zipkin2 + zipkin + + diff --git a/kafka/src/main/java/zipkin2/reporter/kafka/KafkaSender.java b/kafka/src/main/java/zipkin2/reporter/kafka/KafkaSender.java index a59d4e99..df88069f 100644 --- a/kafka/src/main/java/zipkin2/reporter/kafka/KafkaSender.java +++ b/kafka/src/main/java/zipkin2/reporter/kafka/KafkaSender.java @@ -26,22 +26,22 @@ import org.apache.kafka.clients.producer.RecordMetadata; import org.apache.kafka.common.KafkaFuture; import org.apache.kafka.common.serialization.ByteArraySerializer; -import zipkin2.Call; -import zipkin2.Callback; -import zipkin2.CheckResult; -import zipkin2.codec.Encoding; -import zipkin2.reporter.AsyncReporter; import zipkin2.reporter.AwaitableCallback; import zipkin2.reporter.BytesMessageEncoder; +import zipkin2.reporter.Call; +import zipkin2.reporter.Callback; +import zipkin2.reporter.CheckResult; import zipkin2.reporter.ClosedSenderException; +import zipkin2.reporter.Encoding; import zipkin2.reporter.Sender; /** * This sends (usually json v2) encoded spans to a Kafka topic. * *

Usage

- * - * This type is designed for {@link AsyncReporter.Builder#builder(Sender) the async reporter}. + *

+ * This type is designed for {@link zipkin2.reporter.AsyncReporter.Builder#builder(Sender) the async + * reporter}. * *

Here's a simple configuration, configured for json: * @@ -121,7 +121,7 @@ public Builder topic(String topic) { * * @see ProducerConfig#BOOTSTRAP_SERVERS_CONFIG */ - public final Builder bootstrapServers(String bootstrapServers) { + public Builder bootstrapServers(String bootstrapServers) { if (bootstrapServers == null) throw new NullPointerException("bootstrapServers == null"); properties.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapServers); return this; @@ -177,7 +177,7 @@ public Builder overrides(Map overrides) { * * @see ProducerConfig */ - public final Builder overrides(Properties overrides) { + public Builder overrides(Properties overrides) { if (overrides == null) throw new NullPointerException("overrides == null"); properties.putAll(overrides); return this; @@ -256,11 +256,11 @@ public Builder toBuilder() { } /** - * This sends all of the spans as a single message. + * This sends all the spans as a single message. * *

NOTE: this blocks until the metadata server is available. */ - @Override public zipkin2.Call sendSpans(List encodedSpans) { + @Override public Call sendSpans(List encodedSpans) { if (closeCalled) throw new ClosedSenderException(); byte[] message = encoder.encode(encodedSpans); return new KafkaCall(message); diff --git a/kafka/src/test/java/zipkin2/reporter/kafka/ITKafkaSender.java b/kafka/src/test/java/zipkin2/reporter/kafka/ITKafkaSender.java index 919f5bb0..7eb9f335 100644 --- a/kafka/src/test/java/zipkin2/reporter/kafka/ITKafkaSender.java +++ b/kafka/src/test/java/zipkin2/reporter/kafka/ITKafkaSender.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2023 The OpenZipkin Authors + * Copyright 2016-2024 The OpenZipkin Authors * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except * in compliance with the License. You may obtain a copy of the License at @@ -34,13 +34,13 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestInstance; import org.junit.jupiter.api.extension.RegisterExtension; -import zipkin2.Call; -import zipkin2.CheckResult; import zipkin2.Span; -import zipkin2.codec.Encoding; import zipkin2.codec.SpanBytesDecoder; -import zipkin2.codec.SpanBytesEncoder; +import zipkin2.reporter.SpanBytesEncoder; import zipkin2.reporter.AsyncReporter; +import zipkin2.reporter.Call; +import zipkin2.reporter.CheckResult; +import zipkin2.reporter.Encoding; import zipkin2.reporter.Sender; import static java.util.stream.Collectors.toList; diff --git a/libthrift/pom.xml b/libthrift/pom.xml index 01b460d1..2ef39148 100644 --- a/libthrift/pom.xml +++ b/libthrift/pom.xml @@ -20,7 +20,7 @@ io.zipkin.reporter2 zipkin-reporter-parent - 2.17.3-SNAPSHOT + 3.0.0-SNAPSHOT zipkin-sender-libthrift @@ -38,6 +38,14 @@ ${project.groupId} zipkin-reporter ${project.version} + + + + io.zipkin.zipkin2 + zipkin + + diff --git a/libthrift/src/main/java/zipkin2/reporter/libthrift/LibthriftSender.java b/libthrift/src/main/java/zipkin2/reporter/libthrift/LibthriftSender.java index e2d256c1..8c8c375e 100644 --- a/libthrift/src/main/java/zipkin2/reporter/libthrift/LibthriftSender.java +++ b/libthrift/src/main/java/zipkin2/reporter/libthrift/LibthriftSender.java @@ -17,11 +17,11 @@ import java.util.Collections; import java.util.List; import org.apache.thrift.TException; -import zipkin2.Call; -import zipkin2.Callback; -import zipkin2.CheckResult; -import zipkin2.codec.Encoding; +import zipkin2.reporter.Call; +import zipkin2.reporter.Callback; +import zipkin2.reporter.CheckResult; import zipkin2.reporter.ClosedSenderException; +import zipkin2.reporter.Encoding; import zipkin2.reporter.Sender; /** @@ -71,6 +71,7 @@ public Builder socketTimeout(int socketTimeout) { this.socketTimeout = socketTimeout; return this; } + /** Default 10 * 1000 milliseconds. 0 implies no timeout. */ public Builder connectTimeout(int connectTimeout) { this.connectTimeout = connectTimeout; @@ -87,7 +88,8 @@ public final LibthriftSender build() { return new LibthriftSender(this); } - Builder() {} + Builder() { + } } final String host; diff --git a/libthrift/src/test/java/zipkin2/reporter/libthrift/ITLibthriftSender.java b/libthrift/src/test/java/zipkin2/reporter/libthrift/ITLibthriftSender.java index 7935607c..93733cb4 100644 --- a/libthrift/src/test/java/zipkin2/reporter/libthrift/ITLibthriftSender.java +++ b/libthrift/src/test/java/zipkin2/reporter/libthrift/ITLibthriftSender.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2023 The OpenZipkin Authors + * Copyright 2016-2024 The OpenZipkin Authors * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except * in compliance with the License. You may obtain a copy of the License at @@ -23,7 +23,7 @@ import org.junit.jupiter.api.TestInstance; import org.junit.jupiter.api.extension.RegisterExtension; import zipkin2.Span; -import zipkin2.codec.SpanBytesEncoder; +import zipkin2.reporter.SpanBytesEncoder; import static java.util.stream.Collectors.toList; import static org.assertj.core.api.Assertions.assertThat; diff --git a/libthrift/src/test/java/zipkin2/reporter/libthrift/InternalScribeCodecTest.java b/libthrift/src/test/java/zipkin2/reporter/libthrift/InternalScribeCodecTest.java index c7648669..12926dd0 100644 --- a/libthrift/src/test/java/zipkin2/reporter/libthrift/InternalScribeCodecTest.java +++ b/libthrift/src/test/java/zipkin2/reporter/libthrift/InternalScribeCodecTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2023 The OpenZipkin Authors + * Copyright 2016-2024 The OpenZipkin Authors * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except * in compliance with the License. You may obtain a copy of the License at @@ -22,7 +22,7 @@ import org.junit.jupiter.api.Test; import zipkin2.Endpoint; import zipkin2.Span; -import zipkin2.codec.SpanBytesEncoder; +import zipkin2.reporter.SpanBytesEncoder; import static java.util.Arrays.asList; import static org.assertj.core.api.Assertions.assertThat; diff --git a/metrics-micrometer/pom.xml b/metrics-micrometer/pom.xml index da1ad0fb..8e2fe7bb 100644 --- a/metrics-micrometer/pom.xml +++ b/metrics-micrometer/pom.xml @@ -20,7 +20,7 @@ io.zipkin.reporter2 zipkin-reporter-parent - 2.17.3-SNAPSHOT + 3.0.0-SNAPSHOT zipkin-reporter-metrics-micrometer @@ -39,6 +39,14 @@ ${project.groupId} zipkin-reporter ${project.version} + + + + io.zipkin.zipkin2 + zipkin + + diff --git a/metrics-micrometer/src/main/java/zipkin2/reporter/metrics/micrometer/MicrometerReporterMetrics.java b/metrics-micrometer/src/main/java/zipkin2/reporter/metrics/micrometer/MicrometerReporterMetrics.java index 2acfbbf5..f44374e2 100644 --- a/metrics-micrometer/src/main/java/zipkin2/reporter/metrics/micrometer/MicrometerReporterMetrics.java +++ b/metrics-micrometer/src/main/java/zipkin2/reporter/metrics/micrometer/MicrometerReporterMetrics.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2019 The OpenZipkin Authors + * Copyright 2016-2024 The OpenZipkin Authors * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except * in compliance with the License. You may obtain a copy of the License at @@ -18,10 +18,9 @@ import io.micrometer.core.instrument.MeterRegistry; import io.micrometer.core.instrument.Tag; import io.micrometer.core.instrument.Tags; -import zipkin2.reporter.ReporterMetrics; - import java.util.Arrays; import java.util.concurrent.atomic.AtomicInteger; +import zipkin2.reporter.ReporterMetrics; /** * Implementation of {@link ReporterMetrics} with Micrometer. diff --git a/okhttp3/pom.xml b/okhttp3/pom.xml index 7cab76d9..86bf0942 100644 --- a/okhttp3/pom.xml +++ b/okhttp3/pom.xml @@ -20,7 +20,7 @@ io.zipkin.reporter2 zipkin-reporter-parent - 2.17.3-SNAPSHOT + 3.0.0-SNAPSHOT zipkin-sender-okhttp3 @@ -40,6 +40,14 @@ ${project.groupId} zipkin-reporter ${project.version} + + + + io.zipkin.zipkin2 + zipkin + + diff --git a/okhttp3/src/main/java/zipkin2/reporter/okhttp3/HttpCall.java b/okhttp3/src/main/java/zipkin2/reporter/okhttp3/HttpCall.java index 2ba99aff..818574ef 100644 --- a/okhttp3/src/main/java/zipkin2/reporter/okhttp3/HttpCall.java +++ b/okhttp3/src/main/java/zipkin2/reporter/okhttp3/HttpCall.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2019 The OpenZipkin Authors + * Copyright 2016-2024 The OpenZipkin Authors * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except * in compliance with the License. You may obtain a copy of the License at @@ -19,8 +19,8 @@ import okio.BufferedSource; import okio.GzipSource; import okio.Okio; -import zipkin2.Call; -import zipkin2.Callback; +import zipkin2.reporter.Call; +import zipkin2.reporter.Callback; final class HttpCall extends Call { diff --git a/okhttp3/src/main/java/zipkin2/reporter/okhttp3/OkHttpSender.java b/okhttp3/src/main/java/zipkin2/reporter/okhttp3/OkHttpSender.java index a2717c3c..53d39f6f 100644 --- a/okhttp3/src/main/java/zipkin2/reporter/okhttp3/OkHttpSender.java +++ b/okhttp3/src/main/java/zipkin2/reporter/okhttp3/OkHttpSender.java @@ -13,15 +13,12 @@ */ package zipkin2.reporter.okhttp3; -import static java.util.concurrent.TimeUnit.MILLISECONDS; - import java.io.IOException; import java.util.List; import java.util.concurrent.SynchronousQueue; import java.util.concurrent.ThreadFactory; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; - import okhttp3.Dispatcher; import okhttp3.HttpUrl; import okhttp3.MediaType; @@ -33,18 +30,20 @@ import okio.BufferedSink; import okio.GzipSink; import okio.Okio; -import zipkin2.CheckResult; -import zipkin2.codec.Encoding; -import zipkin2.reporter.AsyncReporter; +import zipkin2.reporter.CheckResult; import zipkin2.reporter.ClosedSenderException; +import zipkin2.reporter.Encoding; import zipkin2.reporter.Sender; +import static java.util.concurrent.TimeUnit.MILLISECONDS; + /** * Reports spans to Zipkin, using its POST endpoint. * *

Usage

- * - * This type is designed for {@link AsyncReporter.Builder#builder(Sender) the async reporter}. + *

+ * This type is designed for {@link zipkin2.reporter.AsyncReporter.Builder#builder(Sender) the async + * reporter}. * *

Here's a simple configuration, configured for json: * @@ -158,19 +157,19 @@ public Builder encoding(Encoding encoding) { } /** Sets the default connect timeout (in milliseconds) for new connections. Default 10000 */ - public final Builder connectTimeout(int connectTimeoutMillis) { + public Builder connectTimeout(int connectTimeoutMillis) { clientBuilder.connectTimeout(connectTimeoutMillis, MILLISECONDS); return this; } /** Sets the default read timeout (in milliseconds) for new connections. Default 10000 */ - public final Builder readTimeout(int readTimeoutMillis) { + public Builder readTimeout(int readTimeoutMillis) { clientBuilder.readTimeout(readTimeoutMillis, MILLISECONDS); return this; } /** Sets the default write timeout (in milliseconds) for new connections. Default 10000 */ - public final Builder writeTimeout(int writeTimeoutMillis) { + public Builder writeTimeout(int writeTimeoutMillis) { clientBuilder.writeTimeout(writeTimeoutMillis, MILLISECONDS); return this; } @@ -246,7 +245,7 @@ enum OkHttpSenderThreadFactory implements ThreadFactory { * Creates a builder out of this object. Note: if the {@link Builder#clientBuilder()} was * customized, you'll need to re-apply those customizations. */ - public final Builder toBuilder() { + public Builder toBuilder() { return new Builder(this); } @@ -270,7 +269,7 @@ public final Builder toBuilder() { volatile boolean closeCalled; /** The returned call sends spans as a POST to {@link Builder#endpoint(String)}. */ - @Override public zipkin2.Call sendSpans(List encodedSpans) { + @Override public zipkin2.reporter.Call sendSpans(List encodedSpans) { if (closeCalled) throw new ClosedSenderException(); Request request; try { diff --git a/okhttp3/src/main/java/zipkin2/reporter/okhttp3/Platform.java b/okhttp3/src/main/java/zipkin2/reporter/okhttp3/Platform.java index ddd26468..1b2b5151 100644 --- a/okhttp3/src/main/java/zipkin2/reporter/okhttp3/Platform.java +++ b/okhttp3/src/main/java/zipkin2/reporter/okhttp3/Platform.java @@ -16,7 +16,7 @@ import java.io.IOException; import java.lang.reflect.Constructor; -/** Taken from {@code zipkin2.internal.Platform} to avoid needing to shade over a single method. */ +/** Taken from {@code zipkin2.reporter.internal.Platform} to avoid needing to shade over a single method. */ abstract class Platform { private static final Platform PLATFORM = findPlatform(); diff --git a/okhttp3/src/main/java/zipkin2/reporter/okhttp3/RequestBodyMessageEncoder.java b/okhttp3/src/main/java/zipkin2/reporter/okhttp3/RequestBodyMessageEncoder.java index 74f26906..5df1f27a 100644 --- a/okhttp3/src/main/java/zipkin2/reporter/okhttp3/RequestBodyMessageEncoder.java +++ b/okhttp3/src/main/java/zipkin2/reporter/okhttp3/RequestBodyMessageEncoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2019 The OpenZipkin Authors + * Copyright 2016-2024 The OpenZipkin Authors * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except * in compliance with the License. You may obtain a copy of the License at @@ -18,7 +18,7 @@ import okhttp3.MediaType; import okhttp3.RequestBody; import okio.BufferedSink; -import zipkin2.codec.Encoding; +import zipkin2.reporter.Encoding; enum RequestBodyMessageEncoder { JSON { diff --git a/okhttp3/src/test/java/zipkin2/reporter/okhttp3/ITOkHttpSender.java b/okhttp3/src/test/java/zipkin2/reporter/okhttp3/ITOkHttpSender.java index aa95d594..0750f1bf 100644 --- a/okhttp3/src/test/java/zipkin2/reporter/okhttp3/ITOkHttpSender.java +++ b/okhttp3/src/test/java/zipkin2/reporter/okhttp3/ITOkHttpSender.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2023 The OpenZipkin Authors + * Copyright 2016-2024 The OpenZipkin Authors * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except * in compliance with the License. You may obtain a copy of the License at @@ -27,14 +27,14 @@ import okhttp3.mockwebserver.SocketPolicy; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; -import zipkin2.Call; -import zipkin2.Callback; import zipkin2.Span; -import zipkin2.codec.Encoding; import zipkin2.codec.SpanBytesDecoder; -import zipkin2.codec.SpanBytesEncoder; +import zipkin2.reporter.SpanBytesEncoder; import zipkin2.reporter.AsyncReporter; import zipkin2.reporter.AwaitableCallback; +import zipkin2.reporter.Call; +import zipkin2.reporter.Callback; +import zipkin2.reporter.Encoding; import zipkin2.reporter.Sender; import static java.util.Arrays.asList; @@ -53,7 +53,7 @@ public class ITOkHttpSender { // public for use in src/it String endpoint = server.url("/api/v2/spans").toString(); OkHttpSender sender = - OkHttpSender.newBuilder().endpoint(endpoint).compressionEnabled(false).build(); + OkHttpSender.newBuilder().endpoint(endpoint).compressionEnabled(false).build(); @Test void badUrlIsAnIllegalArgument() { assertThatThrownBy(() -> OkHttpSender.create("htp://localhost:9411/api/v1/spans")) @@ -88,7 +88,7 @@ public class ITOkHttpSender { // public for use in src/it // Now, let's read back the spans we sent! assertThat(SpanBytesDecoder.JSON_V2.decodeList(server.takeRequest().getBody().readByteArray())) - .containsExactly(CLIENT_SPAN, CLIENT_SPAN); + .containsExactly(CLIENT_SPAN, CLIENT_SPAN); } @Test void sendsSpans_PROTO3() throws Exception { @@ -103,7 +103,7 @@ public class ITOkHttpSender { // public for use in src/it // Now, let's read back the spans we sent! assertThat(SpanBytesDecoder.PROTO3.decodeList(server.takeRequest().getBody().readByteArray())) - .containsExactly(CLIENT_SPAN, CLIENT_SPAN); + .containsExactly(CLIENT_SPAN, CLIENT_SPAN); } @Test void sendsSpans_THRIFT() throws Exception { @@ -118,7 +118,7 @@ public class ITOkHttpSender { // public for use in src/it // Now, let's read back the spans we sent! assertThat(SpanBytesDecoder.THRIFT.decodeList(server.takeRequest().getBody().readByteArray())) - .containsExactly(CLIENT_SPAN, CLIENT_SPAN); + .containsExactly(CLIENT_SPAN, CLIENT_SPAN); } @Test void compression() throws Exception { @@ -136,7 +136,7 @@ public class ITOkHttpSender { // public for use in src/it // we expect the first compressed request to be smaller than the uncompressed one. assertThat(requests.get(0).getBodySize()) - .isLessThan(requests.get(1).getBodySize()); + .isLessThan(requests.get(1).getBodySize()); } @Test void ensuresProxiesDontTrace() throws Exception { @@ -155,7 +155,7 @@ public class ITOkHttpSender { // public for use in src/it // block until the request arrived assertThat(server.takeRequest().getHeader("Content-Type")) - .isEqualTo("application/json"); + .isEqualTo("application/json"); } @Test void closeWhileRequestInFlight_cancelsRequest() throws Exception { @@ -316,8 +316,8 @@ public class ITOkHttpSender { // public for use in src/it @Test void bugGuardCache() { assertThat(sender.client.cache()) - .withFailMessage("senders should not open a disk cache") - .isNull(); + .withFailMessage("senders should not open a disk cache") + .isNull(); } Call send(Span... spans) { diff --git a/pom.xml b/pom.xml index 98e0e2ef..99719f33 100755 --- a/pom.xml +++ b/pom.xml @@ -19,7 +19,7 @@ io.zipkin.reporter2 zipkin-reporter-parent - 2.17.3-SNAPSHOT + 3.0.0-SNAPSHOT pom diff --git a/spring-beans/pom.xml b/spring-beans/pom.xml index eee11dc0..9f5db53a 100644 --- a/spring-beans/pom.xml +++ b/spring-beans/pom.xml @@ -18,7 +18,7 @@ io.zipkin.reporter2 zipkin-reporter-parent - 2.17.3-SNAPSHOT + 3.0.0-SNAPSHOT 4.0.0 @@ -50,6 +50,18 @@ ${project.groupId} zipkin-reporter ${project.version} + + + io.zipkin.zipkin2 + zipkin + + + + + io.zipkin.zipkin2 + zipkin + ${zipkin.version} + true ${project.groupId} diff --git a/spring-beans/src/main/java/zipkin2/reporter/beans/ActiveMQSenderFactoryBean.java b/spring-beans/src/main/java/zipkin2/reporter/beans/ActiveMQSenderFactoryBean.java index 0220890e..16a76982 100644 --- a/spring-beans/src/main/java/zipkin2/reporter/beans/ActiveMQSenderFactoryBean.java +++ b/spring-beans/src/main/java/zipkin2/reporter/beans/ActiveMQSenderFactoryBean.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2019 The OpenZipkin Authors + * Copyright 2016-2024 The OpenZipkin Authors * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except * in compliance with the License. You may obtain a copy of the License at @@ -15,7 +15,7 @@ import org.apache.activemq.ActiveMQConnectionFactory; import org.springframework.beans.factory.config.AbstractFactoryBean; -import zipkin2.codec.Encoding; +import zipkin2.reporter.Encoding; import zipkin2.reporter.activemq.ActiveMQSender; /** Spring XML config does not support chained builders. This converts accordingly */ diff --git a/spring-beans/src/main/java/zipkin2/reporter/beans/AsyncReporterFactoryBean.java b/spring-beans/src/main/java/zipkin2/reporter/beans/AsyncReporterFactoryBean.java index 72bc053f..418109f4 100644 --- a/spring-beans/src/main/java/zipkin2/reporter/beans/AsyncReporterFactoryBean.java +++ b/spring-beans/src/main/java/zipkin2/reporter/beans/AsyncReporterFactoryBean.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2020 The OpenZipkin Authors + * Copyright 2016-2024 The OpenZipkin Authors * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except * in compliance with the License. You may obtain a copy of the License at @@ -14,8 +14,8 @@ package zipkin2.reporter.beans; import java.util.concurrent.TimeUnit; -import zipkin2.codec.SpanBytesEncoder; import zipkin2.reporter.AsyncReporter; +import zipkin2.reporter.SpanBytesEncoder; /** Spring XML config does not support chained builders. This converts accordingly */ public class AsyncReporterFactoryBean extends BaseAsyncFactoryBean { diff --git a/spring-beans/src/main/java/zipkin2/reporter/beans/KafkaSenderFactoryBean.java b/spring-beans/src/main/java/zipkin2/reporter/beans/KafkaSenderFactoryBean.java index f5f4dbf2..b81acd26 100644 --- a/spring-beans/src/main/java/zipkin2/reporter/beans/KafkaSenderFactoryBean.java +++ b/spring-beans/src/main/java/zipkin2/reporter/beans/KafkaSenderFactoryBean.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2019 The OpenZipkin Authors + * Copyright 2016-2024 The OpenZipkin Authors * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except * in compliance with the License. You may obtain a copy of the License at @@ -14,7 +14,7 @@ package zipkin2.reporter.beans; import org.springframework.beans.factory.config.AbstractFactoryBean; -import zipkin2.codec.Encoding; +import zipkin2.reporter.Encoding; import zipkin2.reporter.kafka.KafkaSender; /** Spring XML config does not support chained builders. This converts accordingly */ diff --git a/spring-beans/src/main/java/zipkin2/reporter/beans/OkHttpSenderFactoryBean.java b/spring-beans/src/main/java/zipkin2/reporter/beans/OkHttpSenderFactoryBean.java index f8d97562..249150fd 100644 --- a/spring-beans/src/main/java/zipkin2/reporter/beans/OkHttpSenderFactoryBean.java +++ b/spring-beans/src/main/java/zipkin2/reporter/beans/OkHttpSenderFactoryBean.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2019 The OpenZipkin Authors + * Copyright 2016-2024 The OpenZipkin Authors * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except * in compliance with the License. You may obtain a copy of the License at @@ -14,7 +14,7 @@ package zipkin2.reporter.beans; import org.springframework.beans.factory.config.AbstractFactoryBean; -import zipkin2.codec.Encoding; +import zipkin2.reporter.Encoding; import zipkin2.reporter.okhttp3.OkHttpSender; /** Spring XML config does not support chained builders. This converts accordingly */ diff --git a/spring-beans/src/main/java/zipkin2/reporter/beans/RabbitMQSenderFactoryBean.java b/spring-beans/src/main/java/zipkin2/reporter/beans/RabbitMQSenderFactoryBean.java index 4afc3176..4e4228c7 100644 --- a/spring-beans/src/main/java/zipkin2/reporter/beans/RabbitMQSenderFactoryBean.java +++ b/spring-beans/src/main/java/zipkin2/reporter/beans/RabbitMQSenderFactoryBean.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2019 The OpenZipkin Authors + * Copyright 2016-2024 The OpenZipkin Authors * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except * in compliance with the License. You may obtain a copy of the License at @@ -15,7 +15,7 @@ import java.io.IOException; import org.springframework.beans.factory.config.AbstractFactoryBean; -import zipkin2.codec.Encoding; +import zipkin2.reporter.Encoding; import zipkin2.reporter.amqp.RabbitMQSender; /** Spring XML config does not support chained builders. This converts accordingly */ diff --git a/spring-beans/src/main/java/zipkin2/reporter/beans/URLConnectionSenderFactoryBean.java b/spring-beans/src/main/java/zipkin2/reporter/beans/URLConnectionSenderFactoryBean.java index e53a51db..7706d6c8 100644 --- a/spring-beans/src/main/java/zipkin2/reporter/beans/URLConnectionSenderFactoryBean.java +++ b/spring-beans/src/main/java/zipkin2/reporter/beans/URLConnectionSenderFactoryBean.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2019 The OpenZipkin Authors + * Copyright 2016-2024 The OpenZipkin Authors * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except * in compliance with the License. You may obtain a copy of the License at @@ -14,7 +14,7 @@ package zipkin2.reporter.beans; import org.springframework.beans.factory.config.AbstractFactoryBean; -import zipkin2.codec.Encoding; +import zipkin2.reporter.Encoding; import zipkin2.reporter.urlconnection.URLConnectionSender; /** Spring XML config does not support chained builders. This converts accordingly */ diff --git a/spring-beans/src/test/java/zipkin2/reporter/beans/ActiveMQSenderFactoryBeanTest.java b/spring-beans/src/test/java/zipkin2/reporter/beans/ActiveMQSenderFactoryBeanTest.java index c93dd6af..3bec3c21 100755 --- a/spring-beans/src/test/java/zipkin2/reporter/beans/ActiveMQSenderFactoryBeanTest.java +++ b/spring-beans/src/test/java/zipkin2/reporter/beans/ActiveMQSenderFactoryBeanTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2023 The OpenZipkin Authors + * Copyright 2016-2024 The OpenZipkin Authors * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except * in compliance with the License. You may obtain a copy of the License at @@ -16,7 +16,7 @@ import java.util.Arrays; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; -import zipkin2.codec.Encoding; +import zipkin2.reporter.Encoding; import zipkin2.reporter.activemq.ActiveMQSender; import static org.assertj.core.api.Assertions.assertThat; diff --git a/spring-beans/src/test/java/zipkin2/reporter/beans/AsyncReporterFactoryBeanTest.java b/spring-beans/src/test/java/zipkin2/reporter/beans/AsyncReporterFactoryBeanTest.java index b0afbcc6..7ca97bcb 100755 --- a/spring-beans/src/test/java/zipkin2/reporter/beans/AsyncReporterFactoryBeanTest.java +++ b/spring-beans/src/test/java/zipkin2/reporter/beans/AsyncReporterFactoryBeanTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2023 The OpenZipkin Authors + * Copyright 2016-2024 The OpenZipkin Authors * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except * in compliance with the License. You may obtain a copy of the License at @@ -16,17 +16,17 @@ import java.util.concurrent.TimeUnit; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; -import zipkin2.codec.Encoding; -import zipkin2.codec.SpanBytesEncoder; import zipkin2.reporter.AsyncReporter; +import zipkin2.reporter.Encoding; import zipkin2.reporter.ReporterMetrics; import zipkin2.reporter.Sender; +import zipkin2.reporter.SpanBytesEncoder; import static org.assertj.core.api.Assertions.assertThat; class AsyncReporterFactoryBeanTest { public static Sender SENDER = new FakeSender(); - public static Sender PROTO3_SENDER = new FakeSender(){ + public static Sender PROTO3_SENDER = new FakeSender() { @Override public Encoding encoding() { return Encoding.PROTO3; } @@ -41,144 +41,144 @@ class AsyncReporterFactoryBeanTest { @Test void sender() { context = new XmlBeans("" - + "\n" - + " \n" - + " \n" - + " \n" - + " \n" // disable thread for test - + "" + + "\n" + + " \n" + + " \n" + + " \n" + + " \n" // disable thread for test + + "" ); assertThat(context.getBean("asyncReporter", AsyncReporter.class)) - .extracting("sender") - .isEqualTo(SENDER); + .extracting("delegate.sender") + .isEqualTo(SENDER); } @Test void metrics() { context = new XmlBeans("" - + "\n" - + " \n" - + " \n" - + " \n" - + " \n" - + " \n" - + " \n" - + " \n" // disable thread for test - + "" + + "\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" // disable thread for test + + "" ); assertThat(context.getBean("asyncReporter", AsyncReporter.class)) - .extracting("metrics") - .isEqualTo(METRICS); + .extracting("delegate.metrics") + .isEqualTo(METRICS); } @Test void messageMaxBytes() { context = new XmlBeans("" - + "\n" - + " \n" - + " \n" - + " \n" - + " \n" - + " \n" // disable thread for test - + "" + + "\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" // disable thread for test + + "" ); assertThat(context.getBean("asyncReporter", AsyncReporter.class)) - .extracting("messageMaxBytes") - .isEqualTo(512); + .extracting("delegate.messageMaxBytes") + .isEqualTo(512); } @Test void messageTimeout() { context = new XmlBeans("" - + "\n" - + " \n" - + " \n" - + " \n" - + " \n" - + "" + + "\n" + + " \n" + + " \n" + + " \n" + + " \n" + + "" ); assertThat(context.getBean("asyncReporter", AsyncReporter.class)) - .extracting("messageTimeoutNanos") - .isEqualTo(TimeUnit.MILLISECONDS.toNanos(500)); + .extracting("delegate.messageTimeoutNanos") + .isEqualTo(TimeUnit.MILLISECONDS.toNanos(500)); } @Test void closeTimeout() { context = new XmlBeans("" - + "\n" - + " \n" - + " \n" - + " \n" - + " \n" - + " \n" // disable thread for test - + "" + + "\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" // disable thread for test + + "" ); assertThat(context.getBean("asyncReporter", AsyncReporter.class)) - .extracting("closeTimeoutNanos") - .isEqualTo(TimeUnit.MILLISECONDS.toNanos(500)); + .extracting("delegate.closeTimeoutNanos") + .isEqualTo(TimeUnit.MILLISECONDS.toNanos(500)); } @Test void queuedMaxSpans() { context = new XmlBeans("" - + "\n" - + " \n" - + " \n" - + " \n" - + " \n" - + " \n" // disable thread for test - + "" + + "\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" // disable thread for test + + "" ); assertThat(context.getBean("asyncReporter", AsyncReporter.class)) - .extracting("pending.maxSize") - .isEqualTo(10); + .extracting("delegate.pending.maxSize") + .isEqualTo(10); } @Test void queuedMaxBytes() { context = new XmlBeans("" - + "\n" - + " \n" - + " \n" - + " \n" - + " \n" - + " \n" // disable thread for test - + "" + + "\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" // disable thread for test + + "" ); assertThat(context.getBean("asyncReporter", AsyncReporter.class)) - .extracting("pending.maxBytes") - .isEqualTo(512); + .extracting("delegate.pending.maxBytes") + .isEqualTo(512); } @Test void sender_proto3() { context = new XmlBeans("" - + "\n" - + " \n" - + " \n" - + " \n" - + " \n" // disable thread for test - + "" + + "\n" + + " \n" + + " \n" + + " \n" + + " \n" // disable thread for test + + "" ); assertThat(context.getBean("asyncReporter", AsyncReporter.class)) - .extracting("encoder") - .isEqualTo(SpanBytesEncoder.PROTO3); + .extracting("delegate.encoder.delegate") + .isEqualTo(SpanBytesEncoder.PROTO3); } @Test void encoder() { context = new XmlBeans("" - + "\n" - + " \n" - + " \n" - + " \n" - + " \n" - + " \n" // disable thread for test - + "" + + "\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" // disable thread for test + + "" ); assertThat(context.getBean("asyncReporter", AsyncReporter.class)) - .extracting("encoder") - .isEqualTo(SpanBytesEncoder.PROTO3); + .extracting("delegate.encoder.delegate") + .isEqualTo(SpanBytesEncoder.PROTO3); } } diff --git a/spring-beans/src/test/java/zipkin2/reporter/beans/FakeSender.java b/spring-beans/src/test/java/zipkin2/reporter/beans/FakeSender.java index 17d28542..73cce760 100644 --- a/spring-beans/src/test/java/zipkin2/reporter/beans/FakeSender.java +++ b/spring-beans/src/test/java/zipkin2/reporter/beans/FakeSender.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2023 The OpenZipkin Authors + * Copyright 2016-2024 The OpenZipkin Authors * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except * in compliance with the License. You may obtain a copy of the License at @@ -14,8 +14,8 @@ package zipkin2.reporter.beans; import java.util.List; -import zipkin2.Call; -import zipkin2.codec.Encoding; +import zipkin2.reporter.Call; +import zipkin2.reporter.Encoding; import zipkin2.reporter.Sender; class FakeSender extends Sender { diff --git a/spring-beans/src/test/java/zipkin2/reporter/beans/KafkaSenderFactoryBeanTest.java b/spring-beans/src/test/java/zipkin2/reporter/beans/KafkaSenderFactoryBeanTest.java index fcebb8de..579d66fc 100755 --- a/spring-beans/src/test/java/zipkin2/reporter/beans/KafkaSenderFactoryBeanTest.java +++ b/spring-beans/src/test/java/zipkin2/reporter/beans/KafkaSenderFactoryBeanTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2023 The OpenZipkin Authors + * Copyright 2016-2024 The OpenZipkin Authors * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except * in compliance with the License. You may obtain a copy of the License at @@ -16,7 +16,7 @@ import java.util.Arrays; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; -import zipkin2.codec.Encoding; +import zipkin2.reporter.Encoding; import zipkin2.reporter.kafka.KafkaSender; import static org.assertj.core.api.Assertions.assertThat; @@ -31,71 +31,71 @@ class KafkaSenderFactoryBeanTest { @Test void bootstrapServers() { context = new XmlBeans("" - + "\n" - + " \n" - + "" + + "\n" + + " \n" + + "" ); assertThat(context.getBean("sender", KafkaSender.class)) - .usingRecursiveComparison() - .isEqualTo(KafkaSender.newBuilder() - .bootstrapServers("localhost:9092") - .build()); + .usingRecursiveComparison() + .isEqualTo(KafkaSender.newBuilder() + .bootstrapServers("localhost:9092") + .build()); } @Test void topic() { context = new XmlBeans("" - + "\n" - + " \n" - + " \n" - + "" + + "\n" + + " \n" + + " \n" + + "" ); assertThat(context.getBean("sender", KafkaSender.class)) - .usingRecursiveComparison() - .isEqualTo(KafkaSender.newBuilder() - .bootstrapServers("localhost:9092") - .topic("zipkin2").build()); + .usingRecursiveComparison() + .isEqualTo(KafkaSender.newBuilder() + .bootstrapServers("localhost:9092") + .topic("zipkin2").build()); } @Test void messageMaxBytes() { context = new XmlBeans("" - + "\n" - + " \n" - + " \n" - + "" + + "\n" + + " \n" + + " \n" + + "" ); assertThat(context.getBean("sender", KafkaSender.class)) - .extracting("messageMaxBytes") - .isEqualTo(1024); + .extracting("messageMaxBytes") + .isEqualTo(1024); } @Test void encoding() { context = new XmlBeans("" - + "\n" - + " \n" - + " \n" - + "" + + "\n" + + " \n" + + " \n" + + "" ); assertThat(context.getBean("sender", KafkaSender.class)) - .extracting("encoding") - .isEqualTo(Encoding.PROTO3); + .extracting("encoding") + .isEqualTo(Encoding.PROTO3); } @Test void close_closesSender() { assertThrows(IllegalStateException.class, () -> { context = new XmlBeans("" - + "\n" - + " \n" - + "" + + "\n" + + " \n" + + "" ); KafkaSender sender = context.getBean("sender", KafkaSender.class); context.close(); - sender.sendSpans(Arrays.asList(new byte[]{'{', '}'})); + sender.sendSpans(Arrays.asList(new byte[] {'{', '}'})); }); } } diff --git a/spring-beans/src/test/java/zipkin2/reporter/beans/OkHttpSenderFactoryBeanTest.java b/spring-beans/src/test/java/zipkin2/reporter/beans/OkHttpSenderFactoryBeanTest.java index fcd914b3..e02f589d 100755 --- a/spring-beans/src/test/java/zipkin2/reporter/beans/OkHttpSenderFactoryBeanTest.java +++ b/spring-beans/src/test/java/zipkin2/reporter/beans/OkHttpSenderFactoryBeanTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2023 The OpenZipkin Authors + * Copyright 2016-2024 The OpenZipkin Authors * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except * in compliance with the License. You may obtain a copy of the License at @@ -17,7 +17,7 @@ import okhttp3.HttpUrl; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; -import zipkin2.codec.Encoding; +import zipkin2.reporter.Encoding; import zipkin2.reporter.okhttp3.OkHttpSender; import static org.assertj.core.api.Assertions.assertThat; diff --git a/spring-beans/src/test/java/zipkin2/reporter/beans/RabbitMQSenderFactoryBeanTest.java b/spring-beans/src/test/java/zipkin2/reporter/beans/RabbitMQSenderFactoryBeanTest.java index 571d28a3..02d8cfa5 100755 --- a/spring-beans/src/test/java/zipkin2/reporter/beans/RabbitMQSenderFactoryBeanTest.java +++ b/spring-beans/src/test/java/zipkin2/reporter/beans/RabbitMQSenderFactoryBeanTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2023 The OpenZipkin Authors + * Copyright 2016-2024 The OpenZipkin Authors * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except * in compliance with the License. You may obtain a copy of the License at @@ -14,10 +14,9 @@ package zipkin2.reporter.beans; import com.rabbitmq.client.Address; -import java.util.Arrays; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; -import zipkin2.codec.Encoding; +import zipkin2.reporter.Encoding; import zipkin2.reporter.amqp.RabbitMQSender; import static java.util.Arrays.asList; @@ -33,107 +32,107 @@ class RabbitMQSenderFactoryBeanTest { @Test void addresses() { context = new XmlBeans("" - + "\n" - + " \n" - + "" + + "\n" + + " \n" + + "" ); assertThat(context.getBean("sender", RabbitMQSender.class)) - .extracting("addresses") - .isEqualTo(asList(new Address("localhost"))); + .extracting("addresses") + .isEqualTo(asList(new Address("localhost"))); } @Test void queue() { context = new XmlBeans("" - + "\n" - + " \n" - + " \n" - + "" + + "\n" + + " \n" + + " \n" + + "" ); assertThat(context.getBean("sender", RabbitMQSender.class)) - .extracting("queue") - .isEqualTo("zipkin2"); + .extracting("queue") + .isEqualTo("zipkin2"); } @Test void connectionTimeout() { context = new XmlBeans("" - + "\n" - + " \n" - + " \n" - + "" + + "\n" + + " \n" + + " \n" + + "" ); assertThat(context.getBean("sender", RabbitMQSender.class)) - .extracting("connectionFactory.connectionTimeout") - .isEqualTo(0); + .extracting("connectionFactory.connectionTimeout") + .isEqualTo(0); } @Test void virtualHost() { context = new XmlBeans("" - + "\n" - + " \n" - + " \n" - + "" + + "\n" + + " \n" + + " \n" + + "" ); assertThat(context.getBean("sender", RabbitMQSender.class)) - .extracting("connectionFactory.virtualHost") - .isEqualTo("zipkin3"); + .extracting("connectionFactory.virtualHost") + .isEqualTo("zipkin3"); } @Test void usernamePassword() { context = new XmlBeans("" - + "\n" - + " \n" - + " \n" - + " \n" - + "" + + "\n" + + " \n" + + " \n" + + " \n" + + "" ); assertThat(context.getBean("sender", RabbitMQSender.class)) - .extracting("connectionFactory.username", "connectionFactory.password") - .isEqualTo(asList("foo", "bar")); + .extracting("connectionFactory.username", "connectionFactory.password") + .isEqualTo(asList("foo", "bar")); } @Test void messageMaxBytes() { context = new XmlBeans("" - + "\n" - + " \n" - + " \n" - + "" + + "\n" + + " \n" + + " \n" + + "" ); assertThat(context.getBean("sender", RabbitMQSender.class)) - .extracting("messageMaxBytes") - .isEqualTo(1024); + .extracting("messageMaxBytes") + .isEqualTo(1024); } @Test void encoding() { context = new XmlBeans("" - + "\n" - + " \n" - + " \n" - + "" + + "\n" + + " \n" + + " \n" + + "" ); assertThat(context.getBean("sender", RabbitMQSender.class)) - .extracting("encoding") - .isEqualTo(Encoding.PROTO3); + .extracting("encoding") + .isEqualTo(Encoding.PROTO3); } @Test void close_closesSender() { assertThrows(IllegalStateException.class, () -> { context = new XmlBeans("" - + "\n" - + " \n" - + "" + + "\n" + + " \n" + + "" ); RabbitMQSender sender = context.getBean("sender", RabbitMQSender.class); context.close(); - sender.sendSpans(asList(new byte[]{'{', '}'})); + sender.sendSpans(asList(new byte[] {'{', '}'})); }); } } diff --git a/spring-beans/src/test/java/zipkin2/reporter/beans/URLConnectionSenderFactoryBeanTest.java b/spring-beans/src/test/java/zipkin2/reporter/beans/URLConnectionSenderFactoryBeanTest.java index f22deaff..8aeb0621 100755 --- a/spring-beans/src/test/java/zipkin2/reporter/beans/URLConnectionSenderFactoryBeanTest.java +++ b/spring-beans/src/test/java/zipkin2/reporter/beans/URLConnectionSenderFactoryBeanTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2023 The OpenZipkin Authors + * Copyright 2016-2024 The OpenZipkin Authors * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except * in compliance with the License. You may obtain a copy of the License at @@ -18,7 +18,7 @@ import java.util.Arrays; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; -import zipkin2.codec.Encoding; +import zipkin2.reporter.Encoding; import zipkin2.reporter.urlconnection.URLConnectionSender; import static org.assertj.core.api.Assertions.assertThat; diff --git a/urlconnection/pom.xml b/urlconnection/pom.xml index 67fde149..91eedc1e 100644 --- a/urlconnection/pom.xml +++ b/urlconnection/pom.xml @@ -20,7 +20,7 @@ io.zipkin.reporter2 zipkin-reporter-parent - 2.17.3-SNAPSHOT + 3.0.0-SNAPSHOT zipkin-sender-urlconnection @@ -38,6 +38,14 @@ ${project.groupId} zipkin-reporter ${project.version} + + + + io.zipkin.zipkin2 + zipkin + + diff --git a/urlconnection/src/main/java/zipkin2/reporter/urlconnection/URLConnectionSender.java b/urlconnection/src/main/java/zipkin2/reporter/urlconnection/URLConnectionSender.java index 9c92fdb4..6e76f428 100644 --- a/urlconnection/src/main/java/zipkin2/reporter/urlconnection/URLConnectionSender.java +++ b/urlconnection/src/main/java/zipkin2/reporter/urlconnection/URLConnectionSender.java @@ -21,12 +21,12 @@ import java.net.URL; import java.util.List; import java.util.zip.GZIPOutputStream; -import zipkin2.Call; -import zipkin2.Callback; -import zipkin2.CheckResult; -import zipkin2.codec.Encoding; import zipkin2.reporter.BytesMessageEncoder; +import zipkin2.reporter.Call; +import zipkin2.reporter.Callback; +import zipkin2.reporter.CheckResult; import zipkin2.reporter.ClosedSenderException; +import zipkin2.reporter.Encoding; import zipkin2.reporter.Sender; /** @@ -65,7 +65,7 @@ public static final class Builder { * usually "http://zipkinhost:9411/api/v2/spans" */ // customizable so that users can re-map /api/v2/spans ex for browser-originated traces - public final Builder endpoint(String endpoint) { + public Builder endpoint(String endpoint) { if (endpoint == null) throw new NullPointerException("endpoint == null"); try { diff --git a/urlconnection/src/test/java/zipkin2/reporter/urlconnection/ITURLConnectionSender.java b/urlconnection/src/test/java/zipkin2/reporter/urlconnection/ITURLConnectionSender.java index f13c73d7..f401d9f0 100644 --- a/urlconnection/src/test/java/zipkin2/reporter/urlconnection/ITURLConnectionSender.java +++ b/urlconnection/src/test/java/zipkin2/reporter/urlconnection/ITURLConnectionSender.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2023 The OpenZipkin Authors + * Copyright 2016-2024 The OpenZipkin Authors * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except * in compliance with the License. You may obtain a copy of the License at @@ -24,13 +24,13 @@ import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import zipkin2.Call; -import zipkin2.Callback; import zipkin2.Span; -import zipkin2.codec.Encoding; import zipkin2.codec.SpanBytesDecoder; import zipkin2.codec.SpanBytesEncoder; import zipkin2.reporter.AsyncReporter; +import zipkin2.reporter.Call; +import zipkin2.reporter.Callback; +import zipkin2.reporter.Encoding; import zipkin2.reporter.Sender; import static java.util.Arrays.asList; @@ -51,9 +51,9 @@ class ITURLConnectionSender { @BeforeEach void setUp() { sender = URLConnectionSender.newBuilder() - .endpoint(endpoint) - .compressionEnabled(false) - .build(); + .endpoint(endpoint) + .compressionEnabled(false) + .build(); } @Test void badUrlIsAnIllegalArgument() { @@ -72,7 +72,7 @@ class ITURLConnectionSender { // Now, let's read back the spans we sent! assertThat(SpanBytesDecoder.JSON_V2.decodeList(server.takeRequest().getBody().readByteArray())) - .containsExactly(CLIENT_SPAN, CLIENT_SPAN); + .containsExactly(CLIENT_SPAN, CLIENT_SPAN); } @Test void sendsSpans_PROTO3() throws Exception { @@ -87,7 +87,7 @@ class ITURLConnectionSender { // Now, let's read back the spans we sent! assertThat(SpanBytesDecoder.PROTO3.decodeList(server.takeRequest().getBody().readByteArray())) - .containsExactly(CLIENT_SPAN, CLIENT_SPAN); + .containsExactly(CLIENT_SPAN, CLIENT_SPAN); } @Test void sendsSpans_THRIFT() throws Exception { @@ -102,7 +102,7 @@ class ITURLConnectionSender { // Now, let's read back the spans we sent! assertThat(SpanBytesDecoder.THRIFT.decodeList(server.takeRequest().getBody().readByteArray())) - .containsExactly(CLIENT_SPAN, CLIENT_SPAN); + .containsExactly(CLIENT_SPAN, CLIENT_SPAN); } @Test void compression() throws Exception { @@ -120,7 +120,7 @@ class ITURLConnectionSender { // we expect the first compressed request to be smaller than the uncompressed one. assertThat(requests.get(0).getBodySize()) - .isLessThan(requests.get(1).getBodySize()); + .isLessThan(requests.get(1).getBodySize()); } @Test void ensuresProxiesDontTrace() throws Exception { @@ -139,7 +139,7 @@ class ITURLConnectionSender { // block until the request arrived assertThat(server.takeRequest().getHeader("Content-Type")) - .isEqualTo("application/json"); + .isEqualTo("application/json"); } @Test void noExceptionWhenServerErrors() {